mirror of
https://github.com/danbulant/oxc
synced 2026-05-20 04:38:54 +00:00
feat(parser,linter)!: use a different ModuleRecord for linter (#7554)
The parser returns a simple `ModuleRecord` that is allocated in the arena for performance reasons. The linter uses a more complicated, `Send` + `Sync` `ModuleRecord` that will hold more cross-module information. The next step is to return more esm information from the parser to eliminated the need of the `oxc_module_lexer` crate.
This commit is contained in:
parent
7e42e71f34
commit
c2ced15dfd
33 changed files with 734 additions and 250 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1986,7 +1986,6 @@ version = "0.38.0"
|
|||
dependencies = [
|
||||
"assert-unchecked",
|
||||
"bitflags 2.6.0",
|
||||
"dashmap 6.1.0",
|
||||
"nonmax",
|
||||
"oxc_allocator",
|
||||
"oxc_ast_macros",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use oxc_allocator::Allocator;
|
|||
use oxc_diagnostics::{Error, NamedSource, Severity};
|
||||
use oxc_linter::{
|
||||
loader::{JavaScriptSource, Loader},
|
||||
FixKind, Linter,
|
||||
FixKind, Linter, ModuleRecord,
|
||||
};
|
||||
use oxc_parser::{ParseOptions, Parser};
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
|
|
@ -290,7 +290,7 @@ impl IsolatedLintHandler {
|
|||
return Some(Self::wrap_diagnostics(path, &source_text, reports, start));
|
||||
};
|
||||
|
||||
let module_record = Arc::new(ret.module_record);
|
||||
let module_record = Arc::new(ModuleRecord::new(path, &ret.module_record));
|
||||
let mut semantic = semantic_ret.semantic;
|
||||
semantic.set_irregular_whitespaces(ret.irregular_whitespaces);
|
||||
let result = self.linter.run(path, Rc::new(semantic), module_record);
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc};
|
|||
|
||||
use oxc_semantic::Semantic;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use crate::{
|
||||
config::{LintConfig, LintPlugins},
|
||||
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
|
||||
fixer::{FixKind, Message},
|
||||
frameworks,
|
||||
module_record::ModuleRecord,
|
||||
options::LintOptions,
|
||||
utils, FrameworkFlags, RuleWithSeverity,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use oxc_cfg::ControlFlowGraph;
|
|||
use oxc_diagnostics::{OxcDiagnostic, Severity};
|
||||
use oxc_semantic::Semantic;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::rule::RuleFixMeta;
|
||||
|
|
@ -15,7 +14,7 @@ use crate::{
|
|||
disable_directives::DisableDirectives,
|
||||
fixer::{FixKind, Message, RuleFix, RuleFixer},
|
||||
javascript_globals::GLOBALS,
|
||||
AllowWarnDeny, FrameworkFlags, OxlintEnv, OxlintGlobals, OxlintSettings,
|
||||
AllowWarnDeny, FrameworkFlags, ModuleRecord, OxlintEnv, OxlintGlobals, OxlintSettings,
|
||||
};
|
||||
|
||||
pub(crate) use host::ContextHost;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::{hash, path::Path};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use crate::ModuleRecord;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ mod fixer;
|
|||
mod frameworks;
|
||||
mod globals;
|
||||
mod javascript_globals;
|
||||
mod module_graph_visitor;
|
||||
mod module_record;
|
||||
mod options;
|
||||
mod rule;
|
||||
mod rules;
|
||||
|
|
@ -24,7 +26,6 @@ pub mod table;
|
|||
use std::{io::Write, path::Path, rc::Rc, sync::Arc};
|
||||
|
||||
use oxc_semantic::{AstNode, Semantic};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
pub use crate::{
|
||||
builder::{LinterBuilder, LinterBuilderError},
|
||||
|
|
@ -32,6 +33,7 @@ pub use crate::{
|
|||
context::LintContext,
|
||||
fixer::FixKind,
|
||||
frameworks::FrameworkFlags,
|
||||
module_record::ModuleRecord,
|
||||
options::{AllowWarnDeny, InvalidFilterKind, LintFilter, LintFilterKind},
|
||||
rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity},
|
||||
service::{LintService, LintServiceOptions},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::{marker::PhantomData, path::PathBuf, sync::Arc};
|
|||
use oxc_span::CompactStr;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::module_record::ModuleRecord;
|
||||
use crate::ModuleRecord;
|
||||
|
||||
type ModulePair<'a> = (&'a CompactStr, &'a Arc<ModuleRecord>);
|
||||
|
||||
|
|
@ -73,19 +73,19 @@ impl<'a, T> ModuleGraphVisitorBuilder<'a, T> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the enter module event closure.
|
||||
#[must_use]
|
||||
pub fn enter<F: FnMut(ModulePair, &ModuleRecord) + 'a>(mut self, enter: F) -> Self {
|
||||
self.enter = Some(Box::new(enter));
|
||||
self
|
||||
}
|
||||
// /// Sets the enter module event closure.
|
||||
// #[must_use]
|
||||
// pub fn enter<F: FnMut(ModulePair, &ModuleRecord) + 'a>(mut self, enter: F) -> Self {
|
||||
// self.enter = Some(Box::new(enter));
|
||||
// self
|
||||
// }
|
||||
|
||||
/// Sets the leave module event closure.
|
||||
#[must_use]
|
||||
pub fn leave<F: FnMut(ModulePair, &ModuleRecord) + 'a>(mut self, leave: F) -> Self {
|
||||
self.leave = Some(Box::new(leave));
|
||||
self
|
||||
}
|
||||
// /// Sets the leave module event closure.
|
||||
// #[must_use]
|
||||
// pub fn leave<F: FnMut(ModulePair, &ModuleRecord) + 'a>(mut self, leave: F) -> Self {
|
||||
// self.leave = Some(Box::new(leave));
|
||||
// self
|
||||
// }
|
||||
|
||||
/// Behaves similar to a flat fold_while iteration.
|
||||
pub fn visit_fold<V: Fn(T, ModulePair, &ModuleRecord) -> VisitFoldWhile<T>>(
|
||||
|
|
@ -125,13 +125,13 @@ impl<T> Default for ModuleGraphVisitorBuilder<'_, T> {
|
|||
|
||||
pub struct ModuleGraphVisitResult<T> {
|
||||
pub result: T,
|
||||
pub traversed: FxHashSet<PathBuf>,
|
||||
pub max_depth: u32,
|
||||
pub _traversed: FxHashSet<PathBuf>,
|
||||
pub _max_depth: u32,
|
||||
}
|
||||
|
||||
impl<T> ModuleGraphVisitResult<T> {
|
||||
fn with_result(result: T, visitor: ModuleGraphVisitor) -> Self {
|
||||
Self { result, traversed: visitor.traversed, max_depth: visitor.max_depth }
|
||||
Self { result, _traversed: visitor.traversed, _max_depth: visitor.max_depth }
|
||||
}
|
||||
}
|
||||
|
||||
494
crates/oxc_linter/src/module_record.rs
Normal file
494
crates/oxc_linter/src/module_record.rs
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
//! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records)
|
||||
#![allow(missing_docs)] // fixme
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
|
||||
use oxc_span::{CompactStr, Span};
|
||||
pub use oxc_syntax::module_record::RequestedModule;
|
||||
|
||||
type FxDashMap<K, V> = DashMap<K, V, FxBuildHasher>;
|
||||
|
||||
/// ESM Module Record
|
||||
///
|
||||
/// All data inside this data structure are for ESM, no commonjs data is allowed.
|
||||
///
|
||||
/// See
|
||||
/// * <https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records>
|
||||
/// * <https://tc39.es/ecma262/#cyclic-module-record>
|
||||
#[derive(Default)]
|
||||
pub struct ModuleRecord {
|
||||
/// This module has no import / export statements
|
||||
pub not_esm: bool,
|
||||
|
||||
/// Resolved absolute path to this module record
|
||||
pub resolved_absolute_path: PathBuf,
|
||||
|
||||
/// `[[RequestedModules]]`
|
||||
///
|
||||
/// A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is in source text occurrence order.
|
||||
///
|
||||
/// Module requests from:
|
||||
/// import ImportClause FromClause
|
||||
/// import ModuleSpecifier
|
||||
/// export ExportFromClause FromClause
|
||||
/// Keyed by ModuleSpecifier, valued by all node occurrences
|
||||
pub requested_modules: FxHashMap<CompactStr, Vec<RequestedModule>>,
|
||||
|
||||
/// `[[LoadedModules]]`
|
||||
///
|
||||
/// A map from the specifier strings used by the module represented by this record to request
|
||||
/// the importation of a module to the resolved Module Record. The list does not contain two
|
||||
/// different Records with the same `[[Specifier]]`.
|
||||
///
|
||||
/// Note that Oxc does not support cross-file analysis, so this map will be empty after
|
||||
/// [`ModuleRecord`] is created. You must link the module records yourself.
|
||||
pub loaded_modules: FxDashMap<CompactStr, Arc<ModuleRecord>>,
|
||||
|
||||
/// `[[ImportEntries]]`
|
||||
///
|
||||
/// A List of `ImportEntry` records derived from the code of this module
|
||||
pub import_entries: Vec<ImportEntry>,
|
||||
|
||||
/// `[[LocalExportEntries]]`
|
||||
///
|
||||
/// A List of `ExportEntry` records derived from the code of this module
|
||||
/// that correspond to declarations that occur within the module
|
||||
pub local_export_entries: Vec<ExportEntry>,
|
||||
|
||||
/// `[[IndirectExportEntries]]`
|
||||
///
|
||||
/// A List of `ExportEntry` records derived from the code of this module
|
||||
/// that correspond to reexported imports that occur within the module
|
||||
/// or exports from `export * as namespace` declarations.
|
||||
pub indirect_export_entries: Vec<ExportEntry>,
|
||||
|
||||
/// `[[StarExportEntries]]`
|
||||
///
|
||||
/// A List of `ExportEntry` records derived from the code of this module
|
||||
/// that correspond to `export *` declarations that occur within the module,
|
||||
/// not including `export * as namespace` declarations.
|
||||
pub star_export_entries: Vec<ExportEntry>,
|
||||
|
||||
/// Local exported bindings
|
||||
pub exported_bindings: FxHashMap<CompactStr, Span>,
|
||||
|
||||
/// Local duplicated exported bindings, for diagnostics
|
||||
pub exported_bindings_duplicated: Vec<NameSpan>,
|
||||
|
||||
/// Reexported bindings from `export * from 'specifier'`
|
||||
/// Keyed by resolved path
|
||||
pub exported_bindings_from_star_export: FxDashMap<PathBuf, Vec<CompactStr>>,
|
||||
|
||||
/// `export default name`
|
||||
/// ^^^^^^^ span
|
||||
pub export_default: Option<Span>,
|
||||
|
||||
/// Duplicated span of `export default` for diagnostics
|
||||
pub export_default_duplicated: Vec<Span>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ModuleRecord {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// recursively formatting loaded modules can crash when the module graph is cyclic
|
||||
let loaded_modules = self
|
||||
.loaded_modules
|
||||
.iter()
|
||||
.map(|entry| (entry.key().to_string()))
|
||||
.reduce(|acc, key| format!("{acc}, {key}"))
|
||||
.unwrap_or_default();
|
||||
let loaded_modules = format!("{{ {loaded_modules} }}");
|
||||
f.debug_struct("ModuleRecord")
|
||||
.field("not_esm", &self.not_esm)
|
||||
.field("resolved_absolute_path", &self.resolved_absolute_path)
|
||||
.field("requested_modules", &self.requested_modules)
|
||||
.field("loaded_modules", &loaded_modules)
|
||||
.field("import_entries", &self.import_entries)
|
||||
.field("local_export_entries", &self.local_export_entries)
|
||||
.field("indirect_export_entries", &self.indirect_export_entries)
|
||||
.field("star_export_entries", &self.star_export_entries)
|
||||
.field("exported_bindings", &self.exported_bindings)
|
||||
.field("exported_bindings_duplicated", &self.exported_bindings_duplicated)
|
||||
.field("exported_bindings_from_star_export", &self.exported_bindings_from_star_export)
|
||||
.field("export_default", &self.export_default)
|
||||
.field("export_default_duplicated", &self.export_default_duplicated)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NameSpan {
|
||||
name: CompactStr,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl NameSpan {
|
||||
pub fn new(name: CompactStr, span: Span) -> Self {
|
||||
Self { name, span }
|
||||
}
|
||||
|
||||
pub const fn name(&self) -> &CompactStr {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub const fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::NameSpan<'a>> for NameSpan {
|
||||
fn from(other: &oxc_syntax::module_record::NameSpan<'a>) -> Self {
|
||||
Self { name: CompactStr::from(other.name.as_str()), span: other.span }
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ImportEntry`](https://tc39.es/ecma262/#importentry-record)
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```ts
|
||||
/// // _ local_name
|
||||
/// import v from "mod";
|
||||
/// // ^^^ module_request
|
||||
///
|
||||
/// // ____ is_type will be `true`
|
||||
/// import type { foo as bar } from "mod";
|
||||
/// // import_name^^^ ^^^ local_name
|
||||
///
|
||||
/// import * as ns from "mod";
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportEntry {
|
||||
/// String value of the ModuleSpecifier of the ImportDeclaration.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```ts
|
||||
/// import { foo } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub module_request: NameSpan,
|
||||
|
||||
/// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```ts
|
||||
/// import { foo } from "mod";
|
||||
/// // ^^^
|
||||
/// import { foo as bar } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub import_name: ImportImportName,
|
||||
|
||||
/// The name that is used to locally access the imported value from within the importing module.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```ts
|
||||
/// import { foo } from "mod";
|
||||
/// // ^^^
|
||||
/// import { foo as bar } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub local_name: NameSpan,
|
||||
|
||||
/// Whether this binding is for a TypeScript type-only import. This is a non-standard field.
|
||||
/// When creating a [`ModuleRecord`] for a JavaScript file, this will always be false.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// `is_type` will be `true` for the following imports:
|
||||
/// ```ts
|
||||
/// import type { foo } from "mod";
|
||||
/// import { type foo } from "mod";
|
||||
/// ```
|
||||
///
|
||||
/// and will be `false` for these imports:
|
||||
/// ```ts
|
||||
/// import { foo } from "mod";
|
||||
/// import { foo as type } from "mod";
|
||||
/// ```
|
||||
pub is_type: bool,
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ImportEntry<'a>> for ImportEntry {
|
||||
fn from(other: &oxc_syntax::module_record::ImportEntry<'a>) -> Self {
|
||||
Self {
|
||||
module_request: NameSpan::from(&other.module_request),
|
||||
import_name: ImportImportName::from(&other.import_name),
|
||||
local_name: NameSpan::from(&other.local_name),
|
||||
is_type: other.is_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `ImportName` For `ImportEntry`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ImportImportName {
|
||||
Name(NameSpan),
|
||||
NamespaceObject,
|
||||
Default(Span),
|
||||
}
|
||||
|
||||
impl ImportImportName {
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
}
|
||||
|
||||
pub fn is_namespace_object(&self) -> bool {
|
||||
matches!(self, Self::NamespaceObject)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ImportImportName<'a>> for ImportImportName {
|
||||
fn from(other: &oxc_syntax::module_record::ImportImportName<'a>) -> Self {
|
||||
match other {
|
||||
oxc_syntax::module_record::ImportImportName::Name(name_span) => {
|
||||
Self::Name(NameSpan::from(name_span))
|
||||
}
|
||||
oxc_syntax::module_record::ImportImportName::NamespaceObject => Self::NamespaceObject,
|
||||
oxc_syntax::module_record::ImportImportName::Default(span) => Self::Default(*span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ExportEntry`](https://tc39.es/ecma262/#exportentry-record)
|
||||
///
|
||||
/// Describes a single exported binding from a module. Named export statements that contain more
|
||||
/// than one binding produce multiple ExportEntry records.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```ts
|
||||
/// // foo's ExportEntry nas no `module_request` or `import_name.
|
||||
/// // ___ local_name
|
||||
/// export { foo };
|
||||
/// // ^^^ export_name. Since there's no alias, it's the same as local_name.
|
||||
///
|
||||
/// // re-exports do not produce local bindings, so `local_name` is null.
|
||||
/// // ___ import_name __ module_request
|
||||
/// export { foo as bar } from "mod";
|
||||
/// // ^^^ export_name
|
||||
///
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct ExportEntry {
|
||||
/// Span for the entire export entry
|
||||
pub span: Span,
|
||||
|
||||
/// The String value of the ModuleSpecifier of the ExportDeclaration.
|
||||
/// null if the ExportDeclaration does not have a ModuleSpecifier.
|
||||
pub module_request: Option<NameSpan>,
|
||||
|
||||
/// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`.
|
||||
/// null if the ExportDeclaration does not have a ModuleSpecifier.
|
||||
/// "all" is used for `export * as ns from "mod"`` declarations.
|
||||
/// "all-but-default" is used for `export * from "mod" declarations`.
|
||||
pub import_name: ExportImportName,
|
||||
|
||||
/// The name used to export this binding by this module.
|
||||
pub export_name: ExportExportName,
|
||||
|
||||
/// The name that is used to locally access the exported value from within the importing module.
|
||||
/// null if the exported value is not locally accessible from within the module.
|
||||
pub local_name: ExportLocalName,
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ExportEntry<'a>> for ExportEntry {
|
||||
fn from(other: &oxc_syntax::module_record::ExportEntry<'a>) -> Self {
|
||||
Self {
|
||||
span: other.span,
|
||||
module_request: other.module_request.as_ref().map(NameSpan::from),
|
||||
import_name: ExportImportName::from(&other.import_name),
|
||||
export_name: ExportExportName::from(&other.export_name),
|
||||
local_name: ExportLocalName::from(&other.local_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `ImportName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportImportName {
|
||||
Name(NameSpan),
|
||||
/// all is used for export * as ns from "mod" declarations.
|
||||
All,
|
||||
/// all-but-default is used for export * from "mod" declarations.
|
||||
AllButDefault,
|
||||
/// the ExportDeclaration does not have a ModuleSpecifier
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ExportImportName<'a>> for ExportImportName {
|
||||
fn from(other: &oxc_syntax::module_record::ExportImportName<'a>) -> Self {
|
||||
match other {
|
||||
oxc_syntax::module_record::ExportImportName::Name(name_span) => {
|
||||
Self::Name(NameSpan::from(name_span))
|
||||
}
|
||||
oxc_syntax::module_record::ExportImportName::All => Self::All,
|
||||
oxc_syntax::module_record::ExportImportName::AllButDefault => Self::AllButDefault,
|
||||
oxc_syntax::module_record::ExportImportName::Null => Self::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExportImportName {
|
||||
pub fn is_all(&self) -> bool {
|
||||
matches!(self, Self::All)
|
||||
}
|
||||
|
||||
pub fn is_all_but_default(&self) -> bool {
|
||||
matches!(self, Self::AllButDefault)
|
||||
}
|
||||
}
|
||||
|
||||
/// `ExportName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportExportName {
|
||||
Name(NameSpan),
|
||||
Default(Span),
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
impl ExportExportName {
|
||||
/// Returns `true` if this is [`ExportExportName::Default`].
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if this is [`ExportExportName::Null`].
|
||||
pub fn is_null(&self) -> bool {
|
||||
matches!(self, Self::Null)
|
||||
}
|
||||
|
||||
/// Attempt to get the [`Span`] of this export name.
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::Name(name) => Some(name.span()),
|
||||
Self::Default(span) => Some(*span),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ExportExportName<'a>> for ExportExportName {
|
||||
fn from(other: &oxc_syntax::module_record::ExportExportName<'a>) -> Self {
|
||||
match other {
|
||||
oxc_syntax::module_record::ExportExportName::Name(name_span) => {
|
||||
Self::Name(NameSpan::from(name_span))
|
||||
}
|
||||
oxc_syntax::module_record::ExportExportName::Default(span) => Self::Default(*span),
|
||||
oxc_syntax::module_record::ExportExportName::Null => Self::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `LocalName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportLocalName {
|
||||
Name(NameSpan),
|
||||
/// `export default name_span`
|
||||
Default(NameSpan),
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
impl ExportLocalName {
|
||||
/// `true` if this is a [`ExportLocalName::Default`].
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
}
|
||||
|
||||
/// `true` if this is a [`ExportLocalName::Null`].
|
||||
pub fn is_null(&self) -> bool {
|
||||
matches!(self, Self::Null)
|
||||
}
|
||||
|
||||
/// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`].
|
||||
pub const fn name(&self) -> Option<&CompactStr> {
|
||||
match self {
|
||||
Self::Name(name) | Self::Default(name) => Some(name.name()),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&oxc_syntax::module_record::ExportLocalName<'a>> for ExportLocalName {
|
||||
fn from(other: &oxc_syntax::module_record::ExportLocalName<'a>) -> Self {
|
||||
match other {
|
||||
oxc_syntax::module_record::ExportLocalName::Name(name_span) => {
|
||||
Self::Name(NameSpan::from(name_span))
|
||||
}
|
||||
oxc_syntax::module_record::ExportLocalName::Default(name_span) => {
|
||||
Self::Default(NameSpan::from(name_span))
|
||||
}
|
||||
oxc_syntax::module_record::ExportLocalName::Null => Self::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleRecord {
|
||||
pub fn new(path: &Path, other: &oxc_syntax::module_record::ModuleRecord) -> Self {
|
||||
Self {
|
||||
not_esm: other.not_esm,
|
||||
resolved_absolute_path: path.to_path_buf(),
|
||||
requested_modules: other
|
||||
.requested_modules
|
||||
.iter()
|
||||
.map(|(name, requested_modules)| {
|
||||
(
|
||||
CompactStr::from(name.as_str()),
|
||||
requested_modules.iter().copied().collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
import_entries: other.import_entries.iter().map(ImportEntry::from).collect(),
|
||||
|
||||
local_export_entries: other
|
||||
.local_export_entries
|
||||
.iter()
|
||||
.map(ExportEntry::from)
|
||||
.collect(),
|
||||
indirect_export_entries: other
|
||||
.indirect_export_entries
|
||||
.iter()
|
||||
.map(ExportEntry::from)
|
||||
.collect(),
|
||||
star_export_entries: other.star_export_entries.iter().map(ExportEntry::from).collect(),
|
||||
exported_bindings: other
|
||||
.exported_bindings
|
||||
.iter()
|
||||
.map(|(name, span)| (CompactStr::from(name.as_str()), *span))
|
||||
.collect(),
|
||||
exported_bindings_duplicated: other
|
||||
.exported_bindings_duplicated
|
||||
.iter()
|
||||
.map(NameSpan::from)
|
||||
.collect(),
|
||||
exported_bindings_from_star_export: other
|
||||
.exported_bindings_from_star_export
|
||||
.iter()
|
||||
.map(|(name, values)| {
|
||||
(
|
||||
PathBuf::from(name.as_str()),
|
||||
values
|
||||
.into_iter()
|
||||
.map(|v| CompactStr::from(v.as_str()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
export_default: other.export_default,
|
||||
export_default_duplicated: other.export_default_duplicated.iter().copied().collect(),
|
||||
..ModuleRecord::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,12 @@ use rustc_hash::FxHashMap;
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use oxc_syntax::module_record::{ExportImportName, ImportImportName};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_record::{ExportImportName, ImportImportName},
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn no_duplicate_imports_diagnostic(module_name: &str, span: Span, span2: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!("'{module_name}' import is duplicated"))
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
//! consider variables ignored by name pattern, but by where they are declared.
|
||||
use oxc_ast::{ast::*, AstKind};
|
||||
use oxc_semantic::{NodeId, Semantic};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use super::{options::ArgsOption, NoUnusedVars, Symbol};
|
||||
use crate::rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding};
|
||||
use crate::{
|
||||
rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding},
|
||||
ModuleRecord,
|
||||
};
|
||||
|
||||
impl Symbol<'_, '_> {
|
||||
/// Check if the declaration of this [`Symbol`] is use.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use oxc_ast::ast::*;
|
||||
use oxc_semantic::{Semantic, SymbolId};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use super::{symbol::Symbol, NoUnusedVars};
|
||||
|
||||
use crate::ModuleRecord;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct BindingContext<'s, 'a> {
|
||||
pub options: &'s NoUnusedVars,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ use oxc_semantic::{
|
|||
SymbolTable,
|
||||
};
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use crate::ModuleRecord;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Symbol<'s, 'a> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{Span, VALID_EXTENSIONS};
|
||||
use oxc_syntax::module_record::ImportImportName;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{context::LintContext, module_record::ImportImportName, rule::Rule};
|
||||
|
||||
fn default_diagnostic(imported_name: &str, span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!("No default export found in imported module {imported_name:?}"))
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{context::LintContext, rule::Rule, ModuleRecord};
|
||||
|
||||
fn no_named_export(module_name: &str, span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!("No named exports found in module '{module_name}'"))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::module_record::{ExportImportName, ImportImportName};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_record::{ExportImportName, ImportImportName},
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn named_diagnostic(imported_name: &str, module_name: &str, span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!("named import {imported_name:?} not found"))
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ use oxc_diagnostics::OxcDiagnostic;
|
|||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::AstNode;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::{
|
||||
ExportExportName, ExportImportName, ImportImportName, ModuleRecord,
|
||||
};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_record::{ExportExportName, ExportImportName, ImportImportName, ModuleRecord},
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn no_export(span: Span, specifier_name: &str, namespace_name: &str) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!(
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ use cow_utils::CowUtils;
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use oxc_syntax::{
|
||||
module_graph_visitor::{ModuleGraphVisitorBuilder, ModuleGraphVisitorEvent, VisitFoldWhile},
|
||||
module_record::ModuleRecord,
|
||||
};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_graph_visitor::{ModuleGraphVisitorBuilder, ModuleGraphVisitorEvent, VisitFoldWhile},
|
||||
rule::Rule,
|
||||
ModuleRecord,
|
||||
};
|
||||
|
||||
fn no_cycle_diagnostic(span: Span, paths: &str) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Dependency cycle detected")
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ use itertools::Itertools;
|
|||
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::module_record::{ImportImportName, RequestedModule};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_record::{ImportImportName, RequestedModule},
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn no_duplicates_diagnostic<I>(
|
||||
module_name: &str,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::module_record::ImportImportName;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{context::LintContext, module_record::ImportImportName, rule::Rule};
|
||||
|
||||
fn no_named_as_default_diagnostic(
|
||||
span: Span,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ use oxc_diagnostics::OxcDiagnostic;
|
|||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::SymbolId;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::module_record::ImportImportName;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{context::LintContext, module_record::ImportImportName, rule::Rule};
|
||||
|
||||
fn no_named_as_default_member_dignostic(
|
||||
span: Span,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ use oxc_diagnostics::OxcDiagnostic;
|
|||
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use oxc_syntax::module_record::ImportImportName;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{context::LintContext, module_record::ImportImportName, rule::Rule};
|
||||
|
||||
fn no_namespace_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Usage of namespaced aka wildcard \"*\" imports prohibited")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_syntax::{
|
||||
module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile},
|
||||
module_record::ModuleRecord,
|
||||
};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile},
|
||||
rule::Rule,
|
||||
ModuleRecord,
|
||||
};
|
||||
|
||||
fn no_barrel_file(total: usize, threshold: usize, labels: Vec<LabeledSpan>) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
use dashmap::{mapref::one::Ref, DashMap};
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
use crate::ModuleRecord;
|
||||
|
||||
type FxDashMap<K, V> = DashMap<K, V, FxBuildHasher>;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use rayon::{iter::ParallelBridge, prelude::ParallelIterator};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_diagnostics::{DiagnosticSender, DiagnosticService, Error, OxcDiagnostic};
|
||||
use oxc_parser::{ParseOptions, Parser};
|
||||
use oxc_resolver::Resolver;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
use rayon::{iter::ParallelBridge, prelude::ParallelIterator};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
loader::{JavaScriptSource, PartialLoader, LINT_PARTIAL_LOADER_EXT},
|
||||
module_record::ModuleRecord,
|
||||
utils::read_to_string,
|
||||
Fixer, Linter, Message,
|
||||
};
|
||||
|
|
@ -218,7 +219,7 @@ impl Runtime {
|
|||
.with_build_jsdoc(true)
|
||||
.with_check_syntax_error(check_syntax_errors);
|
||||
|
||||
let mut module_record = ret.module_record;
|
||||
let mut module_record = ModuleRecord::new(path, &ret.module_record);
|
||||
module_record.resolved_absolute_path = path.to_path_buf();
|
||||
let module_record = Arc::new(module_record);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ snapshot_kind: text
|
|||
╰────
|
||||
help: Merge these imports into a single import statement
|
||||
|
||||
⚠ eslint-plugin-import(no-duplicates): Module './bar?optionX' is imported more than once in this file
|
||||
╭─[index.ts:1:49]
|
||||
⚠ eslint-plugin-import(no-duplicates): Module './bar.js?optionX' is imported more than once in this file
|
||||
╭─[index.ts:1:15]
|
||||
1 │ import x from './bar.js?optionX'; import y from './bar?optionX';
|
||||
· ────────────────── ───────┬───────
|
||||
· │ ╰── It is first imported here
|
||||
· ─────────┬──────── ───────────────
|
||||
· ╰── It is first imported here
|
||||
╰────
|
||||
help: Merge these imports into a single import statement
|
||||
|
||||
|
|
@ -34,11 +34,11 @@ snapshot_kind: text
|
|||
╰────
|
||||
help: Merge these imports into a single import statement
|
||||
|
||||
⚠ eslint-plugin-import(no-duplicates): Module './bar.js?optionX' is imported more than once in this file
|
||||
╭─[index.ts:1:46]
|
||||
⚠ eslint-plugin-import(no-duplicates): Module './bar?optionX' is imported more than once in this file
|
||||
╭─[index.ts:1:15]
|
||||
1 │ import x from './bar?optionX'; import y from './bar.js?optionX';
|
||||
· ─────────────── ─────────┬────────
|
||||
· │ ╰── It is first imported here
|
||||
· ───────┬─────── ──────────────────
|
||||
· ╰── It is first imported here
|
||||
╰────
|
||||
help: Merge these imports into a single import statement
|
||||
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ mod test {
|
|||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use crate::{options::LintOptions, ContextHost};
|
||||
use crate::{options::LintOptions, ContextHost, ModuleRecord};
|
||||
|
||||
#[test]
|
||||
fn test_is_jest_file() {
|
||||
|
|
@ -321,12 +321,11 @@ mod test {
|
|||
SemanticBuilder::new().with_cfg(true).build(&parser_ret.program).semantic;
|
||||
let semantic_ret = Rc::new(semantic_ret);
|
||||
|
||||
let module_record = Arc::new(parser_ret.module_record);
|
||||
let build_ctx = |path: &'static str| {
|
||||
Rc::new(ContextHost::new(
|
||||
path,
|
||||
Rc::clone(&semantic_ret),
|
||||
Arc::clone(&module_record),
|
||||
Arc::new(ModuleRecord::default()),
|
||||
LintOptions::default(),
|
||||
Arc::default(),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ pub struct ParserReturn<'a> {
|
|||
pub program: Program<'a>,
|
||||
|
||||
/// See <https://tc39.es/ecma262/#sec-abstract-module-records>
|
||||
pub module_record: ModuleRecord,
|
||||
pub module_record: ModuleRecord<'a>,
|
||||
|
||||
/// Syntax errors encountered while parsing.
|
||||
///
|
||||
|
|
@ -370,7 +370,7 @@ struct ParserImpl<'a> {
|
|||
ast: AstBuilder<'a>,
|
||||
|
||||
/// Module Record Builder
|
||||
module_record_builder: ModuleRecordBuilder,
|
||||
module_record_builder: ModuleRecordBuilder<'a>,
|
||||
|
||||
/// Precomputed typescript detection
|
||||
is_ts: bool,
|
||||
|
|
@ -389,8 +389,6 @@ impl<'a> ParserImpl<'a> {
|
|||
options: ParseOptions,
|
||||
unique: UniquePromise,
|
||||
) -> Self {
|
||||
let mut module_record_builder = ModuleRecordBuilder::default();
|
||||
module_record_builder.module_record.not_esm = true;
|
||||
Self {
|
||||
options,
|
||||
lexer: Lexer::new(allocator, source_text, source_type, unique),
|
||||
|
|
@ -402,7 +400,7 @@ impl<'a> ParserImpl<'a> {
|
|||
state: ParserState::default(),
|
||||
ctx: Self::default_context(source_type, options),
|
||||
ast: AstBuilder::new(allocator),
|
||||
module_record_builder,
|
||||
module_record_builder: ModuleRecordBuilder::new(allocator),
|
||||
is_ts: source_type.is_typescript(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,24 @@
|
|||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_ecmascript::BoundNames;
|
||||
use oxc_span::{CompactStr, GetSpan, Span};
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::module_record::*;
|
||||
|
||||
use crate::diagnostics;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModuleRecordBuilder {
|
||||
pub module_record: ModuleRecord,
|
||||
export_entries: Vec<ExportEntry>,
|
||||
pub struct ModuleRecordBuilder<'a> {
|
||||
allocator: &'a Allocator,
|
||||
module_record: ModuleRecord<'a>,
|
||||
export_entries: Vec<ExportEntry<'a>>,
|
||||
}
|
||||
|
||||
impl ModuleRecordBuilder {
|
||||
pub fn build(mut self) -> ModuleRecord {
|
||||
impl<'a> ModuleRecordBuilder<'a> {
|
||||
pub fn new(allocator: &'a Allocator) -> Self {
|
||||
Self { allocator, module_record: ModuleRecord::new(allocator), export_entries: vec![] }
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> ModuleRecord<'a> {
|
||||
// The `ParseModule` algorithm requires `importedBoundNames` (import entries) to be
|
||||
// resolved before resolving export entries.
|
||||
self.resolve_export_entries();
|
||||
|
|
@ -27,12 +32,8 @@ impl ModuleRecordBuilder {
|
|||
|
||||
// It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
|
||||
for name_span in &module_record.exported_bindings_duplicated {
|
||||
let old_span = module_record.exported_bindings[name_span.name()];
|
||||
errors.push(diagnostics::duplicate_export(
|
||||
name_span.name(),
|
||||
name_span.span(),
|
||||
old_span,
|
||||
));
|
||||
let old_span = module_record.exported_bindings[&name_span.name];
|
||||
errors.push(diagnostics::duplicate_export(&name_span.name, name_span.span, old_span));
|
||||
}
|
||||
|
||||
for span in &module_record.export_default_duplicated {
|
||||
|
|
@ -51,35 +52,35 @@ impl ModuleRecordBuilder {
|
|||
errors
|
||||
}
|
||||
|
||||
fn add_module_request(&mut self, name_span: &NameSpan, is_type: bool, is_import: bool) {
|
||||
fn add_module_request(&mut self, name_span: &NameSpan<'a>, is_type: bool, is_import: bool) {
|
||||
self.module_record
|
||||
.requested_modules
|
||||
.entry(name_span.name().clone())
|
||||
.or_default()
|
||||
.push(RequestedModule::new(name_span.span(), is_type, is_import));
|
||||
.entry(name_span.name.clone())
|
||||
.or_insert_with(|| oxc_allocator::Vec::new_in(self.allocator))
|
||||
.push(RequestedModule::new(name_span.span, is_type, is_import));
|
||||
}
|
||||
|
||||
fn add_import_entry(&mut self, entry: ImportEntry) {
|
||||
fn add_import_entry(&mut self, entry: ImportEntry<'a>) {
|
||||
self.module_record.import_entries.push(entry);
|
||||
}
|
||||
|
||||
fn add_export_entry(&mut self, entry: ExportEntry) {
|
||||
fn add_export_entry(&mut self, entry: ExportEntry<'a>) {
|
||||
self.export_entries.push(entry);
|
||||
}
|
||||
|
||||
fn append_local_export_entry(&mut self, entry: ExportEntry) {
|
||||
fn append_local_export_entry(&mut self, entry: ExportEntry<'a>) {
|
||||
self.module_record.local_export_entries.push(entry);
|
||||
}
|
||||
|
||||
fn append_indirect_export_entry(&mut self, entry: ExportEntry) {
|
||||
fn append_indirect_export_entry(&mut self, entry: ExportEntry<'a>) {
|
||||
self.module_record.indirect_export_entries.push(entry);
|
||||
}
|
||||
|
||||
fn append_star_export_entry(&mut self, entry: ExportEntry) {
|
||||
fn append_star_export_entry(&mut self, entry: ExportEntry<'a>) {
|
||||
self.module_record.star_export_entries.push(entry);
|
||||
}
|
||||
|
||||
fn add_export_binding(&mut self, name: CompactStr, span: Span) {
|
||||
fn add_export_binding(&mut self, name: Atom<'a>, span: Span) {
|
||||
if let Some(old_node) = self.module_record.exported_bindings.insert(name.clone(), span) {
|
||||
self.module_record.exported_bindings_duplicated.push(NameSpan::new(name, old_node));
|
||||
}
|
||||
|
|
@ -104,7 +105,7 @@ impl ModuleRecordBuilder {
|
|||
.module_record
|
||||
.import_entries
|
||||
.iter()
|
||||
.find(|entry| entry.local_name.name() == name.name()),
|
||||
.find(|entry| entry.local_name.name == name.name),
|
||||
_ => None,
|
||||
};
|
||||
match found_import_entry {
|
||||
|
|
@ -164,7 +165,7 @@ impl ModuleRecordBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn visit_module_declaration(&mut self, module_decl: &ModuleDeclaration) {
|
||||
pub fn visit_module_declaration(&mut self, module_decl: &ModuleDeclaration<'a>) {
|
||||
self.module_record.not_esm = false;
|
||||
match module_decl {
|
||||
ModuleDeclaration::ImportDeclaration(import_decl) => {
|
||||
|
|
@ -184,28 +185,28 @@ impl ModuleRecordBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_import_declaration(&mut self, decl: &ImportDeclaration) {
|
||||
let module_request = NameSpan::new(decl.source.value.to_compact_str(), decl.source.span);
|
||||
fn visit_import_declaration(&mut self, decl: &ImportDeclaration<'a>) {
|
||||
let module_request = NameSpan::new(decl.source.value.clone(), decl.source.span);
|
||||
|
||||
if let Some(specifiers) = &decl.specifiers {
|
||||
for specifier in specifiers {
|
||||
let (import_name, local_name, is_type) = match specifier {
|
||||
ImportDeclarationSpecifier::ImportSpecifier(specifier) => (
|
||||
ImportImportName::Name(NameSpan::new(
|
||||
specifier.imported.name().to_compact_str(),
|
||||
specifier.imported.name(),
|
||||
specifier.imported.span(),
|
||||
)),
|
||||
NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span),
|
||||
NameSpan::new(specifier.local.name.clone(), specifier.local.span),
|
||||
decl.import_kind.is_type() || specifier.import_kind.is_type(),
|
||||
),
|
||||
ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => (
|
||||
ImportImportName::NamespaceObject,
|
||||
NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span),
|
||||
NameSpan::new(specifier.local.name.clone(), specifier.local.span),
|
||||
decl.import_kind.is_type(),
|
||||
),
|
||||
ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => (
|
||||
ImportImportName::Default(specifier.span),
|
||||
NameSpan::new(specifier.local.name.to_compact_str(), specifier.local.span),
|
||||
NameSpan::new(specifier.local.name.clone(), specifier.local.span),
|
||||
decl.import_kind.is_type(),
|
||||
),
|
||||
};
|
||||
|
|
@ -224,8 +225,8 @@ impl ModuleRecordBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
fn visit_export_all_declaration(&mut self, decl: &ExportAllDeclaration) {
|
||||
let module_request = NameSpan::new(decl.source.value.to_compact_str(), decl.source.span);
|
||||
fn visit_export_all_declaration(&mut self, decl: &ExportAllDeclaration<'a>) {
|
||||
let module_request = NameSpan::new(decl.source.value.clone(), decl.source.span);
|
||||
let export_entry = ExportEntry {
|
||||
module_request: Some(module_request.clone()),
|
||||
import_name: decl
|
||||
|
|
@ -233,17 +234,14 @@ impl ModuleRecordBuilder {
|
|||
.as_ref()
|
||||
.map_or(ExportImportName::AllButDefault, |_| ExportImportName::All),
|
||||
export_name: decl.exported.as_ref().map_or(ExportExportName::Null, |exported_name| {
|
||||
ExportExportName::Name(NameSpan::new(
|
||||
exported_name.name().to_compact_str(),
|
||||
exported_name.span(),
|
||||
))
|
||||
ExportExportName::Name(NameSpan::new(exported_name.name(), exported_name.span()))
|
||||
}),
|
||||
span: decl.span,
|
||||
..ExportEntry::default()
|
||||
};
|
||||
self.add_export_entry(export_entry);
|
||||
if let Some(exported_name) = &decl.exported {
|
||||
self.add_export_binding(exported_name.name().to_compact_str(), exported_name.span());
|
||||
self.add_export_binding(exported_name.name(), exported_name.span());
|
||||
}
|
||||
self.add_module_request(
|
||||
&module_request,
|
||||
|
|
@ -252,7 +250,7 @@ impl ModuleRecordBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration) {
|
||||
fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration<'a>) {
|
||||
// ignore all TypeScript syntax as they overload
|
||||
if decl.declaration.is_typescript_syntax() {
|
||||
return;
|
||||
|
|
@ -263,17 +261,17 @@ impl ModuleRecordBuilder {
|
|||
|
||||
let local_name = match &decl.declaration {
|
||||
ExportDefaultDeclarationKind::Identifier(ident) => {
|
||||
ExportLocalName::Default(NameSpan::new(ident.name.to_compact_str(), ident.span))
|
||||
ExportLocalName::Default(NameSpan::new(ident.name.clone(), ident.span))
|
||||
}
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(func) => {
|
||||
func.id.as_ref().map_or_else(
|
||||
|| ExportLocalName::Null,
|
||||
|id| ExportLocalName::Name(NameSpan::new(id.name.to_compact_str(), id.span)),
|
||||
|id| ExportLocalName::Name(NameSpan::new(id.name.clone(), id.span)),
|
||||
)
|
||||
}
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref().map_or_else(
|
||||
|| ExportLocalName::Null,
|
||||
|id| ExportLocalName::Name(NameSpan::new(id.name.to_compact_str(), id.span)),
|
||||
|id| ExportLocalName::Name(NameSpan::new(id.name.clone(), id.span)),
|
||||
),
|
||||
_ => ExportLocalName::Null,
|
||||
};
|
||||
|
|
@ -286,7 +284,7 @@ impl ModuleRecordBuilder {
|
|||
self.add_export_entry(export_entry);
|
||||
}
|
||||
|
||||
fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration) {
|
||||
fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) {
|
||||
if decl.export_kind.is_type() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -295,10 +293,8 @@ impl ModuleRecordBuilder {
|
|||
return;
|
||||
}
|
||||
|
||||
let module_request = decl
|
||||
.source
|
||||
.as_ref()
|
||||
.map(|source| NameSpan::new(source.value.to_compact_str(), source.span));
|
||||
let module_request =
|
||||
decl.source.as_ref().map(|source| NameSpan::new(source.value.clone(), source.span));
|
||||
|
||||
if let Some(module_request) = &module_request {
|
||||
self.add_module_request(
|
||||
|
|
@ -311,9 +307,9 @@ impl ModuleRecordBuilder {
|
|||
if let Some(decl) = &decl.declaration {
|
||||
decl.bound_names(&mut |ident| {
|
||||
let export_name =
|
||||
ExportExportName::Name(NameSpan::new(ident.name.to_compact_str(), ident.span));
|
||||
ExportExportName::Name(NameSpan::new(ident.name.clone(), ident.span));
|
||||
let local_name =
|
||||
ExportLocalName::Name(NameSpan::new(ident.name.to_compact_str(), ident.span));
|
||||
ExportLocalName::Name(NameSpan::new(ident.name.clone(), ident.span));
|
||||
let export_entry = ExportEntry {
|
||||
span: decl.span(),
|
||||
module_request: module_request.clone(),
|
||||
|
|
@ -322,18 +318,18 @@ impl ModuleRecordBuilder {
|
|||
local_name,
|
||||
};
|
||||
self.add_export_entry(export_entry);
|
||||
self.add_export_binding(ident.name.to_compact_str(), ident.span);
|
||||
self.add_export_binding(ident.name.clone(), ident.span);
|
||||
});
|
||||
}
|
||||
|
||||
for specifier in &decl.specifiers {
|
||||
let export_name = ExportExportName::Name(NameSpan::new(
|
||||
specifier.exported.name().to_compact_str(),
|
||||
specifier.exported.name().clone(),
|
||||
specifier.exported.span(),
|
||||
));
|
||||
let import_name = if module_request.is_some() {
|
||||
ExportImportName::Name(NameSpan::new(
|
||||
specifier.local.name().to_compact_str(),
|
||||
specifier.local.name().clone(),
|
||||
specifier.local.span(),
|
||||
))
|
||||
} else {
|
||||
|
|
@ -343,7 +339,7 @@ impl ModuleRecordBuilder {
|
|||
ExportLocalName::Null
|
||||
} else {
|
||||
ExportLocalName::Name(NameSpan::new(
|
||||
specifier.local.name().to_compact_str(),
|
||||
specifier.local.name().clone(),
|
||||
specifier.local.span(),
|
||||
))
|
||||
};
|
||||
|
|
@ -355,10 +351,7 @@ impl ModuleRecordBuilder {
|
|||
local_name,
|
||||
};
|
||||
self.add_export_entry(export_entry);
|
||||
self.add_export_binding(
|
||||
specifier.exported.name().to_compact_str(),
|
||||
specifier.exported.span(),
|
||||
);
|
||||
self.add_export_binding(specifier.exported.name().clone(), specifier.exported.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -371,10 +364,9 @@ mod module_record_tests {
|
|||
|
||||
use crate::Parser;
|
||||
|
||||
fn build(source_text: &str) -> ModuleRecord {
|
||||
fn build<'a>(allocator: &'a Allocator, source_text: &'a str) -> ModuleRecord<'a> {
|
||||
let source_type = SourceType::mjs();
|
||||
let allocator = Allocator::default();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
let ret = Parser::new(allocator, source_text, source_type).parse();
|
||||
ret.module_record
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +375,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn import_default() {
|
||||
let module_record = build("import v from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "import v from 'mod'");
|
||||
let import_entry = ImportEntry {
|
||||
module_request: NameSpan::new("mod".into(), Span::new(14, 19)),
|
||||
import_name: ImportImportName::Default(Span::new(7, 8)),
|
||||
|
|
@ -396,7 +389,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn import_namespace() {
|
||||
let module_record = build("import * as ns from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "import * as ns from 'mod'");
|
||||
let import_entry = ImportEntry {
|
||||
module_request: NameSpan::new("mod".into(), Span::new(20, 25)),
|
||||
import_name: ImportImportName::NamespaceObject,
|
||||
|
|
@ -409,7 +403,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn import_specifier() {
|
||||
let module_record = build("import { x } from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "import { x } from 'mod'");
|
||||
let import_entry = ImportEntry {
|
||||
module_request: NameSpan::new("mod".into(), Span::new(18, 23)),
|
||||
import_name: ImportImportName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
|
|
@ -422,7 +417,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn import_specifier_alias() {
|
||||
let module_record = build("import { x as v } from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "import { x as v } from 'mod'");
|
||||
let import_entry = ImportEntry {
|
||||
module_request: NameSpan::new("mod".into(), Span::new(23, 28)),
|
||||
import_name: ImportImportName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
|
|
@ -435,7 +431,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn import_without_binding() {
|
||||
let module_record = build("import 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "import 'mod'");
|
||||
assert!(module_record.import_entries.is_empty());
|
||||
}
|
||||
|
||||
|
|
@ -446,7 +443,8 @@ mod module_record_tests {
|
|||
fn export_star() {
|
||||
// ExportDeclaration : export ExportFromClause FromClause ;
|
||||
// ExportFromClause : *
|
||||
let module_record = build("export * from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export * from 'mod'");
|
||||
let export_entry = ExportEntry {
|
||||
module_request: Some(NameSpan::new("mod".into(), Span::new(14, 19))),
|
||||
import_name: ExportImportName::AllButDefault,
|
||||
|
|
@ -461,7 +459,8 @@ mod module_record_tests {
|
|||
fn export_star_as_namespace() {
|
||||
// ExportDeclaration : export ExportFromClause FromClause ;
|
||||
// ExportFromClause : * as ModuleExportName
|
||||
let module_record = build("export * as ns from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export * as ns from 'mod'");
|
||||
let export_entry = ExportEntry {
|
||||
module_request: Some(NameSpan::new("mod".into(), Span::new(20, 25))),
|
||||
import_name: ExportImportName::All,
|
||||
|
|
@ -477,7 +476,8 @@ mod module_record_tests {
|
|||
fn named_exports() {
|
||||
// ExportDeclaration : export NamedExports ;
|
||||
// ExportSpecifier : ModuleExportName
|
||||
let module_record = build("export { x }");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export { x }");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
local_name: ExportLocalName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
|
|
@ -492,7 +492,8 @@ mod module_record_tests {
|
|||
fn named_exports_alias() {
|
||||
// ExportDeclaration : export NamedExports ;
|
||||
// ExportSpecifier : ModuleExportName as ModuleExportName
|
||||
let module_record = build("export { x as v }");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export { x as v }");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(14, 15))),
|
||||
local_name: ExportLocalName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
|
|
@ -507,7 +508,8 @@ mod module_record_tests {
|
|||
fn named_exports_from() {
|
||||
// ExportDeclaration : export ExportFromClause FromClause ;
|
||||
// ExportSpecifier : ModuleExportName
|
||||
let module_record = build("export { x } from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export { x } from 'mod'");
|
||||
let export_entry = ExportEntry {
|
||||
module_request: Some(NameSpan::new("mod".into(), Span::new(18, 23))),
|
||||
export_name: ExportExportName::Name(NameSpan::new("x".into(), Span::new(9, 10))),
|
||||
|
|
@ -523,7 +525,8 @@ mod module_record_tests {
|
|||
fn named_exports_alias_from() {
|
||||
// ExportDeclaration : export ExportFromClause FromClause ;
|
||||
// ExportSpecifier : ModuleExportName as ModuleExportName
|
||||
let module_record = build("export { x as v } from 'mod'");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export { x as v } from 'mod'");
|
||||
let export_entry = ExportEntry {
|
||||
module_request: Some(NameSpan::new("mod".into(), Span::new(23, 28))),
|
||||
export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(14, 15))),
|
||||
|
|
@ -538,7 +541,8 @@ mod module_record_tests {
|
|||
#[test]
|
||||
fn export_declaration() {
|
||||
// ExportDeclaration : export VariableStatement
|
||||
let module_record = build("export var v");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export var v");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Name(NameSpan::new("v".into(), Span::new(11, 12))),
|
||||
local_name: ExportLocalName::Name(NameSpan::new("v".into(), Span::new(11, 12))),
|
||||
|
|
@ -552,7 +556,8 @@ mod module_record_tests {
|
|||
#[test]
|
||||
fn export_default_declaration() {
|
||||
// ExportDeclaration : export default HoistableDeclaration
|
||||
let module_record = build("export default function f() {}");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export default function f() {}");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Default(Span::new(7, 14)),
|
||||
local_name: ExportLocalName::Name(NameSpan::new("f".into(), Span::new(24, 25))),
|
||||
|
|
@ -566,7 +571,8 @@ mod module_record_tests {
|
|||
#[test]
|
||||
fn export_default_function_expression() {
|
||||
// ExportDeclaration : export default HoistableDeclaration
|
||||
let module_record = build("export default function() {}");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export default function() {}");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Default(Span::new(7, 14)),
|
||||
local_name: ExportLocalName::Null,
|
||||
|
|
@ -580,7 +586,8 @@ mod module_record_tests {
|
|||
#[test]
|
||||
fn export_default_expression() {
|
||||
// ExportDeclaration : export default HoistableDeclaration
|
||||
let module_record = build("export default 42");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export default 42");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Default(Span::new(7, 14)),
|
||||
local_name: ExportLocalName::Null,
|
||||
|
|
@ -593,7 +600,8 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn export_named_default() {
|
||||
let module_record = build("export { default }");
|
||||
let allocator = Allocator::default();
|
||||
let module_record = build(&allocator, "export { default }");
|
||||
let export_entry = ExportEntry {
|
||||
export_name: ExportExportName::Name(NameSpan::new("default".into(), Span::new(9, 16))),
|
||||
local_name: ExportLocalName::Name(NameSpan::new("default".into(), Span::new(9, 16))),
|
||||
|
|
@ -606,8 +614,9 @@ mod module_record_tests {
|
|||
|
||||
#[test]
|
||||
fn indirect_export_entries() {
|
||||
let allocator = Allocator::default();
|
||||
let module_record =
|
||||
build("import { x } from 'mod';export { x };export * as ns from 'mod';");
|
||||
build(&allocator, "import { x } from 'mod';export { x };export * as ns from 'mod';");
|
||||
assert_eq!(module_record.indirect_export_entries.len(), 2);
|
||||
assert_eq!(
|
||||
module_record.indirect_export_entries[0],
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ oxc_span = { workspace = true }
|
|||
|
||||
assert-unchecked = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
nonmax = { workspace = true }
|
||||
phf = { workspace = true, features = ["macros"] }
|
||||
rustc-hash = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
pub mod class;
|
||||
pub mod identifier;
|
||||
pub mod keyword;
|
||||
pub mod module_graph_visitor;
|
||||
pub mod module_record;
|
||||
pub mod node;
|
||||
pub mod number;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
//! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records)
|
||||
#![allow(missing_docs)] // fixme
|
||||
|
||||
use std::{fmt, path::PathBuf, sync::Arc};
|
||||
use std::fmt;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
use oxc_allocator::{Allocator, Vec};
|
||||
use oxc_span::{Atom, Span};
|
||||
|
||||
type FxDashMap<K, V> = DashMap<K, V, FxBuildHasher>;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
/// ESM Module Record
|
||||
///
|
||||
|
|
@ -16,14 +15,10 @@ type FxDashMap<K, V> = DashMap<K, V, FxBuildHasher>;
|
|||
/// See
|
||||
/// * <https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records>
|
||||
/// * <https://tc39.es/ecma262/#cyclic-module-record>
|
||||
#[derive(Default)]
|
||||
pub struct ModuleRecord {
|
||||
pub struct ModuleRecord<'a> {
|
||||
/// This module has no import / export statements
|
||||
pub not_esm: bool,
|
||||
|
||||
/// Resolved absolute path to this module record
|
||||
pub resolved_absolute_path: PathBuf,
|
||||
|
||||
/// `[[RequestedModules]]`
|
||||
///
|
||||
/// A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is in source text occurrence order.
|
||||
|
|
@ -33,82 +28,73 @@ pub struct ModuleRecord {
|
|||
/// import ModuleSpecifier
|
||||
/// export ExportFromClause FromClause
|
||||
/// Keyed by ModuleSpecifier, valued by all node occurrences
|
||||
pub requested_modules: FxHashMap<CompactStr, Vec<RequestedModule>>,
|
||||
|
||||
/// `[[LoadedModules]]`
|
||||
///
|
||||
/// A map from the specifier strings used by the module represented by this record to request
|
||||
/// the importation of a module to the resolved Module Record. The list does not contain two
|
||||
/// different Records with the same `[[Specifier]]`.
|
||||
///
|
||||
/// Note that Oxc does not support cross-file analysis, so this map will be empty after
|
||||
/// [`ModuleRecord`] is created. You must link the module records yourself.
|
||||
pub loaded_modules: FxDashMap<CompactStr, Arc<ModuleRecord>>,
|
||||
pub requested_modules: FxHashMap<Atom<'a>, Vec<'a, RequestedModule>>,
|
||||
|
||||
/// `[[ImportEntries]]`
|
||||
///
|
||||
/// A List of ImportEntry records derived from the code of this module
|
||||
pub import_entries: Vec<ImportEntry>,
|
||||
pub import_entries: Vec<'a, ImportEntry<'a>>,
|
||||
|
||||
/// `[[LocalExportEntries]]`
|
||||
///
|
||||
/// A List of [`ExportEntry`] records derived from the code of this module
|
||||
/// that correspond to declarations that occur within the module
|
||||
pub local_export_entries: Vec<ExportEntry>,
|
||||
pub local_export_entries: Vec<'a, ExportEntry<'a>>,
|
||||
|
||||
/// `[[IndirectExportEntries]]`
|
||||
///
|
||||
/// A List of [`ExportEntry`] records derived from the code of this module
|
||||
/// that correspond to reexported imports that occur within the module
|
||||
/// or exports from `export * as namespace` declarations.
|
||||
pub indirect_export_entries: Vec<ExportEntry>,
|
||||
pub indirect_export_entries: Vec<'a, ExportEntry<'a>>,
|
||||
|
||||
/// `[[StarExportEntries]]`
|
||||
///
|
||||
/// A List of [`ExportEntry`] records derived from the code of this module
|
||||
/// that correspond to `export *` declarations that occur within the module,
|
||||
/// not including `export * as namespace` declarations.
|
||||
pub star_export_entries: Vec<ExportEntry>,
|
||||
pub star_export_entries: Vec<'a, ExportEntry<'a>>,
|
||||
|
||||
/// Local exported bindings
|
||||
pub exported_bindings: FxHashMap<CompactStr, Span>,
|
||||
pub exported_bindings: FxHashMap<Atom<'a>, Span>,
|
||||
|
||||
/// Local duplicated exported bindings, for diagnostics
|
||||
pub exported_bindings_duplicated: Vec<NameSpan>,
|
||||
pub exported_bindings_duplicated: Vec<'a, NameSpan<'a>>,
|
||||
|
||||
/// Reexported bindings from `export * from 'specifier'`
|
||||
/// Keyed by resolved path
|
||||
pub exported_bindings_from_star_export: FxDashMap<PathBuf, Vec<CompactStr>>,
|
||||
pub exported_bindings_from_star_export: FxHashMap<Atom<'a>, Vec<'a, Atom<'a>>>,
|
||||
|
||||
/// `export default name`
|
||||
/// ^^^^^^^ span
|
||||
pub export_default: Option<Span>,
|
||||
|
||||
/// Duplicated span of `export default` for diagnostics
|
||||
pub export_default_duplicated: Vec<Span>,
|
||||
pub export_default_duplicated: Vec<'a, Span>,
|
||||
}
|
||||
|
||||
impl ModuleRecord {
|
||||
pub fn new(resolved_absolute_path: PathBuf) -> Self {
|
||||
Self { resolved_absolute_path, ..Self::default() }
|
||||
impl<'a> ModuleRecord<'a> {
|
||||
pub fn new(allocator: &'a Allocator) -> Self {
|
||||
Self {
|
||||
not_esm: true,
|
||||
requested_modules: FxHashMap::default(),
|
||||
import_entries: Vec::new_in(allocator),
|
||||
local_export_entries: Vec::new_in(allocator),
|
||||
indirect_export_entries: Vec::new_in(allocator),
|
||||
star_export_entries: Vec::new_in(allocator),
|
||||
exported_bindings: FxHashMap::default(),
|
||||
exported_bindings_duplicated: Vec::new_in(allocator),
|
||||
exported_bindings_from_star_export: FxHashMap::default(),
|
||||
export_default: None,
|
||||
export_default_duplicated: Vec::new_in(allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ModuleRecord {
|
||||
impl fmt::Debug for ModuleRecord<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// recursively formatting loaded modules can crash when the module graph is cyclic
|
||||
let loaded_modules = self
|
||||
.loaded_modules
|
||||
.iter()
|
||||
.map(|entry| (entry.key().to_string()))
|
||||
.reduce(|acc, key| format!("{acc}, {key}"))
|
||||
.unwrap_or_default();
|
||||
let loaded_modules = format!("{{ {loaded_modules} }}");
|
||||
f.debug_struct("ModuleRecord")
|
||||
.field("not_esm", &self.not_esm)
|
||||
.field("resolved_absolute_path", &self.resolved_absolute_path)
|
||||
.field("requested_modules", &self.requested_modules)
|
||||
.field("loaded_modules", &loaded_modules)
|
||||
.field("import_entries", &self.import_entries)
|
||||
.field("local_export_entries", &self.local_export_entries)
|
||||
.field("indirect_export_entries", &self.indirect_export_entries)
|
||||
|
|
@ -123,23 +109,15 @@ impl fmt::Debug for ModuleRecord {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NameSpan {
|
||||
name: CompactStr,
|
||||
span: Span,
|
||||
pub struct NameSpan<'a> {
|
||||
pub name: Atom<'a>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl NameSpan {
|
||||
pub fn new(name: CompactStr, span: Span) -> Self {
|
||||
impl<'a> NameSpan<'a> {
|
||||
pub fn new(name: Atom<'a>, span: Span) -> Self {
|
||||
Self { name, span }
|
||||
}
|
||||
|
||||
pub const fn name(&self) -> &CompactStr {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub const fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ImportEntry`](https://tc39.es/ecma262/#importentry-record)
|
||||
|
|
@ -158,7 +136,7 @@ impl NameSpan {
|
|||
/// import * as ns from "mod";
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportEntry {
|
||||
pub struct ImportEntry<'a> {
|
||||
/// String value of the ModuleSpecifier of the ImportDeclaration.
|
||||
///
|
||||
/// ## Examples
|
||||
|
|
@ -167,7 +145,7 @@ pub struct ImportEntry {
|
|||
/// import { foo } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub module_request: NameSpan,
|
||||
pub module_request: NameSpan<'a>,
|
||||
|
||||
/// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`.
|
||||
///
|
||||
|
|
@ -179,7 +157,7 @@ pub struct ImportEntry {
|
|||
/// import { foo as bar } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub import_name: ImportImportName,
|
||||
pub import_name: ImportImportName<'a>,
|
||||
|
||||
/// The name that is used to locally access the imported value from within the importing module.
|
||||
///
|
||||
|
|
@ -191,7 +169,7 @@ pub struct ImportEntry {
|
|||
/// import { foo as bar } from "mod";
|
||||
/// // ^^^
|
||||
/// ```
|
||||
pub local_name: NameSpan,
|
||||
pub local_name: NameSpan<'a>,
|
||||
|
||||
/// Whether this binding is for a TypeScript type-only import. This is a non-standard field.
|
||||
/// When creating a [`ModuleRecord`] for a JavaScript file, this will always be false.
|
||||
|
|
@ -214,13 +192,13 @@ pub struct ImportEntry {
|
|||
|
||||
/// `ImportName` For `ImportEntry`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ImportImportName {
|
||||
Name(NameSpan),
|
||||
pub enum ImportImportName<'a> {
|
||||
Name(NameSpan<'a>),
|
||||
NamespaceObject,
|
||||
Default(Span),
|
||||
}
|
||||
|
||||
impl ImportImportName {
|
||||
impl ImportImportName<'_> {
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
}
|
||||
|
|
@ -250,32 +228,32 @@ impl ImportImportName {
|
|||
///
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct ExportEntry {
|
||||
pub struct ExportEntry<'a> {
|
||||
/// Span for the entire export entry
|
||||
pub span: Span,
|
||||
|
||||
/// The String value of the ModuleSpecifier of the ExportDeclaration.
|
||||
/// null if the ExportDeclaration does not have a ModuleSpecifier.
|
||||
pub module_request: Option<NameSpan>,
|
||||
pub module_request: Option<NameSpan<'a>>,
|
||||
|
||||
/// The name under which the desired binding is exported by the module identified by `[[ModuleRequest]]`.
|
||||
/// null if the ExportDeclaration does not have a ModuleSpecifier.
|
||||
/// "all" is used for `export * as ns from "mod"`` declarations.
|
||||
/// "all-but-default" is used for `export * from "mod" declarations`.
|
||||
pub import_name: ExportImportName,
|
||||
pub import_name: ExportImportName<'a>,
|
||||
|
||||
/// The name used to export this binding by this module.
|
||||
pub export_name: ExportExportName,
|
||||
pub export_name: ExportExportName<'a>,
|
||||
|
||||
/// The name that is used to locally access the exported value from within the importing module.
|
||||
/// null if the exported value is not locally accessible from within the module.
|
||||
pub local_name: ExportLocalName,
|
||||
pub local_name: ExportLocalName<'a>,
|
||||
}
|
||||
|
||||
/// `ImportName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportImportName {
|
||||
Name(NameSpan),
|
||||
pub enum ExportImportName<'a> {
|
||||
Name(NameSpan<'a>),
|
||||
/// all is used for export * as ns from "mod" declarations.
|
||||
All,
|
||||
/// all-but-default is used for export * from "mod" declarations.
|
||||
|
|
@ -285,7 +263,7 @@ pub enum ExportImportName {
|
|||
Null,
|
||||
}
|
||||
|
||||
impl ExportImportName {
|
||||
impl ExportImportName<'_> {
|
||||
pub fn is_all(&self) -> bool {
|
||||
matches!(self, Self::All)
|
||||
}
|
||||
|
|
@ -297,14 +275,14 @@ impl ExportImportName {
|
|||
|
||||
/// `ExportName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportExportName {
|
||||
Name(NameSpan),
|
||||
pub enum ExportExportName<'a> {
|
||||
Name(NameSpan<'a>),
|
||||
Default(Span),
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
impl ExportExportName {
|
||||
impl ExportExportName<'_> {
|
||||
/// Returns `true` if this is [`ExportExportName::Default`].
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
|
|
@ -318,7 +296,7 @@ impl ExportExportName {
|
|||
/// Attempt to get the [`Span`] of this export name.
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::Name(name) => Some(name.span()),
|
||||
Self::Name(name) => Some(name.span),
|
||||
Self::Default(span) => Some(*span),
|
||||
Self::Null => None,
|
||||
}
|
||||
|
|
@ -327,15 +305,15 @@ impl ExportExportName {
|
|||
|
||||
/// `LocalName` for `ExportEntry`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum ExportLocalName {
|
||||
Name(NameSpan),
|
||||
pub enum ExportLocalName<'a> {
|
||||
Name(NameSpan<'a>),
|
||||
/// `export default name_span`
|
||||
Default(NameSpan),
|
||||
Default(NameSpan<'a>),
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
impl ExportLocalName {
|
||||
impl<'a> ExportLocalName<'a> {
|
||||
/// `true` if this is a [`ExportLocalName::Default`].
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, Self::Default(_))
|
||||
|
|
@ -347,19 +325,15 @@ impl ExportLocalName {
|
|||
}
|
||||
|
||||
/// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`].
|
||||
pub const fn name(&self) -> Option<&CompactStr> {
|
||||
pub fn name(&self) -> Option<&Atom<'a>> {
|
||||
match self {
|
||||
Self::Name(name) | Self::Default(name) => Some(name.name()),
|
||||
Self::Name(name) | Self::Default(name) => Some(&name.name),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionMeta {
|
||||
pub deprecated: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RequestedModule {
|
||||
span: Span,
|
||||
is_type: bool,
|
||||
|
|
|
|||
|
|
@ -21,11 +21,10 @@ use oxc::{
|
|||
ScopeFlags, ScopeId, ScopeTree, SemanticBuilder, SymbolTable,
|
||||
},
|
||||
span::SourceType,
|
||||
syntax::module_record::ModuleRecord,
|
||||
transformer::{TransformOptions, Transformer},
|
||||
};
|
||||
use oxc_index::Idx;
|
||||
use oxc_linter::Linter;
|
||||
use oxc_linter::{Linter, ModuleRecord};
|
||||
use oxc_prettier::{Prettier, PrettierOptions};
|
||||
use serde::Serialize;
|
||||
use tsify::Tsify;
|
||||
|
|
@ -229,7 +228,7 @@ impl Oxc {
|
|||
);
|
||||
}
|
||||
|
||||
let module_record = Arc::new(module_record);
|
||||
let module_record = Arc::new(ModuleRecord::new(&path, &module_record));
|
||||
self.run_linter(&run_options, &path, &program, &module_record);
|
||||
|
||||
self.run_prettier(&run_options, source_text, source_type);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{env, path::Path, rc::Rc, sync::Arc};
|
|||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use oxc_linter::{FixKind, LinterBuilder};
|
||||
use oxc_linter::{FixKind, LinterBuilder, ModuleRecord};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
|
|
@ -39,7 +39,7 @@ fn bench_linter(criterion: &mut Criterion) {
|
|||
.build(&ret.program);
|
||||
let linter = LinterBuilder::all().with_fix(FixKind::All).build();
|
||||
let semantic = Rc::new(semantic_ret.semantic);
|
||||
let module_record = Arc::new(ret.module_record);
|
||||
let module_record = Arc::new(ModuleRecord::new(path, &ret.module_record));
|
||||
b.iter(|| linter.run(path, Rc::clone(&semantic), Arc::clone(&module_record)));
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue