diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd30260b2cea3..2d58bdcf63005 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: RUSTFLAGS: "--cfg tokio_unstable -C target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128 -C link-args=--max-memory=67108864" CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --dir=${{ github.workspace }}::${{ github.workspace }} --" # Insta is not able to run on wasmtime, omit the packages that depend on it - TEST_FLAGS: "-p oxc_ast -p oxc_cfg -p oxc_regular_expression -p oxc_module_lexer -- --nocapture" + TEST_FLAGS: "-p oxc_ast -p oxc_cfg -p oxc_regular_expression -- --nocapture" steps: - uses: taiki-e/checkout-action@v1 - uses: Boshen/setup-rust@main diff --git a/Cargo.lock b/Cargo.lock index 700d0119a275e..62daefb644aeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,17 +1798,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "oxc_module_lexer" -version = "0.38.0" -dependencies = [ - "oxc_allocator", - "oxc_ast", - "oxc_ecmascript", - "oxc_parser", - "oxc_span", -] - [[package]] name = "oxc_parser" version = "0.38.0" @@ -1840,7 +1829,6 @@ dependencies = [ "napi-build", "napi-derive", "oxc", - "oxc_module_lexer", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 2d05e519de9ef..4e2b3288fea1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,7 +91,6 @@ oxc_estree = { version = "0.38.0", path = "crates/oxc_estree" } oxc_isolated_declarations = { version = "0.38.0", path = "crates/oxc_isolated_declarations" } oxc_mangler = { version = "0.38.0", path = "crates/oxc_mangler" } oxc_minifier = { version = "0.38.0", path = "crates/oxc_minifier" } -oxc_module_lexer = { version = "0.38.0", path = "crates/oxc_module_lexer" } oxc_parser = { version = "0.38.0", path = "crates/oxc_parser" } oxc_regular_expression = { version = "0.38.0", path = "crates/oxc_regular_expression" } oxc_semantic = { version = "0.38.0", path = "crates/oxc_semantic" } diff --git a/crates/oxc_module_lexer/CHANGELOG.md b/crates/oxc_module_lexer/CHANGELOG.md deleted file mode 100644 index e4ff71aba877f..0000000000000 --- a/crates/oxc_module_lexer/CHANGELOG.md +++ /dev/null @@ -1,67 +0,0 @@ -# Changelog - -All notable changes to this package will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. - -## [0.35.0] - 2024-11-04 - -### Bug Fixes - -- 9ed9501 module_lexer: Add missing `export * from 'foo';` case (#7103) (Boshen) - -### Testing - -- 64e2499 module_lexer: Use a single integration test for faster compilation (#7097) (Boshen) - -## [0.34.0] - 2024-10-26 - -### Refactor - -- 423d54c rust: Remove the annoying `clippy::wildcard_imports` (#6860) (Boshen) - -## [0.31.0] - 2024-10-08 - -### Features - -- 9e62396 syntax_operations: Add crate `oxc_ecmascript` (#6202) (Boshen) - -## [0.28.0] - 2024-09-11 - -- 4a8aec1 span: [**BREAKING**] Change `SourceType::js` to `SourceType::cjs` and `SourceType::mjs` (#5606) (Boshen) - -### Features - - -## [0.26.0] - 2024-09-03 - -### Features - -- d22bd20 module_lexer: Distinguish for types-only imports and exports (#5184) (Kevin Deng 三咲智子) - -## [0.16.0] - 2024-06-26 - -- 4456034 ast: [**BREAKING**] Add `IdentifierReference` to `ExportSpecifier` (#3820) (Boshen) - -### Features - - -## [0.13.2] - 2024-06-03 - -### Bug Fixes - -- 350cd91 parser: Should parser error when function declaration has no name (#3461) (Dunqing) - -## [0.13.0] - 2024-05-14 - -### Refactor - -- 7e1fe36 ast: Squash nested enums (#3115) (overlookmotel) - -## [0.11.0] - 2024-03-30 - -### Refactor - -- fc38783 ast: Add walk_mut functions (#2776) (Ali Rezvani) -- 198eea0 ast: Add walk functions to Visit trait. (#2791) (Ali Rezvani) - diff --git a/crates/oxc_module_lexer/Cargo.toml b/crates/oxc_module_lexer/Cargo.toml deleted file mode 100644 index 823d4f4d72cfb..0000000000000 --- a/crates/oxc_module_lexer/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "oxc_module_lexer" -version = "0.38.0" -authors.workspace = true -categories.workspace = true -edition.workspace = true -homepage.workspace = true -include = ["/examples", "/src"] -keywords.workspace = true -license.workspace = true -publish = true -repository.workspace = true -rust-version.workspace = true -description.workspace = true - -[lints] -workspace = true - -[lib] -test = false -doctest = false - -[dependencies] -oxc_ast = { workspace = true } -oxc_ecmascript = { workspace = true } -oxc_span = { workspace = true } - -[dev-dependencies] -oxc_allocator = { workspace = true } -oxc_parser = { workspace = true } diff --git a/crates/oxc_module_lexer/README.md b/crates/oxc_module_lexer/README.md deleted file mode 100644 index 0159482e9f8a7..0000000000000 --- a/crates/oxc_module_lexer/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Oxc Module Lexer - -This is not a lexer. The name "lexer" is used for easier recognition. - -## [es-module-lexer](https://github.com/guybedford/es-module-lexer) - -Outputs the list of exports and locations of import specifiers, including dynamic import and import meta handling. - -Does not have any [limitations](https://github.com/guybedford/es-module-lexer?tab=readme-ov-file#limitations) mentioned in `es-module-lexer`. - -- [ ] get imported variables https://github.com/guybedford/es-module-lexer/issues/163 -- [ ] track star exports as imports as well https://github.com/guybedford/es-module-lexer/issues/76 -- [ ] TypeScript specific syntax -- [ ] TypeScript `type` import / export keyword - -## [cjs-module-lexer](https://github.com/nodejs/cjs-module-lexer) - -- [ ] TODO - -## Benchmark - -This is 2 times slower than `es-module-lexer`, but will be significantly faster when TypeScript is processed. - -The difference is around 10ms vs 20ms on a large file (700k). diff --git a/crates/oxc_module_lexer/examples/module_lexer.rs b/crates/oxc_module_lexer/examples/module_lexer.rs deleted file mode 100644 index 0cdac3514e08b..0000000000000 --- a/crates/oxc_module_lexer/examples/module_lexer.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![allow(clippy::print_stdout)] -use std::{env, path::Path}; - -use oxc_allocator::Allocator; -use oxc_module_lexer::ModuleLexer; -use oxc_parser::Parser; -use oxc_span::SourceType; - -// Instruction: -// * create a `test.js` -// * `just example module_lexer - -fn main() -> Result<(), String> { - let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); - let path = Path::new(&name); - let source_text = std::fs::read_to_string(path).map_err(|_| format!("Missing '{name}'"))?; - let allocator = Allocator::default(); - let source_type = SourceType::from_path(path).unwrap(); - let ret = Parser::new(&allocator, &source_text, source_type).parse(); - - println!("source:"); - println!("{source_text}"); - - for error in ret.errors { - let error = error.with_source_code(source_text.clone()); - println!("{error:?}"); - println!("Parsed with Errors."); - } - - let ModuleLexer { imports, exports, facade, has_module_syntax } = - ModuleLexer::new().build(&ret.program); - - println!("\nimports:"); - for import in imports { - println!("{import:?}"); - } - - println!("\nexports:"); - for export in exports { - println!("{export:?}"); - } - - println!("\nfacade: {facade}"); - println!("has_module_syntax {has_module_syntax}"); - - Ok(()) -} diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs deleted file mode 100644 index 2bd25070692d1..0000000000000 --- a/crates/oxc_module_lexer/src/lib.rs +++ /dev/null @@ -1,331 +0,0 @@ -//! ESM module lexer -//! -//! * - -use oxc_ast::{ast::*, visit::walk, Visit}; -use oxc_ecmascript::BoundNames; -use oxc_span::{Atom, GetSpan}; - -#[derive(Debug, Clone)] -pub struct ImportSpecifier<'a> { - /// Module name - /// - /// To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. - /// - /// For dynamic import expressions, this field will be empty if not a valid JS string. - pub n: Option>, - - /// Start of module specifier - pub s: u32, - - /// End of module specifier - pub e: u32, - - /// Start of import statement - pub ss: u32, - - /// End of import statement - pub se: u32, - - /// Dynamic import / Static import / `import.meta` - pub d: ImportType, - - /// If this import has an import assertion, this is the start value - pub a: Option, - - /// If this import is for types only - pub t: bool, -} - -#[derive(Debug, Clone)] -pub struct ExportSpecifier<'a> { - /// Exported name - pub n: Atom<'a>, - - /// Local name, or undefined. - pub ln: Option>, - - /// Start of exported name - pub s: u32, - - /// End of exported name - pub e: u32, - - /// Start of local name - pub ls: Option, - - /// End of local name - pub le: Option, - - /// If this export is for types only - pub t: bool, -} - -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] -pub enum ImportType { - /// If this import keyword is a static import - #[default] - StaticImport, - /// If this import is an `export *` - ExportStar, - /// If this import keyword is a dynamic import, this is the start value. - DynamicImport(u32), - /// If this import keyword is an import.meta expression - ImportMeta, -} - -impl ImportType { - pub fn as_dynamic_import(&self) -> Option { - match self { - Self::DynamicImport(start) => Some(*start), - Self::StaticImport | Self::ExportStar | Self::ImportMeta => None, - } - } -} - -pub struct ModuleLexer<'a> { - pub imports: Vec>, - - pub exports: Vec>, - - /// ESM syntax detection - /// - /// The use of ESM syntax: import / export statements and `import.meta` - pub has_module_syntax: bool, - - /// Facade modules that only use import / export syntax - pub facade: bool, -} - -impl Default for ModuleLexer<'_> { - fn default() -> Self { - Self::new() - } -} - -impl<'a> ModuleLexer<'a> { - #[must_use] - pub fn new() -> Self { - Self { imports: vec![], exports: vec![], has_module_syntax: false, facade: true } - } - - #[must_use] - pub fn build(mut self, program: &Program<'a>) -> Self { - self.visit_program(program); - self - } -} - -impl<'a> Visit<'a> for ModuleLexer<'a> { - fn visit_statement(&mut self, stmt: &Statement<'a>) { - if self.facade && !stmt.is_module_declaration() && !stmt.is_declaration() { - self.facade = false; - } - walk::walk_statement(self, stmt); - } - - fn visit_module_declaration(&mut self, decl: &ModuleDeclaration<'a>) { - if !self.has_module_syntax { - self.has_module_syntax = true; - } - walk::walk_module_declaration(self, decl); - } - - // import.meta - fn visit_meta_property(&mut self, prop: &MetaProperty<'a>) { - if !self.has_module_syntax { - self.has_module_syntax = true; - } - if prop.meta.name == "import" && prop.property.name == "meta" { - self.imports.push(ImportSpecifier { - n: None, - s: prop.span.start, - e: prop.span.end, - ss: prop.span.start, - se: prop.span.end, - d: ImportType::ImportMeta, - a: None, - t: false, - }); - } - walk::walk_meta_property(self, prop); - } - - // import("foo") - fn visit_import_expression(&mut self, expr: &ImportExpression<'a>) { - let (source, source_span_start, source_span_end) = - if let Expression::StringLiteral(s) = &expr.source { - (Some(s.value.clone()), s.span.start, s.span.end) - } else { - let span = expr.source.span(); - (None, span.start, span.end) - }; - self.imports.push(ImportSpecifier { - n: source, - s: source_span_start, - e: source_span_end, - ss: expr.span.start, - se: expr.span.end, - d: ImportType::DynamicImport(expr.span.start + 6), - a: expr.arguments.first().map(|e| e.span().start), - t: false, - }); - walk::walk_import_expression(self, expr); - } - - fn visit_ts_import_type(&mut self, impt: &TSImportType<'a>) { - let (source, source_span) = match &impt.parameter { - TSType::TSLiteralType(literal_type) => match &literal_type.literal { - TSLiteral::StringLiteral(s) => (Some(s.value.clone()), s.span()), - _ => (None, literal_type.span()), - }, - _ => (None, impt.parameter.span()), - }; - - self.imports.push(ImportSpecifier { - n: source, - s: source_span.start, - e: source_span.end, - ss: impt.span.start, - se: impt.span.end, - d: ImportType::DynamicImport(impt.span.start + 6), - a: None, - t: true, - }); - } - - fn visit_import_declaration(&mut self, decl: &ImportDeclaration<'a>) { - let assertions = decl - .with_clause - .as_ref() - .filter(|c| c.with_entries.first().is_some_and(|a| a.key.as_atom() == "type")) - .map(|c| c.span.start); - self.imports.push(ImportSpecifier { - n: Some(decl.source.value.clone()), - s: decl.source.span.start + 1, // +- 1 for removing string quotes - e: decl.source.span.end - 1, - ss: decl.span.start, - se: decl.span.end, - d: ImportType::StaticImport, - a: assertions, - t: decl.import_kind.is_type(), - }); - walk::walk_import_declaration(self, decl); - } - - fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) { - if let Some(source) = &decl.source { - // export { named } from 'foo' - self.imports.push(ImportSpecifier { - n: Some(source.value.clone()), - s: source.span.start + 1, - e: source.span.end - 1, - ss: decl.span.start, - se: decl.span.end, - d: ImportType::StaticImport, - a: None, - t: decl.export_kind.is_type(), - }); - } - - // export const/let/var/function/class ... - if let Some(decl) = &decl.declaration { - if self.facade { - self.facade = false; - } - decl.bound_names(&mut |ident| { - self.exports.push(ExportSpecifier { - n: ident.name.clone(), - ln: Some(ident.name.clone()), - s: ident.span.start, - e: ident.span.end, - ls: None, - le: None, - t: false, - }); - }); - } - - // export { named } - self.exports.extend(decl.specifiers.iter().map(|s| { - let (exported_start, exported_end) = match &s.exported { - ModuleExportName::IdentifierName(ident) => (ident.span.start, ident.span.end), - ModuleExportName::IdentifierReference(ident) => (ident.span.start, ident.span.end), - // +1 -1 to remove the string quotes - ModuleExportName::StringLiteral(s) => (s.span.start + 1, s.span.end - 1), - }; - ExportSpecifier { - n: s.exported.name().clone(), - ln: decl.source.is_none().then(|| s.local.name().clone()), - s: exported_start, - e: exported_end, - ls: Some(s.local.span().start), - le: Some(s.local.span().end), - t: decl.export_kind.is_type(), - } - })); - walk::walk_export_named_declaration(self, decl); - } - - // export default foo - fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration<'a>) { - if self.facade { - self.facade = false; - } - let ln = match &decl.declaration { - ExportDefaultDeclarationKind::FunctionDeclaration(func) => func.id.as_ref(), - ExportDefaultDeclarationKind::ClassDeclaration(class) => class.id.as_ref(), - _ => None, - }; - self.exports.push(ExportSpecifier { - n: decl.exported.name().clone(), - ln: ln.map(|id| id.name.clone()), - s: decl.exported.span().start, - e: decl.exported.span().end, - ls: None, - le: None, - t: false, - }); - } - - fn visit_export_all_declaration(&mut self, decl: &ExportAllDeclaration<'a>) { - // export * as ns from 'foo' - if let Some(exported) = &decl.exported { - let n = exported.name().clone(); - let s = exported.span().start; - let e = exported.span().end; - self.exports.push(ExportSpecifier { - n: n.clone(), - ln: None, - s, - e, - ls: None, - le: None, - t: decl.export_kind.is_type(), - }); - self.imports.push(ImportSpecifier { - n: Some(n), - s, - e, - ss: decl.span.start, - se: decl.span.end, - d: ImportType::StaticImport, - a: None, - t: decl.export_kind.is_type(), - }); - } else { - // export * from 'foo' - self.imports.push(ImportSpecifier { - n: Some(decl.source.value.clone()), - s: decl.source.span.start + 1, // +- 1 for removing string quotes - e: decl.source.span.end - 1, - ss: decl.span.start, - se: decl.span.end, - d: ImportType::ExportStar, - a: None, - t: decl.export_kind.is_type(), - }); - } - walk::walk_export_all_declaration(self, decl); - } -} diff --git a/crates/oxc_module_lexer/tests/integration/esm.rs b/crates/oxc_module_lexer/tests/integration/esm.rs deleted file mode 100644 index 618d0410e21c1..0000000000000 --- a/crates/oxc_module_lexer/tests/integration/esm.rs +++ /dev/null @@ -1,1597 +0,0 @@ -//! - -use oxc_allocator::Allocator; -use oxc_module_lexer::ImportType; -use oxc_parser::Parser; -use oxc_span::SourceType; - -use super::{parse, ExportSpecifier, ModuleLexer}; - -trait Slice { - fn slice(&self, start: u32, end: u32) -> &'static str; -} - -impl Slice for &'static str { - fn slice(&self, start: u32, end: u32) -> &'static str { - &self[start as usize..end as usize] - } -} - -#[allow(clippy::needless_pass_by_value, clippy::similar_names)] -fn assert_export_is( - source: &str, - actual: &ExportSpecifier, - expected_n: &str, - expected_ln: Option<&str>, -) { - // Commented out because: - // * there are no tests hitting the true branch - // * `&source[s..e]` is a Rust string, `expected.n.as_str()` is a escaped JavaScript string, - // which will never be cause for escaped strings. - // let s = actual.s as usize; - // let e = actual.e as usize; - // if matches!(&source[s..s], "\"" | "'") { - // assert_eq!(&source[s..s], &source[e - 1..e - 1]); - // } else { - // assert_eq!(&source[s..e], &expected.n.as_str()); - // } - - let ls = actual.ls; - let le = actual.le; - if let Some(expected_ln) = expected_ln { - if expected_ln.is_empty() { - assert_eq!(ls, None); - assert_eq!(le, None); - } else if let Some(ls) = ls { - let ls = ls as usize; - let le = le.unwrap() as usize; - // Commented out because "true" branch never got him. - // if matches!(&source[ls..ls], "\"" | "'") { - // assert_eq!(&source[ls..ls], &source[le - 1..le - 1]); - // } else { - assert_eq!(&source[ls..le], expected_ln); - // } - } - } - assert_eq!(actual.n, expected_n, "n"); - assert_eq!(actual.ln.as_deref(), expected_ln, "ln"); -} - -/* Added by Oxc */ - -#[test] -fn named_imports() { - let source = "import { a, b, c } from 'foo'"; - let imports = &parse(source).imports; - assert_eq!(imports.len(), 1); - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), source); - assert_eq!(source.slice(impt.s, impt.e), "foo"); -} - -#[test] -fn export_star_from() { - let source = "export * from 'foo'"; - let imports = &parse(source).imports; - assert_eq!(imports.len(), 1); - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), source); - assert_eq!(source.slice(impt.s, impt.e), "foo"); - assert_eq!(impt.n.as_deref(), Some("foo")); - assert_eq!(impt.d, ImportType::ExportStar); -} - -/* Suite Lexer */ - -#[test] -fn dynamic_import_expression_range() { - let source = r#"import(("asdf"))"#; - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), r#"import(("asdf"))"#); - assert_eq!(source.slice(impt.s, impt.e), r#"("asdf")"#); -} - -#[test] -fn dynamic_import_expression_range_2() { - let source = r"import(/* comment */ `asdf` /* comment */)"; - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), r"import(/* comment */ `asdf` /* comment */)"); - assert_eq!(source.slice(impt.s, impt.e), r"`asdf`"); -} - -#[test] -fn dynamic_import_expression_range_3() { - let source = "import(`asdf` // comment\n)"; - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), "import(`asdf` // comment\n)"); - assert_eq!(source.slice(impt.s, impt.e), "`asdf`"); -} - -#[test] -fn dynamic_import_expression_range_4() { - let source = "import(\"foo\" + /* comment */ \"bar\")"; - let impt = &parse(source).imports[0]; - assert_eq!(source.slice(impt.ss, impt.se), "import(\"foo\" + /* comment */ \"bar\")"); - assert_eq!(source.slice(impt.s, impt.e), "\"foo\" + /* comment */ \"bar\""); -} - -#[test] -fn dynamic_import_expression_range_5() { - let source = "import((() => { return \"foo\" })() /* comment */)"; - let impt = &parse(source).imports[0]; - assert_eq!( - source.slice(impt.ss, impt.se), - "import((() => { return \"foo\" })() /* comment */)" - ); - assert_eq!(source.slice(impt.s, impt.e), "(() => { return \"foo\" })()"); -} - -#[test] -fn dynamic_import_expression_range_6() { - let source = "import(/* comment */ `asdf` /* comment */ /* comment 2 */)"; - let impt = &parse(source).imports[0]; - assert_eq!( - source.slice(impt.ss, impt.se), - "import(/* comment */ `asdf` /* comment */ /* comment 2 */)" - ); - assert_eq!(source.slice(impt.s, impt.e), "`asdf`"); -} - -#[test] -fn simple_export_destructuring() { - let source = " - export const{URI,Utils,...Another}=LIB - export var p, { z } = {}; - - export var { aa, qq: { z } } = { qq: {} }, pp = {}; - "; - let exports = parse(source).exports; - assert_eq!( - exports.iter().map(|e| e.n.clone()).collect::>(), - // NOTE: esm-module-lexer does not have "Another", "z", and "pp", and has an extra "qq" - // vec!["URI", "Utils", "p", "aa", "qq"] - vec!["URI", "Utils", "Another", "p", "z", "aa", "z", "pp"] - ); -} - -#[test] -fn export_default_cases() { - let source = " - export default \"export default a\" - export default \"export default 'a'\" - export default \"export function foo() {}\" - export default \"export function foo() {return bar}\" - "; - let exports = parse(source).exports; - assert_eq!( - exports.iter().map(|expt| expt.n.clone()).collect::>(), - vec!["default", "default", "default", "default"] - ); -} - -#[test] -fn import_meta_spread() { - let source = "console.log(...import.meta.obj);"; - let impts = parse(source).imports; - assert_eq!(impts.len(), 1); - assert_eq!(source.slice(impts[0].s, impts[0].e), "import.meta"); -} - -#[test] -fn template_string_default_bracket() { - let source = "export default{};"; - let expts = &parse(source).exports; - let expt = &expts[0]; - assert_eq!(source.slice(expt.s, expt.e), "default"); - assert_eq!(expt.ls, None); - assert_eq!(expt.le, None); - assert_eq!(expt.n, "default"); - assert_eq!(expt.ln, None); -} - -#[test] -fn template_string_default() { - let source = "const css = String.raw; -export default css`:host { solid 1px black }`;"; - let expts = &parse(source).exports; - let expt = &expts[0]; - assert_eq!(source.slice(expt.s, expt.e), "default"); - assert_eq!(expt.ls, None); - assert_eq!(expt.le, None); - assert_eq!(expt.n, "default"); - assert_eq!(expt.ln, None); -} - -#[test] -fn class_fn_asi() { - parse("class a{friendlyName;import}n();"); -} - -#[test] -fn division_const_after_class_parse_case() { - let source = "class a{}const Ti=a/yi;"; - parse(source); -} - -#[test] -fn multiline_dynamic_import_on_windows() { - let source = "import(\n\"./statehash\\u{1011}.js\"\r)"; - let imports = parse(source).imports; - assert_eq!(imports.len(), 1); - assert_eq!(source.slice(imports[0].s, imports[0].e), "\"./statehash\\u{1011}.js\""); -} - -#[test] -fn basic_nested_dynamic_import_support() { - let source = "await import (await import ('foo'))"; - let imports = parse(source).imports; - assert_eq!(imports.len(), 2); - // We can't obtain the left of `(` - // assert_eq!(source.slice(imports[0].ss, imports[0].d.as_dynamic_import().unwrap()), "import "); - assert_eq!(source.slice(imports[0].ss, imports[0].se), "import (await import ('foo'))"); - assert_eq!(source.slice(imports[0].s, imports[0].e), "await import ('foo')"); - // We can't obtain the left of `(` - // assert_eq!(source.slice(imports[1].ss, imports[1].d.as_dynamic_import().unwrap()), "import "); - assert_eq!(source.slice(imports[1].ss, imports[1].se), "import ('foo')"); - assert_eq!(source.slice(imports[1].s, imports[1].e), "'foo'"); -} - -#[test] -fn import_assertions() { - let source = r#" -import json from "./foo.json" assert { type: "json" }; -import("foo.json", { assert: { type: "json" } }); - -import test from './asdf' assert { not: 'an assertion!' } -export var p = 5; -"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 3); - assert_eq!(source.slice(imports[0].s, imports[0].e), "./foo.json"); - assert_eq!(source.slice(imports[0].a.unwrap(), imports[0].se), "{ type: \"json\" };"); - assert_eq!( - source.slice(imports[1].a.unwrap(), imports[1].se), - "{ assert: { type: \"json\" } })" - ); - assert_eq!(source.slice(imports[1].s, imports[1].e), "\"foo.json\""); - assert_eq!(imports[1].n.as_ref().unwrap(), "foo.json"); - assert_eq!(imports[2].n.as_ref().unwrap(), "./asdf"); - assert_eq!(imports[2].a, None); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "p", Some("p")); -} - -#[test] -fn import_attributes() { - let source = " -import json from \"./foo.json\" with { type: \"json\" }; -import(\"foo.json\", { with: { type: \"json\" } }); - -import test from './asdf' -with { not: 'an assertion!' } -export var p = 5; -"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 3); - assert_eq!(source.slice(imports[0].s, imports[0].e), "./foo.json"); - assert_eq!(source.slice(imports[0].a.unwrap(), imports[0].se), "{ type: \"json\" };"); - assert_eq!(source.slice(imports[1].a.unwrap(), imports[1].se), "{ with: { type: \"json\" } })"); - assert_eq!(source.slice(imports[1].s, imports[1].e), "\"foo.json\""); - assert_eq!(imports[1].n.clone().unwrap(), "foo.json"); - assert_eq!(imports[2].n.clone().unwrap(), "./asdf"); - assert_eq!(imports[2].a, None); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "p", Some("p")); -} - -#[test] -fn import_meta_inside_dynamic_import() { - let source = "import(import.meta.url)"; - let imports = parse(source).imports; - assert_eq!(imports.len(), 2); - assert_eq!(source.slice(imports[0].s, imports[0].e), "import.meta.url"); -} - -#[test] -fn export() { - let source = "export var p=5"; - let exports = parse(source).exports; - assert_export_is(source, &exports[0], "p", Some("p")); -} - -#[test] -fn string_encoding() { - let imports = parse( - " - import './\\x61\\x62\\x63.js'; - import './\\u{20204}.js'; - import('./\\u{20204}.js'); - import('./\\u{20204}.js' + dyn); - import('./\\u{20204}.js' ); - import('./\\u{20204}.js' ()); - ", - ) - .imports; - assert_eq!(imports.len(), 6); - assert_eq!(imports[0].n.clone().unwrap(), "./abc.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./𠈄.js"); - assert_eq!(imports[2].n.clone().unwrap(), "./𠈄.js"); - assert_eq!(imports[3].n, None); - assert_eq!(imports[4].n.clone().unwrap(), "./𠈄.js"); - assert_eq!(imports[5].n, None); -} - -#[test] -fn regexp_case() { - parse( - " - class Number { - - } - - /(\"|')(?(\\\\(\\1)|[^\\1])*)?(\\1)/.exec(`'\\\\\"\\\\'aa'`); - - const x = `\"${label.replace(/\"/g, \"\\\\\\\"\")}\"`; - ", - ); -} - -#[test] -fn regexp_keyword_prefixes() { - let imports = parse( - " - x: while (true) { - if (foo) break - /import(\"a\")/.test(bar) || baz() - if (foo) continue - /import(\"b\")/.test(bar) || baz() - if (foo) break x - /import(\"c\")/.test(bar) || baz() - if (foo) continue x - /import(\"d\")/.test(bar) || baz() - } - ", - ) - .imports; - assert_eq!(imports.len(), 0); -} - -#[test] -fn regexp_division() { - parse("\nconst x = num / /'/.exec(l)[0].slice(1, -1)//'"); -} - -#[test] -fn multiline_string_escapes() { - parse( - "const str = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wAAAAAzJ3zzAAAGTElEQV\\\n\t\tRIx+VXe1BU1xn/zjn7ugvL4sIuQnll5U0ELAQxig7WiQYz6NRHa6O206qdSXXSxs60dTK200zNY9q0dcRpMs1jkrRNWmaijCVoaU';\n", - ); -} - -#[test] -fn dotted_number() { - parse( - " - const x = 5. / 10; - ", - ); -} - -#[test] -fn division_operator_case() { - parse(" - function log(r){ - if(g>=0){u[g++]=m;g>=n.logSz&&(g=0)}else{u.push(m);u.length>=n.logSz&&(g=0)}/^(DBG|TICK): /.test(r)||t.Ticker.tick(454,o.slice(0,200)); - } - - (function(n){ - })(); - "); -} - -#[test] -fn single_parse_cases() { - parse("export { x }"); - parse("'asdf'"); - parse("/asdf/"); - parse("`asdf`"); - parse("/**/\n"); - parse(" //"); -} - -#[test] -fn simple_export_with_unicode_conversions() { - let source = "export var p𓀀s,q"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 0); - assert_eq!(exports.len(), 2); - assert_export_is(source, &exports[0], "p𓀀s", Some("p𓀀s")); - assert_export_is(source, &exports[1], "q", Some("q")); -} - -#[test] -fn simple_import() { - let source = " - import test from \"test\"; - console.log(test); - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::StaticImport); - assert_eq!(import.n.clone().unwrap(), "test"); - assert_eq!(source.slice(import.ss, import.se), "import test from \"test\";"); - assert_eq!(exports.len(), 0); -} - -#[test] -fn empty_single_quote_string_import() { - let source = "import ''"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::StaticImport); - assert_eq!(source.slice(import.s, import.e), ""); - assert_eq!(source.slice(import.ss, import.se), "import ''"); - assert_eq!(exports.len(), 0); -} - -#[test] -fn empty_double_quote_string_import() { - let source = "import \"\""; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::StaticImport); - assert_eq!(source.slice(import.s, import.e), ""); - assert_eq!(source.slice(import.ss, import.se), "import \"\""); - assert_eq!(exports.len(), 0); -} - -#[test] -fn import_export_with_comments() { - let source = " - -import /* 'x' */ - 'a'; - -import /* 'x' */ - 'b'; - -export var z /* */ - export { - a, -// b, -/* c */ - d - }; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 2); - assert_eq!(source.slice(imports[0].s, imports[0].e), "a"); - assert_eq!(source.slice(imports[0].ss, imports[0].se), "import /* 'x' */\n 'a';"); - assert_eq!(source.slice(imports[1].s, imports[1].e), "b"); - assert_eq!(source.slice(imports[1].ss, imports[1].se), "import /* 'x' */\n 'b';"); - assert_eq!(exports.len(), 3); - assert_export_is(source, &exports[0], "z", Some("z")); - assert_export_is(source, &exports[1], "a", Some("a")); - assert_export_is(source, &exports[2], "d", Some("d")); -} - -#[test] -fn exported_function_and_class() { - let source = " - export function a𓀀 () { - - } - export class Q{ - - } - "; - let exports = parse(source).exports; - assert_eq!(exports.len(), 2); - assert_export_is(source, &exports[0], "a𓀀", Some("a𓀀")); - assert_export_is(source, &exports[1], "Q", Some("Q")); -} - -#[test] -fn export_destructuring() { - let source = " - export const { a, b } = foo; - - export { ok }; - "; - let exports = parse(source).exports; - assert_eq!(exports.len(), 3); - assert_export_is(source, &exports[0], "a", Some("a")); -} - -#[test] -fn minified_import_syntax() { - let source = r#"import{TemplateResult as t}from"lit-html";import{a as e}from"./chunk-4be41b30.js";export{j as SVGTemplateResult,i as TemplateResult,g as html,h as svg}from"./chunk-4be41b30.js";window.JSCompiler_renameProperty='asdf';"#; - let imports = parse(source).imports; - assert_eq!(imports.len(), 3); - assert_eq!(imports[0].s, 32); - assert_eq!(imports[0].e, 40); - assert_eq!(imports[0].ss, 0); - assert_eq!(imports[0].se, 42); - assert_eq!(imports[1].s, 61); - assert_eq!(imports[1].e, 80); - assert_eq!(imports[1].ss, 42); - assert_eq!(imports[1].se, 82); - assert_eq!(imports[2].s, 156); - assert_eq!(imports[2].e, 175); - assert_eq!(imports[2].ss, 82); - assert_eq!(imports[2].se, 177); -} - -#[test] -fn more_minified_imports() { - let source = r#"import"some/import.js";"#; - let imports = parse(source).imports; - assert_eq!(imports.len(), 1); - assert_eq!(imports[0].s, 7); - assert_eq!(imports[0].e, 21); - assert_eq!(imports[0].ss, 0); - assert_eq!(imports[0].se, 23); -} - -#[test] -fn plus_plus_division() { - parse( - "\ -tick++/fetti;f=(1)+\")\"; -", - ); -} - -#[test] -fn return_bracket_division() { - let source = "function variance(){return s/(a-1)}"; - parse(source); -} - -#[test] -fn simple_reexport() { - let source = r#"export { hello as default } from "test-dep";"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::StaticImport); - assert_eq!(source.slice(import.s, import.e), "test-dep"); - assert_eq!( - source.slice(import.ss, import.se), - "export { hello as default } from \"test-dep\";" - ); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "default", None); -} - -#[test] -fn import_meta() { - let source = r" - export var hello = 'world'; - console.log(import.meta.url); - "; - let imports = parse(source).imports; - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::ImportMeta); - assert_eq!(import.ss, 53); - assert_eq!(import.se, 64); - assert_eq!(source.slice(import.s, import.e), "import.meta"); -} - -#[test] -fn import_meta_edge_cases() { - let source = r" - // Import meta - import. - meta - // Not import meta - a. - import. - meta - "; - let imports = parse(source).imports; - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::ImportMeta); - assert_eq!(import.ss, 28); - assert_eq!(import.se, 47); - assert_eq!(source.slice(import.s, import.e), "import.\n meta"); -} - -#[test] -fn dynamic_import_method() { - let source = r" - class A { - import() { - } - } - "; - let imports = parse(source).imports; - assert_eq!(imports.len(), 0); -} - -#[test] -fn dynamic_import_edge_cases() { - let source = r" - ({ - // not a dynamic import! - import(not1) {} - }); - { - // is a dynamic import! - import(is1); - } - a. - // not a dynamic import! - import(not2); - a. - b() - // is a dynamic import! - import(is2); - - const myObject = { - import: ()=> import(some_url) - } - "; - let imports = parse(source).imports; - assert_eq!(imports.len(), 3); - let imp = &imports[0]; - assert_eq!(imp.ss + 6, imp.d.as_dynamic_import().unwrap()); - assert_eq!(imp.se, imp.e + 1); - assert_eq!(source.slice(imp.d.as_dynamic_import().unwrap(), imp.se), "(is1)"); - assert_eq!(source.slice(imp.s, imp.e), "is1"); - - let imp = &imports[1]; - assert_eq!(imp.ss + 6, imp.d.as_dynamic_import().unwrap()); - assert_eq!(imp.se, imp.e + 1); - assert_eq!(source.slice(imp.s, imp.e), "is2"); - - let imp = &imports[2]; - assert_eq!(imp.ss + 6, imp.d.as_dynamic_import().unwrap()); - assert_eq!(imp.se, imp.e + 1); - assert_eq!(source.slice(imp.s, imp.e), "some_url"); -} - -#[test] -fn import_after_code() { - let source = "\ -export function f () { -g(); -} - -import { g } from './test-circular2.js'; -"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - let import = &imports[0]; - assert_eq!(import.d, ImportType::StaticImport); - assert_eq!(source.slice(import.s, import.e), "./test-circular2.js"); - assert_eq!(source.slice(import.ss, import.se), "import { g } from './test-circular2.js';"); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "f", Some("f")); -} - -#[test] -fn comments() { - let source = " /*\n VERSION\n */\nimport util from 'util';\n\n//\nfunction x() {\n}\n\n/**/\n// '\n/* / */\n/*\n\n * export { b }\n\\*/\nexport { a }\n\n function d() {\n/***/\n }\n "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 1); - assert_eq!(source.slice(imports[0].s, imports[0].e), "util"); - assert_eq!(source.slice(imports[0].ss, imports[0].se), "import util from 'util';"); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "a", Some("a")); -} - -#[test] -fn strings() { - let source = r#" - ""; - ` - ${ - import(`test/${ import(b)}`) - } - ` - export { a } - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 2); - assert_ne!(imports[0].d, ImportType::StaticImport); - assert_eq!(imports[0].ss + 6, imports[0].d.as_dynamic_import().unwrap()); - assert_eq!(imports[0].se, imports[0].e + 1); - assert_eq!(source.slice(imports[0].ss, imports[0].s), "import("); - assert_ne!(imports[1].d, ImportType::StaticImport); - assert_eq!(imports[1].ss + 6, imports[1].d.as_dynamic_import().unwrap()); - assert_eq!(imports[1].se, imports[1].e + 1); - assert_eq!(source.slice(imports[1].ss, imports[1].d.as_dynamic_import().unwrap()), "import"); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "a", Some("a")); -} - -#[test] -fn bracket_matching() { - parse( - " - instance.extend('parseExprAtom', function (nextMethod) { - return function () { - function parseExprAtom(refDestructuringErrors) { - if (this.type === tt._import) { - return parseDynamicImport.call(this); - } - return c(refDestructuringErrors); - } - }(); - }); - export { a } - ", - ); -} - -#[test] -fn division_regex_ambiguity() { - let source = r" - /asdf/; x(); - a / 2; ' / ' - while (true) - /test'/ - x-/a'/g - try {} - finally{}/a'/g - (x);{f()}/d'export { b }/g - ;{}/e'/g; - {}/f'/g - a / 'b' / c; - /a'/ - /b'/; - +{} /g -'/g' - ('a')/h -'/g' - if //x - ('a')/i'/g; - /asdf/ / /as'df/; // ' - p = `\${/test/ + 5}`; - /regex/ / x; - function m() { - return /*asdf8*// 5/; - } - export { a }; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 0); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "a", Some("a")); -} - -#[test] -fn template_string_expression_ambiguity() { - let source = r" - `$` - import 'a'; - `` - export { b }; - `a$b` - import(`$`); - `{$}` - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 2); - assert_eq!(exports.len(), 1); - assert_export_is(source, &exports[0], "b", Some("b")); -} - -#[test] -fn many_exports() { - let exports = parse( - r" - export { _iconsCache as fas, prefix, faAbacus, faAcorn, faAd, faAddressBook, faAddressCard, faAdjust, faAirFreshener, faAlarmClock, faAlarmExclamation, faAlarmPlus, faAlarmSnooze, faAlicorn, faAlignCenter, faAlignJustify, faAlignLeft, faAlignRight, faAlignSlash, faAllergies, faAmbulance, faAmericanSignLanguageInterpreting, faAnalytics, faAnchor, faAngel, faAngleDoubleDown, faAngleDoubleLeft, faAngleDoubleRight, faAngleDoubleUp, faAngleDown, faAngleLeft, faAngleRight, faAngleUp, faAngry, faAnkh, faAppleAlt, faAppleCrate, faArchive, faArchway, faArrowAltCircleDown, faArrowAltCircleLeft, faArrowAltCircleRight, faArrowAltCircleUp, faArrowAltDown, faArrowAltFromBottom, faArrowAltFromLeft, faArrowAltFromRight, faArrowAltFromTop, faArrowAltLeft, faArrowAltRight, faArrowAltSquareDown, faArrowAltSquareLeft, faArrowAltSquareRight, faArrowAltSquareUp, faArrowAltToBottom, faArrowAltToLeft, faArrowAltToRight, faArrowAltToTop, faArrowAltUp, faArrowCircleDown, faArrowCircleLeft, faArrowCircleRight, faArrowCircleUp, faArrowDown, faArrowFromBottom, faArrowFromLeft, faArrowFromRight, faArrowFromTop, faArrowLeft, faArrowRight, faArrowSquareDown, faArrowSquareLeft, faArrowSquareRight, faArrowSquareUp, faArrowToBottom, faArrowToLeft, faArrowToRight, faArrowToTop, faArrowUp, faArrows, faArrowsAlt, faArrowsAltH, faArrowsAltV, faArrowsH, faArrowsV, faAssistiveListeningSystems, faAsterisk, faAt, faAtlas, faAtom, faAtomAlt, faAudioDescription, faAward, faAxe, faAxeBattle, faBaby, faBabyCarriage, faBackpack, faBackspace, faBackward, faBacon, faBadge, faBadgeCheck, faBadgeDollar, faBadgePercent, faBadgerHoney, faBagsShopping, faBalanceScale, faBalanceScaleLeft, faBalanceScaleRight, faBallPile, faBallot, faBallotCheck, faBan, faBandAid, faBarcode, faBarcodeAlt, faBarcodeRead, faBarcodeScan, faBars, faBaseball, faBaseballBall, faBasketballBall, faBasketballHoop, faBat, faBath, faBatteryBolt, faBatteryEmpty, faBatteryFull, faBatteryHalf, faBatteryQuarter, faBatterySlash, faBatteryThreeQuarters, faBed, faBeer, faBell, faBellExclamation, faBellPlus, faBellSchool, faBellSchoolSlash, faBellSlash, faBells, faBezierCurve, faBible, faBicycle, faBiking, faBikingMountain, faBinoculars, faBiohazard, faBirthdayCake, faBlanket, faBlender, faBlenderPhone, faBlind, faBlog, faBold, faBolt, faBomb, faBone, faBoneBreak, faBong, faBook, faBookAlt, faBookDead, faBookHeart, faBookMedical, faBookOpen, faBookReader, faBookSpells, faBookUser, faBookmark, faBooks, faBooksMedical, faBoot, faBoothCurtain, faBorderAll, faBorderBottom, faBorderCenterH, faBorderCenterV, faBorderInner, faBorderLeft, faBorderNone, faBorderOuter, faBorderRight, faBorderStyle, faBorderStyleAlt, faBorderTop, faBowArrow, faBowlingBall, faBowlingPins, faBox, faBoxAlt, faBoxBallot, faBoxCheck, faBoxFragile, faBoxFull, faBoxHeart, faBoxOpen, faBoxUp, faBoxUsd, faBoxes, faBoxesAlt, faBoxingGlove, faBrackets, faBracketsCurly, faBraille, faBrain, faBreadLoaf, faBreadSlice, faBriefcase, faBriefcaseMedical, faBringForward, faBringFront, faBroadcastTower, faBroom, faBrowser, faBrush, faBug, faBuilding, faBullhorn, faBullseye, faBullseyeArrow, faBullseyePointer, faBurgerSoda, faBurn, faBurrito, faBus, faBusAlt, faBusSchool, faBusinessTime, faCabinetFiling, faCalculator, faCalculatorAlt, faCalendar, faCalendarAlt, faCalendarCheck, faCalendarDay, faCalendarEdit, faCalendarExclamation, faCalendarMinus, faCalendarPlus, faCalendarStar, faCalendarTimes, faCalendarWeek, faCamera, faCameraAlt, faCameraRetro, faCampfire, faCampground, faCandleHolder, faCandyCane, faCandyCorn, faCannabis, faCapsules, faCar, faCarAlt, faCarBattery, faCarBuilding, faCarBump, faCarBus, faCarCrash, faCarGarage, faCarMechanic, faCarSide, faCarTilt, faCarWash, faCaretCircleDown, faCaretCircleLeft, faCaretCircleRight, faCaretCircleUp, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareLeft, faCaretSquareRight, faCaretSquareUp, faCaretUp, faCarrot, faCars, faCartArrowDown, faCartPlus, faCashRegister, faCat, faCauldron, faCertificate, faChair, faChairOffice, faChalkboard, faChalkboardTeacher, faChargingStation, faChartArea, faChartBar, faChartLine, faChartLineDown, faChartNetwork, faChartPie, faChartPieAlt, faChartScatter, faCheck, faCheckCircle, faCheckDouble, faCheckSquare, faCheese, faCheeseSwiss, faCheeseburger, faChess, faChessBishop, faChessBishopAlt, faChessBoard, faChessClock, faChessClockAlt, faChessKing, faChessKingAlt, faChessKnight, faChessKnightAlt, faChessPawn, faChessPawnAlt, faChessQueen, faChessQueenAlt, faChessRook, faChessRookAlt, faChevronCircleDown, faChevronCircleLeft, faChevronCircleRight, faChevronCircleUp, faChevronDoubleDown, faChevronDoubleLeft, faChevronDoubleRight, faChevronDoubleUp, faChevronDown, faChevronLeft, faChevronRight, faChevronSquareDown, faChevronSquareLeft, faChevronSquareRight, faChevronSquareUp, faChevronUp, faChild, faChimney, faChurch, faCircle, faCircleNotch, faCity, faClawMarks, faClinicMedical, faClipboard, faClipboardCheck, faClipboardList, faClipboardListCheck, faClipboardPrescription, faClipboardUser, faClock, faClone, faClosedCaptioning, faCloud, faCloudDownload, faCloudDownloadAlt, faCloudDrizzle, faCloudHail, faCloudHailMixed, faCloudMeatball, faCloudMoon, faCloudMoonRain, faCloudRain, faCloudRainbow, faCloudShowers, faCloudShowersHeavy, faCloudSleet, faCloudSnow, faCloudSun, faCloudSunRain, faCloudUpload, faCloudUploadAlt, faClouds, faCloudsMoon, faCloudsSun, faClub, faCocktail, faCode, faCodeBranch, faCodeCommit, faCodeMerge, faCoffee, faCoffeeTogo, faCoffin, faCog, faCogs, faCoin, faCoins, faColumns, faComment, faCommentAlt, faCommentAltCheck, faCommentAltDollar, faCommentAltDots, faCommentAltEdit, faCommentAltExclamation, faCommentAltLines, faCommentAltMedical, faCommentAltMinus, faCommentAltPlus, faCommentAltSlash, faCommentAltSmile, faCommentAltTimes, faCommentCheck, faCommentDollar, faCommentDots, faCommentEdit, faCommentExclamation, faCommentLines, faCommentMedical, faCommentMinus, faCommentPlus, faCommentSlash, faCommentSmile, faCommentTimes, faComments, faCommentsAlt, faCommentsAltDollar, faCommentsDollar, faCompactDisc, faCompass, faCompassSlash, faCompress, faCompressAlt, faCompressArrowsAlt, faCompressWide, faConciergeBell, faConstruction, faContainerStorage, faConveyorBelt, faConveyorBeltAlt, faCookie, faCookieBite, faCopy, faCopyright, faCorn, faCouch, faCow, faCreditCard, faCreditCardBlank, faCreditCardFront, faCricket, faCroissant, faCrop, faCropAlt, faCross, faCrosshairs, faCrow, faCrown, faCrutch, faCrutches, faCube, faCubes, faCurling, faCut, faDagger, faDatabase, faDeaf, faDebug, faDeer, faDeerRudolph, faDemocrat, faDesktop, faDesktopAlt, faDewpoint, faDharmachakra, faDiagnoses, faDiamond, faDice, faDiceD10, faDiceD12, faDiceD20, faDiceD4, faDiceD6, faDiceD8, faDiceFive, faDiceFour, faDiceOne, faDiceSix, faDiceThree, faDiceTwo, faDigging, faDigitalTachograph, faDiploma, faDirections, faDisease, faDivide, faDizzy, faDna, faDoNotEnter, faDog, faDogLeashed, faDollarSign, faDolly, faDollyEmpty, faDollyFlatbed, faDollyFlatbedAlt, faDollyFlatbedEmpty, faDonate, faDoorClosed, faDoorOpen, faDotCircle, faDove, faDownload, faDraftingCompass, faDragon, faDrawCircle, faDrawPolygon, faDrawSquare, faDreidel, faDrone, faDroneAlt, faDrum, faDrumSteelpan, faDrumstick, faDrumstickBite, faDryer, faDryerAlt, faDuck, faDumbbell, faDumpster, faDumpsterFire, faDungeon, faEar, faEarMuffs, faEclipse, faEclipseAlt, faEdit, faEgg, faEggFried, faEject, faElephant, faEllipsisH, faEllipsisHAlt, faEllipsisV, faEllipsisVAlt, faEmptySet, faEngineWarning, faEnvelope, faEnvelopeOpen, faEnvelopeOpenDollar, faEnvelopeOpenText, faEnvelopeSquare, faEquals, faEraser, faEthernet, faEuroSign, faExchange, faExchangeAlt, faExclamation, faExclamationCircle, faExclamationSquare, faExclamationTriangle, faExpand, faExpandAlt, faExpandArrows, faExpandArrowsAlt, faExpandWide, faExternalLink, faExternalLinkAlt, faExternalLinkSquare, faExternalLinkSquareAlt, faEye, faEyeDropper, faEyeEvil, faEyeSlash, faFan, faFarm, faFastBackward, faFastForward, faFax, faFeather, faFeatherAlt, faFemale, faFieldHockey, faFighterJet, faFile, faFileAlt, faFileArchive, faFileAudio, faFileCertificate, faFileChartLine, faFileChartPie, faFileCheck, faFileCode, faFileContract, faFileCsv, faFileDownload, faFileEdit, faFileExcel, faFileExclamation, faFileExport, faFileImage, faFileImport, faFileInvoice, faFileInvoiceDollar, faFileMedical, faFileMedicalAlt, faFileMinus, faFilePdf, faFilePlus, faFilePowerpoint, faFilePrescription, faFileSearch, faFileSignature, faFileSpreadsheet, faFileTimes, faFileUpload, faFileUser, faFileVideo, faFileWord, faFilesMedical, faFill, faFillDrip, faFilm, faFilmAlt, faFilter, faFingerprint, faFire, faFireAlt, faFireExtinguisher, faFireSmoke, faFireplace, faFirstAid, faFish, faFishCooked, faFistRaised, faFlag, faFlagAlt, faFlagCheckered, faFlagUsa, faFlame, faFlask, faFlaskPoison, faFlaskPotion, faFlower, faFlowerDaffodil, faFlowerTulip, faFlushed, faFog, faFolder, faFolderMinus, faFolderOpen, faFolderPlus, faFolderTimes, faFolderTree, faFolders, faFont, faFontAwesomeLogoFull, faFontCase, faFootballBall, faFootballHelmet, faForklift, faForward, faFragile, faFrenchFries, faFrog, faFrostyHead, faFrown, faFrownOpen, faFunction, faFunnelDollar, faFutbol, faGameBoard, faGameBoardAlt, faGamepad, faGasPump, faGasPumpSlash, faGavel, faGem, faGenderless, faGhost, faGift, faGiftCard, faGifts, faGingerbreadMan, faGlass, faGlassChampagne, faGlassCheers, faGlassCitrus, faGlassMartini, faGlassMartiniAlt, faGlassWhiskey, faGlassWhiskeyRocks, faGlasses, faGlassesAlt, faGlobe, faGlobeAfrica, faGlobeAmericas, faGlobeAsia, faGlobeEurope, faGlobeSnow, faGlobeStand, faGolfBall, faGolfClub, faGopuram, faGraduationCap, faGreaterThan, faGreaterThanEqual, faGrimace, faGrin, faGrinAlt, faGrinBeam, faGrinBeamSweat, faGrinHearts, faGrinSquint, faGrinSquintTears, faGrinStars, faGrinTears, faGrinTongue, faGrinTongueSquint, faGrinTongueWink, faGrinWink, faGripHorizontal, faGripLines, faGripLinesVertical, faGripVertical, faGuitar, faHSquare, faH1, faH2, faH3, faH4, faHamburger, faHammer, faHammerWar, faHamsa, faHandHeart, faHandHolding, faHandHoldingBox, faHandHoldingHeart, faHandHoldingMagic, faHandHoldingSeedling, faHandHoldingUsd, faHandHoldingWater, faHandLizard, faHandMiddleFinger, faHandPaper, faHandPeace, faHandPointDown, faHandPointLeft, faHandPointRight, faHandPointUp, faHandPointer, faHandReceiving, faHandRock, faHandScissors, faHandSpock, faHands, faHandsHeart, faHandsHelping, faHandsUsd, faHandshake, faHandshakeAlt, faHanukiah, faHardHat, faHashtag, faHatChef, faHatSanta, faHatWinter, faHatWitch, faHatWizard, faHaykal, faHdd, faHeadSide, faHeadSideBrain, faHeadSideMedical, faHeadVr, faHeading, faHeadphones, faHeadphonesAlt, faHeadset, faHeart, faHeartBroken, faHeartCircle, faHeartRate, faHeartSquare, faHeartbeat, faHelicopter, faHelmetBattle, faHexagon, faHighlighter, faHiking, faHippo, faHistory, faHockeyMask, faHockeyPuck, faHockeySticks, faHollyBerry, faHome, faHomeAlt, faHomeHeart, faHomeLg, faHomeLgAlt, faHoodCloak, faHorizontalRule, faHorse, faHorseHead, faHospital, faHospitalAlt, faHospitalSymbol, faHospitalUser, faHospitals, faHotTub, faHotdog, faHotel, faHourglass, faHourglassEnd, faHourglassHalf, faHourglassStart, faHouseDamage, faHouseFlood, faHryvnia, faHumidity, faHurricane, faICursor, faIceCream, faIceSkate, faIcicles, faIcons, faIconsAlt, faIdBadge, faIdCard, faIdCardAlt, faIgloo, faImage, faImages, faInbox, faInboxIn, faInboxOut, faIndent, faIndustry, faIndustryAlt, faInfinity, faInfo, faInfoCircle, faInfoSquare, faInhaler, faIntegral, faIntersection, faInventory, faIslandTropical, faItalic, faJackOLantern, faJedi, faJoint, faJournalWhills, faKaaba, faKerning, faKey, faKeySkeleton, faKeyboard, faKeynote, faKhanda, faKidneys, faKiss, faKissBeam, faKissWinkHeart, faKite, faKiwiBird, faKnifeKitchen, faLambda, faLamp, faLandmark, faLandmarkAlt, faLanguage, faLaptop, faLaptopCode, faLaptopMedical, faLaugh, faLaughBeam, faLaughSquint, faLaughWink, faLayerGroup, faLayerMinus, faLayerPlus, faLeaf, faLeafHeart, faLeafMaple, faLeafOak, faLemon, faLessThan, faLessThanEqual, faLevelDown, faLevelDownAlt, faLevelUp, faLevelUpAlt, faLifeRing, faLightbulb, faLightbulbDollar, faLightbulbExclamation, faLightbulbOn, faLightbulbSlash, faLightsHoliday, faLineColumns, faLineHeight, faLink, faLips, faLiraSign, faList, faListAlt, faListOl, faListUl, faLocation, faLocationArrow, faLocationCircle, faLocationSlash, faLock, faLockAlt, faLockOpen, faLockOpenAlt, faLongArrowAltDown, faLongArrowAltLeft, faLongArrowAltRight, faLongArrowAltUp, faLongArrowDown, faLongArrowLeft, faLongArrowRight, faLongArrowUp, faLoveseat, faLowVision, faLuchador, faLuggageCart, faLungs, faMace, faMagic, faMagnet, faMailBulk, faMailbox, faMale, faMandolin, faMap, faMapMarked, faMapMarkedAlt, faMapMarker, faMapMarkerAlt, faMapMarkerAltSlash, faMapMarkerCheck, faMapMarkerEdit, faMapMarkerExclamation, faMapMarkerMinus, faMapMarkerPlus, faMapMarkerQuestion, faMapMarkerSlash, faMapMarkerSmile, faMapMarkerTimes, faMapPin, faMapSigns, faMarker, faMars, faMarsDouble, faMarsStroke, faMarsStrokeH, faMarsStrokeV, faMask, faMeat, faMedal, faMedkit, faMegaphone, faMeh, faMehBlank, faMehRollingEyes, faMemory, faMenorah, faMercury, faMeteor, faMicrochip, faMicrophone, faMicrophoneAlt, faMicrophoneAltSlash, faMicrophoneSlash, faMicroscope, faMindShare, faMinus, faMinusCircle, faMinusHexagon, faMinusOctagon, faMinusSquare, faMistletoe, faMitten, faMobile, faMobileAlt, faMobileAndroid, faMobileAndroidAlt, faMoneyBill, faMoneyBillAlt, faMoneyBillWave, faMoneyBillWaveAlt, faMoneyCheck, faMoneyCheckAlt, faMoneyCheckEdit, faMoneyCheckEditAlt, faMonitorHeartRate, faMonkey, faMonument, faMoon, faMoonCloud, faMoonStars, faMortarPestle, faMosque, faMotorcycle, faMountain, faMountains, faMousePointer, faMug, faMugHot, faMugMarshmallows, faMugTea, faMusic, faNarwhal, faNetworkWired, faNeuter, faNewspaper, faNotEqual, faNotesMedical, faObjectGroup, faObjectUngroup, faOctagon, faOilCan, faOilTemp, faOm, faOmega, faOrnament, faOtter, faOutdent, faOverline, faPageBreak, faPager, faPaintBrush, faPaintBrushAlt, faPaintRoller, faPalette, faPallet, faPalletAlt, faPaperPlane, faPaperclip, faParachuteBox, faParagraph, faParagraphRtl, faParking, faParkingCircle, faParkingCircleSlash, faParkingSlash, faPassport, faPastafarianism, faPaste, faPause, faPauseCircle, faPaw, faPawAlt, faPawClaws, faPeace, faPegasus, faPen, faPenAlt, faPenFancy, faPenNib, faPenSquare, faPencil, faPencilAlt, faPencilPaintbrush, faPencilRuler, faPennant, faPeopleCarry, faPepperHot, faPercent, faPercentage, faPersonBooth, faPersonCarry, faPersonDolly, faPersonDollyEmpty, faPersonSign, faPhone, faPhoneAlt, faPhoneLaptop, faPhoneOffice, faPhonePlus, faPhoneSlash, faPhoneSquare, faPhoneSquareAlt, faPhoneVolume, faPhotoVideo, faPi, faPie, faPig, faPiggyBank, faPills, faPizza, faPizzaSlice, faPlaceOfWorship, faPlane, faPlaneAlt, faPlaneArrival, faPlaneDeparture, faPlay, faPlayCircle, faPlug, faPlus, faPlusCircle, faPlusHexagon, faPlusOctagon, faPlusSquare, faPodcast, faPodium, faPodiumStar, faPoll, faPollH, faPollPeople, faPoo, faPooStorm, faPoop, faPopcorn, faPortrait, faPoundSign, faPowerOff, faPray, faPrayingHands, faPrescription, faPrescriptionBottle, faPrescriptionBottleAlt, faPresentation, faPrint, faPrintSearch, faPrintSlash, faProcedures, faProjectDiagram, faPumpkin, faPuzzlePiece, faQrcode, faQuestion, faQuestionCircle, faQuestionSquare, faQuidditch, faQuoteLeft, faQuoteRight, faQuran, faRabbit, faRabbitFast, faRacquet, faRadiation, faRadiationAlt, faRainbow, faRaindrops, faRam, faRampLoading, faRandom, faReceipt, faRectangleLandscape, faRectanglePortrait, faRectangleWide, faRecycle, faRedo, faRedoAlt, faRegistered, faRemoveFormat, faRepeat, faRepeat1, faRepeat1Alt, faRepeatAlt, faReply, faReplyAll, faRepublican, faRestroom, faRetweet, faRetweetAlt, faRibbon, faRing, faRingsWedding, faRoad, faRobot, faRocket, faRoute, faRouteHighway, faRouteInterstate, faRss, faRssSquare, faRubleSign, faRuler, faRulerCombined, faRulerHorizontal, faRulerTriangle, faRulerVertical, faRunning, faRupeeSign, faRv, faSack, faSackDollar, faSadCry, faSadTear, faSalad, faSandwich, faSatellite, faSatelliteDish, faSausage, faSave, faScalpel, faScalpelPath, faScanner, faScannerKeyboard, faScannerTouchscreen, faScarecrow, faScarf, faSchool, faScrewdriver, faScroll, faScrollOld, faScrubber, faScythe, faSdCard, faSearch, faSearchDollar, faSearchLocation, faSearchMinus, faSearchPlus, faSeedling, faSendBack, faSendBackward, faServer, faShapes, faShare, faShareAll, faShareAlt, faShareAltSquare, faShareSquare, faSheep, faShekelSign, faShield, faShieldAlt, faShieldCheck, faShieldCross, faShip, faShippingFast, faShippingTimed, faShishKebab, faShoePrints, faShoppingBag, faShoppingBasket, faShoppingCart, faShovel, faShovelSnow, faShower, faShredder, faShuttleVan, faShuttlecock, faSickle, faSigma, faSign, faSignIn, faSignInAlt, faSignLanguage, faSignOut, faSignOutAlt, faSignal, faSignal1, faSignal2, faSignal3, faSignal4, faSignalAlt, faSignalAlt1, faSignalAlt2, faSignalAlt3, faSignalAltSlash, faSignalSlash, faSignature, faSimCard, faSitemap, faSkating, faSkeleton, faSkiJump, faSkiLift, faSkiing, faSkiingNordic, faSkull, faSkullCrossbones, faSlash, faSledding, faSleigh, faSlidersH, faSlidersHSquare, faSlidersV, faSlidersVSquare, faSmile, faSmileBeam, faSmilePlus, faSmileWink, faSmog, faSmoke, faSmoking, faSmokingBan, faSms, faSnake, faSnooze, faSnowBlowing, faSnowboarding, faSnowflake, faSnowflakes, faSnowman, faSnowmobile, faSnowplow, faSocks, faSolarPanel, faSort, faSortAlphaDown, faSortAlphaDownAlt, faSortAlphaUp, faSortAlphaUpAlt, faSortAlt, faSortAmountDown, faSortAmountDownAlt, faSortAmountUp, faSortAmountUpAlt, faSortDown, faSortNumericDown, faSortNumericDownAlt, faSortNumericUp, faSortNumericUpAlt, faSortShapesDown, faSortShapesDownAlt, faSortShapesUp, faSortShapesUpAlt, faSortSizeDown, faSortSizeDownAlt, faSortSizeUp, faSortSizeUpAlt, faSortUp, faSoup, faSpa, faSpaceShuttle, faSpade, faSparkles, faSpellCheck, faSpider, faSpiderBlackWidow, faSpiderWeb, faSpinner, faSpinnerThird, faSplotch, faSprayCan, faSquare, faSquareFull, faSquareRoot, faSquareRootAlt, faSquirrel, faStaff, faStamp, faStar, faStarAndCrescent, faStarChristmas, faStarExclamation, faStarHalf, faStarHalfAlt, faStarOfDavid, faStarOfLife, faStars, faSteak, faSteeringWheel, faStepBackward, faStepForward, faStethoscope, faStickyNote, faStocking, faStomach, faStop, faStopCircle, faStopwatch, faStore, faStoreAlt, faStream, faStreetView, faStretcher, faStrikethrough, faStroopwafel, faSubscript, faSubway, faSuitcase, faSuitcaseRolling, faSun, faSunCloud, faSunDust, faSunHaze, faSunglasses, faSunrise, faSunset, faSuperscript, faSurprise, faSwatchbook, faSwimmer, faSwimmingPool, faSword, faSwords, faSynagogue, faSync, faSyncAlt, faSyringe, faTable, faTableTennis, faTablet, faTabletAlt, faTabletAndroid, faTabletAndroidAlt, faTabletRugged, faTablets, faTachometer, faTachometerAlt, faTachometerAltAverage, faTachometerAltFast, faTachometerAltFastest, faTachometerAltSlow, faTachometerAltSlowest, faTachometerAverage, faTachometerFast, faTachometerFastest, faTachometerSlow, faTachometerSlowest, faTaco, faTag, faTags, faTally, faTanakh, faTape, faTasks, faTasksAlt, faTaxi, faTeeth, faTeethOpen, faTemperatureFrigid, faTemperatureHigh, faTemperatureHot, faTemperatureLow, faTenge, faTennisBall, faTerminal, faText, faTextHeight, faTextSize, faTextWidth, faTh, faThLarge, faThList, faTheaterMasks, faThermometer, faThermometerEmpty, faThermometerFull, faThermometerHalf, faThermometerQuarter, faThermometerThreeQuarters, faTheta, faThumbsDown, faThumbsUp, faThumbtack, faThunderstorm, faThunderstormMoon, faThunderstormSun, faTicket, faTicketAlt, faTilde, faTimes, faTimesCircle, faTimesHexagon, faTimesOctagon, faTimesSquare, faTint, faTintSlash, faTire, faTireFlat, faTirePressureWarning, faTireRugged, faTired, faToggleOff, faToggleOn, faToilet, faToiletPaper, faToiletPaperAlt, faTombstone, faTombstoneAlt, faToolbox, faTools, faTooth, faToothbrush, faTorah, faToriiGate, faTornado, faTractor, faTrademark, faTrafficCone, faTrafficLight, faTrafficLightGo, faTrafficLightSlow, faTrafficLightStop, faTrain, faTram, faTransgender, faTransgenderAlt, faTrash, faTrashAlt, faTrashRestore, faTrashRestoreAlt, faTrashUndo, faTrashUndoAlt, faTreasureChest, faTree, faTreeAlt, faTreeChristmas, faTreeDecorated, faTreeLarge, faTreePalm, faTrees, faTriangle, faTrophy, faTrophyAlt, faTruck, faTruckContainer, faTruckCouch, faTruckLoading, faTruckMonster, faTruckMoving, faTruckPickup, faTruckPlow, faTruckRamp, faTshirt, faTty, faTurkey, faTurtle, faTv, faTvRetro, faUmbrella, faUmbrellaBeach, faUnderline, faUndo, faUndoAlt, faUnicorn, faUnion, faUniversalAccess, faUniversity, faUnlink, faUnlock, faUnlockAlt, faUpload, faUsdCircle, faUsdSquare, faUser, faUserAlt, faUserAltSlash, faUserAstronaut, faUserChart, faUserCheck, faUserCircle, faUserClock, faUserCog, faUserCrown, faUserEdit, faUserFriends, faUserGraduate, faUserHardHat, faUserHeadset, faUserInjured, faUserLock, faUserMd, faUserMdChat, faUserMinus, faUserNinja, faUserNurse, faUserPlus, faUserSecret, faUserShield, faUserSlash, faUserTag, faUserTie, faUserTimes, faUsers, faUsersClass, faUsersCog, faUsersCrown, faUsersMedical, faUtensilFork, faUtensilKnife, faUtensilSpoon, faUtensils, faUtensilsAlt, faValueAbsolute, faVectorSquare, faVenus, faVenusDouble, faVenusMars, faVial, faVials, faVideo, faVideoPlus, faVideoSlash, faVihara, faVoicemail, faVolcano, faVolleyballBall, faVolume, faVolumeDown, faVolumeMute, faVolumeOff, faVolumeSlash, faVolumeUp, faVoteNay, faVoteYea, faVrCardboard, faWalker, faWalking, faWallet, faWand, faWandMagic, faWarehouse, faWarehouseAlt, faWasher, faWatch, faWatchFitness, faWater, faWaterLower, faWaterRise, faWaveSine, faWaveSquare, faWaveTriangle, faWebcam, faWebcamSlash, faWeight, faWeightHanging, faWhale, faWheat, faWheelchair, faWhistle, faWifi, faWifi1, faWifi2, faWifiSlash, faWind, faWindTurbine, faWindWarning, faWindow, faWindowAlt, faWindowClose, faWindowMaximize, faWindowMinimize, faWindowRestore, faWindsock, faWineBottle, faWineGlass, faWineGlassAlt, faWonSign, faWreath, faWrench, faXRay, faYenSign, faYinYang }; - ", - ).exports; - assert_eq!(exports.len(), 1651); -} - -#[test] -fn empty_export() { - let source = r" - export {}; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 0); - assert_eq!(exports.len(), 0); -} - -#[test] -fn export_star_as() { - let source = r" - export * as X from './asdf'; - export * as yy from './g'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 2); - assert_eq!(exports.len(), 2); - assert_export_is(source, &exports[0], "X", None); - assert_export_is(source, &exports[1], "yy", None); -} - -/* Suite Import From */ - -#[test] -fn non_identifier_string_double_quote() { - let source = r#" - import { "~123" as foo0 } from './mod0.js'; - import { "ab cd" as foo1 } from './mod1.js'; - import { "not identifier" as foo2 } from './mod2.js'; - import { "-notidentifier" as foo3 } from './mod3.js'; - import { "%notidentifier" as foo4 } from './mod4.js'; - import { "@notidentifier" as foo5 } from './mod5.js'; - import { " notidentifier" as foo6 } from './mod6.js'; - import { "notidentifier " as foo7 } from './mod7.js'; - import { " notidentifier " as foo8 } from './mod8.js'; - import LionCombobox from './src/LionCombobox.js'; // assuming LionCombobox is imported directly - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 10); - assert_eq!(imports[0].n.clone().unwrap(), "./mod0.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./mod1.js"); - assert_eq!(imports[2].n.clone().unwrap(), "./mod2.js"); - assert_eq!(imports[3].n.clone().unwrap(), "./mod3.js"); - assert_eq!(imports[4].n.clone().unwrap(), "./mod4.js"); - assert_eq!(imports[5].n.clone().unwrap(), "./mod5.js"); - assert_eq!(imports[6].n.clone().unwrap(), "./mod6.js"); - assert_eq!(imports[7].n.clone().unwrap(), "./mod7.js"); - assert_eq!(imports[8].n.clone().unwrap(), "./mod8.js"); -} - -#[test] -fn non_identifier_string_single_quote() { - let source = r" - import { '~123' as foo0 } from './mod0.js'; - import { 'ab cd' as foo1 } from './mod1.js'; - import { 'not identifier' as foo2 } from './mod2.js'; - import { '-notidentifier' as foo3 } from './mod3.js'; - import { '%notidentifier' as foo4 } from './mod4.js'; - import { '@notidentifier' as foo5 } from './mod5.js'; - import { ' notidentifier' as foo6 } from './mod6.js'; - import { 'notidentifier ' as foo7 } from './mod7.js'; - import { ' notidentifier ' as foo8 } from './mod8.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 9); - assert_eq!(imports[0].n.clone().unwrap(), "./mod0.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./mod1.js"); - assert_eq!(imports[2].n.clone().unwrap(), "./mod2.js"); - assert_eq!(imports[3].n.clone().unwrap(), "./mod3.js"); - assert_eq!(imports[4].n.clone().unwrap(), "./mod4.js"); - assert_eq!(imports[5].n.clone().unwrap(), "./mod5.js"); - assert_eq!(imports[6].n.clone().unwrap(), "./mod6.js"); - assert_eq!(imports[7].n.clone().unwrap(), "./mod7.js"); - assert_eq!(imports[8].n.clone().unwrap(), "./mod8.js"); -} - -#[test] -fn with_backslash_keywords_double_quote() { - let source = r#" - import { " slash\\ " as foo0 } from './mod0.js'; - import { " quote\" " as foo1 } from './mod1.js'; - import { " quote\\\" " as foo2 } from './mod2.js'; - import { " quote' " as foo3 } from './mod3.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 4); - assert_eq!(imports[0].n.clone().unwrap(), "./mod0.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./mod1.js"); - assert_eq!(imports[2].n.clone().unwrap(), "./mod2.js"); - assert_eq!(imports[3].n.clone().unwrap(), "./mod3.js"); -} - -#[test] -fn with_backslash_keywords_single_quote() { - let source = r" - import { ' slash\\ ' as foo0 } from './mod0.js'; - import { ' quote\' ' as foo1 } from './mod1.js'; - import { ' quote\\\' ' as foo2 } from './mod2.js'; - import { ' quote\' ' as foo3 } from './mod3.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 4); - assert_eq!(imports[0].n.clone().unwrap(), "./mod0.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./mod1.js"); - assert_eq!(imports[2].n.clone().unwrap(), "./mod2.js"); - assert_eq!(imports[3].n.clone().unwrap(), "./mod3.js"); -} - -#[test] -fn with_emoji_as() { - let source = r#" - import { "hm🤔" as foo0 } from './mod0.js'; - import { " 🚀rocket space " as foo1 } from './mod1.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 2); - assert_eq!(imports[0].n.clone().unwrap(), "./mod0.js"); - assert_eq!(imports[1].n.clone().unwrap(), "./mod1.js"); -} - -#[test] -fn double_quotes_and_curly_bracket() { - // cannot be parsed - // let source = " - // import { asdf as \"b} from 'wrong'\" } from 'mod0';"; - let source = " - import { asdf as x } from 'mod0';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 1); - assert_eq!(imports[0].n.clone().unwrap(), "mod0"); -} - -#[test] -fn single_quotes_and_curly_bracket() { - // cannot be parsed - // let source = " - // import { asdf as 'b} from \"wrong\"' } from 'mod0';"; - let source = " - import { asdf as x } from 'mod0';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(exports.len(), 0); - assert_eq!(imports.len(), 1); - assert_eq!(imports[0].n.clone().unwrap(), "mod0"); -} - -/* Export From */ - -#[test] -fn identifier_only() { - let source = " - export { x } from './asdf'; - export { x1, x2 } from './g'; - export { foo, x2 as bar, zoo } from './g2'; - export { - /** @type{HTMLElement} */ - LionCombobox - } from './src/LionCombobox.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 7); - assert_export_is(source, &exports[0], "x", None); - assert_export_is(source, &exports[1], "x1", None); - assert_export_is(source, &exports[2], "x2", None); - assert_export_is(source, &exports[3], "foo", None); - assert_export_is(source, &exports[4], "bar", None); - assert_export_is(source, &exports[5], "zoo", None); - assert_export_is(source, &exports[6], "LionCombobox", None); -} - -#[test] -fn non_identifier_string_as_variable_double_quote() { - let source = " - export { \"~123\" as foo0 } from './mod0.js'; - export { \"ab cd\" as foo1 } from './mod1.js'; - export { \"not identifier\" as foo2 } from './mod2.js'; - export { \"-notidentifier\" as foo3 } from './mod3.js'; - export { \"%notidentifier\" as foo4 } from './mod4.js'; - export { \"@notidentifier\" as foo5 } from './mod5.js'; - export { \" notidentifier\" as foo6 } from './mod6.js'; - export { \"notidentifier \" as foo7 } from './mod7.js'; - export { \" notidentifier \" as foo8 } from './mod8.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "foo0", None); - assert_export_is(source, &exports[1], "foo1", None); - assert_export_is(source, &exports[2], "foo2", None); - assert_export_is(source, &exports[3], "foo3", None); - assert_export_is(source, &exports[4], "foo4", None); - assert_export_is(source, &exports[5], "foo5", None); - assert_export_is(source, &exports[6], "foo6", None); - assert_export_is(source, &exports[7], "foo7", None); - assert_export_is(source, &exports[8], "foo8", None); -} - -#[test] -fn non_identifier_string_as_variable_single_quote() { - let source = " - export { '~123' as foo0 } from './mod0.js'; - export { 'ab cd' as foo1 } from './mod1.js'; - export { 'not identifier' as foo2 } from './mod2.js'; - export { '-notidentifier' as foo3 } from './mod3.js'; - export { '%notidentifier' as foo4 } from './mod4.js'; - export { '@notidentifier' as foo5 } from './mod5.js'; - export { ' notidentifier' as foo6 } from './mod6.js'; - export { 'notidentifier ' as foo7 } from './mod7.js'; - export { ' notidentifier ' as foo8 } from './mod8.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "foo0", None); - assert_export_is(source, &exports[1], "foo1", None); - assert_export_is(source, &exports[2], "foo2", None); - assert_export_is(source, &exports[3], "foo3", None); - assert_export_is(source, &exports[4], "foo4", None); - assert_export_is(source, &exports[5], "foo5", None); - assert_export_is(source, &exports[6], "foo6", None); - assert_export_is(source, &exports[7], "foo7", None); - assert_export_is(source, &exports[8], "foo8", None); -} - -#[test] -fn with_backslash_keywords_as_variable_double_quote() { - let source = r#" - export { " slash\\ " as foo0 } from './mod0.js'; - export { " quote\" " as foo1 } from './mod1.js'; - export { " quote\\\" " as foo2 } from './mod2.js'; - export { " quote' " as foo3 } from './mod3.js';"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], "foo0", None); - assert_export_is(source, &exports[1], "foo1", None); - assert_export_is(source, &exports[2], "foo2", None); - assert_export_is(source, &exports[3], "foo3", None); -} - -#[test] -fn with_backslash_keywords_as_variable_single_quote() { - let source = r" - export { ' slash\\ ' as foo0 } from './mod0.js'; - export { ' quote\' ' as foo1 } from './mod1.js'; - export { ' quote\\\' ' as foo2 } from './mod2.js'; - export { ' quote\' ' as foo3 } from './mod3.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], "foo0", None); - assert_export_is(source, &exports[1], "foo1", None); - assert_export_is(source, &exports[2], "foo2", None); - assert_export_is(source, &exports[3], "foo3", None); -} - -#[test] -fn with_emoji_as_2() { - let source = r#" - export { "hm🤔" as foo0 } from './mod0.js'; - export { " 🚀rocket space " as foo1 } from './mod1.js';"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 2); - assert_eq!(exports.len(), 2); - assert_export_is(source, &exports[0], "foo0", None); - assert_export_is(source, &exports[1], "foo1", None); -} - -#[test] -fn non_identifier_string_double_quote_2() { - let source = r#" - export { "~123" } from './mod0.js'; - export { "ab cd" } from './mod1.js'; - export { "not identifier" } from './mod2.js'; - export { "-notidentifier" } from './mod3.js'; - export { "%notidentifier" } from './mod4.js'; - export { "@notidentifier" } from './mod5.js'; - export { " notidentifier" } from './mod6.js'; - export { "notidentifier " } from './mod7.js'; - export { " notidentifier " } from './mod8.js';"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn non_identifier_string_single_quote_2() { - let source = r" - export { '~123' } from './mod0.js'; - export { 'ab cd' } from './mod1.js'; - export { 'not identifier' } from './mod2.js'; - export { '-notidentifier' } from './mod3.js'; - export { '%notidentifier' } from './mod4.js'; - export { '@notidentifier' } from './mod5.js'; - export { ' notidentifier' } from './mod6.js'; - export { 'notidentifier ' } from './mod7.js'; - export { ' notidentifier ' } from './mod8.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn with_backslash_keywords_double_quote_2() { - let source = r#" - export { " slash\\ " } from './mod0.js'; - export { " quote\" " } from './mod1.js'; - export { " quote\\\" " } from './mod2.js'; - export { " quote' " } from './mod3.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote\" ", None); - assert_export_is(source, &exports[2], " quote\\\" ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn with_backslash_keywords_single_quote_2() { - let source = r" - export { ' slash\\ ' } from './mod0.js'; - export { ' quote\' ' } from './mod1.js'; - export { ' quote\\\' ' } from './mod2.js'; - export { ' quote\' ' } from './mod3.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote' ", None); - assert_export_is(source, &exports[2], " quote\\' ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn variable_as_non_identifier_string_double_quote() { - let source = r#" - export { foo0 as "~123" } from './mod0.js'; - export { foo1 as "ab cd" } from './mod1.js'; - export { foo2 as "not identifier" } from './mod2.js'; - export { foo3 as "-notidentifier" } from './mod3.js'; - export { foo4 as "%notidentifier" } from './mod4.js'; - export { foo5 as "@notidentifier" } from './mod5.js'; - export { foo6 as " notidentifier" } from './mod6.js'; - export { foo7 as "notidentifier " } from './mod7.js'; - export { foo8 as " notidentifier " } from './mod8.js';"#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn variable_as_non_identifier_string_single_quote() { - let source = r" - export { foo0 as '~123' } from './mod0.js'; - export { foo1 as 'ab cd' } from './mod1.js'; - export { foo2 as 'not identifier' } from './mod2.js'; - export { foo3 as '-notidentifier' } from './mod3.js'; - export { foo4 as '%notidentifier' } from './mod4.js'; - export { foo5 as '@notidentifier' } from './mod5.js'; - export { foo6 as ' notidentifier' } from './mod6.js'; - export { foo7 as 'notidentifier ' } from './mod7.js'; - export { foo8 as ' notidentifier ' } from './mod8.js';"; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn variable_as_with_backslash_keywords_double_quote() { - let source = r#" - export { foo0 as " slash\\ " } from './mod0.js'; - export { foo1 as " quote\" " } from './mod1.js'; - export { foo2 as " quote\\\" " } from './mod2.js'; - export { foo3 as " quote' " } from './mod3.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote\" ", None); - assert_export_is(source, &exports[2], " quote\\\" ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn variable_as_with_backslash_keywords_single_quote() { - let source = r" - export { foo0 as ' slash\\ ' } from './mod0.js'; - export { foo1 as ' quote\' ' } from './mod1.js'; - export { foo2 as ' quote\\\' ' } from './mod2.js'; - export { foo3 as ' quote\' ' } from './mod3.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote' ", None); - assert_export_is(source, &exports[2], " quote\\' ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn non_identifier_string_as_non_identifier_string_double_quote() { - let source = r#" - export { "~123" as "~123" } from './mod0.js'; - export { "ab cd" as "ab cd" } from './mod1.js'; - export { "not identifier" as "not identifier" } from './mod2.js'; - export { "-notidentifier" as "-notidentifier" } from './mod3.js'; - export { "%notidentifier" as "%notidentifier" } from './mod4.js'; - export { "@notidentifier" as "@notidentifier" } from './mod5.js'; - export { " notidentifier" as " notidentifier" } from './mod6.js'; - export { "notidentifier " as "notidentifier " } from './mod7.js'; - export { " notidentifier " as " notidentifier " } from './mod8.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn non_identifier_string_as_non_identifier_string_single_quote() { - let source = r" - export { '~123' as '~123' } from './mod0.js'; - export { 'ab cd' as 'ab cd' } from './mod1.js'; - export { 'not identifier' as 'not identifier' } from './mod2.js'; - export { '-notidentifier' as '-notidentifier' } from './mod3.js'; - export { '%notidentifier' as '%notidentifier' } from './mod4.js'; - export { '@notidentifier' as '@notidentifier' } from './mod5.js'; - export { ' notidentifier' as ' notidentifier' } from './mod6.js'; - export { 'notidentifier ' as 'notidentifier ' } from './mod7.js'; - export { ' notidentifier ' as ' notidentifier ' } from './mod8.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 9); - assert_eq!(exports.len(), 9); - assert_export_is(source, &exports[0], "~123", None); - assert_export_is(source, &exports[1], "ab cd", None); - assert_export_is(source, &exports[2], "not identifier", None); - assert_export_is(source, &exports[3], "-notidentifier", None); - assert_export_is(source, &exports[4], "%notidentifier", None); - assert_export_is(source, &exports[5], "@notidentifier", None); - assert_export_is(source, &exports[6], " notidentifier", None); - assert_export_is(source, &exports[7], "notidentifier ", None); - assert_export_is(source, &exports[8], " notidentifier ", None); -} - -#[test] -fn with_backslash_keywords_as_with_backslash_keywords_double_quote() { - let source = r#" - export { " slash\\ " as " slash\\ " } from './mod0.js'; - export { " quote\"" as " quote\" " } from './mod1.js' - export { " quote\\\" " as " quote\\\" " } from './mod2.js'; - export { " quote' " as " quote' " } from './mod3.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote\" ", None); - assert_export_is(source, &exports[2], " quote\\\" ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn with_backslash_keywords_as_with_backslash_keywords_single_quote() { - let source = r" - export { ' slash\\ ' as ' slash\\ ' } from './mod0.js'; - export { ' quote\'' as ' quote\' ' } from './mod1.js' - export { ' quote\\\' ' as ' quote\\\' ' } from './mod2.js'; - export { ' quote\' ' as ' quote\' ' } from './mod3.js'; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 4); - assert_eq!(exports.len(), 4); - assert_export_is(source, &exports[0], " slash\\ ", None); - assert_export_is(source, &exports[1], " quote' ", None); - assert_export_is(source, &exports[2], " quote\\' ", None); - assert_export_is(source, &exports[3], " quote' ", None); -} - -#[test] -fn curly_brace_double_quote() { - let source = r#" - export { " right-curlybrace} " } from './mod0.js'; - export { " {left-curlybrace " } from './mod1.js'; - export { " {curlybrackets} " } from './mod2.js'; - export { ' right-curlybrace} ' } from './mod0.js'; - export { ' {left-curlybrace ' } from './mod1.js'; - export { ' {curlybrackets} ' } from './mod2.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 6); - assert_eq!(exports.len(), 6); - assert_export_is(source, &exports[0], " right-curlybrace} ", None); - assert_export_is(source, &exports[1], " {left-curlybrace ", None); - assert_export_is(source, &exports[2], " {curlybrackets} ", None); - assert_export_is(source, &exports[3], " right-curlybrace} ", None); - assert_export_is(source, &exports[4], " {left-curlybrace ", None); - assert_export_is(source, &exports[5], " {curlybrackets} ", None); -} - -#[test] -fn as_curly_brace_double_quote() { - let source = r#" - export { foo as " right-curlybrace} " } from './mod0.js'; - export { foo as " {left-curlybrace " } from './mod1.js'; - export { foo as " {curlybrackets} " } from './mod2.js'; - export { foo as ' right-curlybrace} ' } from './mod0.js'; - export { foo as ' {left-curlybrace ' } from './mod1.js'; - export { foo as ' {curlybrackets} ' } from './mod2.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 6); - assert_eq!(exports.len(), 6); - assert_export_is(source, &exports[0], " right-curlybrace} ", None); - assert_export_is(source, &exports[1], " {left-curlybrace ", None); - assert_export_is(source, &exports[2], " {curlybrackets} ", None); - assert_export_is(source, &exports[3], " right-curlybrace} ", None); - assert_export_is(source, &exports[4], " {left-curlybrace ", None); - assert_export_is(source, &exports[5], " {curlybrackets} ", None); -} - -#[test] -fn curly_brace_as_curly_brace_double_quote() { - let source = r#" - export { " right-curlybrace} " as " right-curlybrace} " } from './mod0.js'; - export { " {left-curlybrace " as " {left-curlybrace " } from './mod1.js'; - export { " {curlybrackets} " as " {curlybrackets} " } from './mod2.js'; - export { ' right-curlybrace} ' as ' right-curlybrace} ' } from './mod0.js'; - export { ' {left-curlybrace ' as ' {left-curlybrace ' } from './mod1.js'; - export { ' {curlybrackets} ' as ' {curlybrackets} ' } from './mod2.js'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 6); - assert_eq!(exports.len(), 6); - assert_export_is(source, &exports[0], " right-curlybrace} ", None); - assert_export_is(source, &exports[1], " {left-curlybrace ", None); - assert_export_is(source, &exports[2], " {curlybrackets} ", None); - assert_export_is(source, &exports[3], " right-curlybrace} ", None); - assert_export_is(source, &exports[4], " {left-curlybrace ", None); - assert_export_is(source, &exports[5], " {curlybrackets} ", None); -} - -#[test] -fn complex_and_edge_cases() { - let source = r#" - export { - foo, - foo1 as foo2, - " {left-curlybrace ", - " {curly-brackets}" as "@notidentifier", - "?" as "identifier", - } from './mod0.js'; - export { "p as 'z' from 'asdf'" as "z'" } from 'asdf'; - export { "z'" as "p as 'z' from 'asdf'" } from 'asdf'; - "#; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 3); - assert_eq!(exports.len(), 7); - assert_export_is(source, &exports[0], "foo", None); - assert_export_is(source, &exports[1], "foo2", None); - assert_export_is(source, &exports[2], " {left-curlybrace ", None); - assert_export_is(source, &exports[3], "@notidentifier", None); - assert_export_is(source, &exports[4], "identifier", None); - assert_export_is(source, &exports[5], "z'", None); - assert_export_is(source, &exports[6], "p as 'z' from 'asdf'", None); -} - -#[test] -fn export_default() { - let source = r" - export default async function example () {}; - export const a = '1'; - export default a; - export default function example1() {}; - export default function() {}; - export default class className {/* ... */}; - export default class {} - export default function* generatorFunctionName(){/* ... */}; - export default function* () {}; - const async = 1 - export default async - - function x() {} - - const asyncVar = 1 - export default asyncVar - - function functionName () {}; - export default functionName; - "; - let ModuleLexer { imports, exports, .. } = parse(source); - assert_eq!(imports.len(), 0); - assert_eq!(exports.len(), 12); - assert_export_is(source, &exports[0], "default", Some("example")); - assert_export_is(source, &exports[1], "a", Some("a")); - assert_export_is(source, &exports[2], "default", None); - assert_export_is(source, &exports[3], "default", Some("example1")); - assert_export_is(source, &exports[4], "default", None); - assert_export_is(source, &exports[5], "default", Some("className")); - assert_export_is(source, &exports[6], "default", None); - assert_export_is(source, &exports[7], "default", Some("generatorFunctionName")); - assert_export_is(source, &exports[8], "default", None); - assert_export_is(source, &exports[9], "default", None); - assert_export_is(source, &exports[10], "default", None); - assert_export_is(source, &exports[11], "default", None); -} - -/* Suite Invalid Syntax */ - -fn expect_parse_error(source: &str) { - let allocator = Allocator::default(); - let source_type = SourceType::mjs(); - let ret = Parser::new(&allocator, source, source_type).parse(); - assert!(!ret.errors.is_empty()); -} - -#[test] -fn unterminated_object() { - let source = r" - const foo = }; - const bar = {}; - "; - expect_parse_error(source); -} - -#[test] -fn invalid_string() { - let source = r"import './export.js'; - - import d from './export.js'; - - import { s as p } from './reexport1.js'; - - import { z, q as r } from './reexport2.js'; - - ' - - import * as q from './reexport1.js'; - - export { d as a, p as b, z as c, r as d, q }`"; - expect_parse_error(source); -} - -#[test] -fn invalid_export() { - let source = "export { a = }"; - expect_parse_error(source); -} - -/* has_module_syntax */ - -#[test] -fn has_module_syntax_import1() { - let has_module_syntax = parse(r#"import foo from "./foo""#).has_module_syntax; - assert!(has_module_syntax); -} - -#[test] -fn has_module_syntax_import2() { - let has_module_syntax = parse(r#"const foo = "import""#).has_module_syntax; - assert!(!has_module_syntax); -} - -#[test] -fn has_module_syntax_import3() { - let has_module_syntax = parse(r#"import("./foo")"#).has_module_syntax; - // dynamic imports can be used in non-ESM files as well - assert!(!has_module_syntax); -} - -#[test] -fn has_module_syntax_import4() { - let has_module_syntax = parse(r"import.meta.url").has_module_syntax; - assert!(has_module_syntax); -} - -#[test] -fn has_module_syntax_export1() { - let has_module_syntax = parse(r#"export const foo = "foo""#).has_module_syntax; - assert!(has_module_syntax); -} - -#[test] -fn has_module_syntax_export2() { - let has_module_syntax = parse(r"export {}").has_module_syntax; - assert!(has_module_syntax); -} - -#[test] -fn has_module_syntax_export3() { - let has_module_syntax = parse(r#"export * from "./foo""#).has_module_syntax; - assert!(has_module_syntax); -} - -/* facade */ - -#[test] -fn facade() { - let facade = parse( - r" - export * from 'external'; - import * as ns from 'external2'; - export { a as b } from 'external3'; - export { ns }; - ", - ) - .facade; - assert!(facade); -} - -#[test] -fn facade_default() { - let facade = parse( - r" - import * as ns from 'external'; - export default ns; - ", - ) - .facade; - assert!(!facade); -} - -#[test] -fn facade_declaration1() { - let facade = parse(r"export function p () {}").facade; - assert!(!facade); -} - -#[test] -fn facade_declaration2() { - let facade = parse(r"export var p").facade; - assert!(!facade); -} - -#[test] -fn facade_declaration3() { - let facade = parse(r"export {}").facade; - assert!(facade); -} - -#[test] -fn facade_declaration4() { - let facade = parse(r"export class Q{}").facade; - assert!(!facade); -} - -#[test] -fn facade_side_effect() { - let facade = parse(r"console.log('any non esm syntax')").facade; - assert!(!facade); -} diff --git a/crates/oxc_module_lexer/tests/integration/main.rs b/crates/oxc_module_lexer/tests/integration/main.rs deleted file mode 100644 index 9f3d12a2d1b47..0000000000000 --- a/crates/oxc_module_lexer/tests/integration/main.rs +++ /dev/null @@ -1,82 +0,0 @@ -pub mod esm; -pub mod typescript; - -use oxc_allocator::Allocator; -use oxc_module_lexer::ImportType; -use oxc_parser::Parser; -use oxc_span::SourceType; - -#[non_exhaustive] -pub struct ModuleLexer { - pub imports: Vec, - pub exports: Vec, - pub has_module_syntax: bool, - pub facade: bool, -} - -#[derive(Debug, Clone)] -pub struct ImportSpecifier { - pub n: Option, - pub s: u32, - pub e: u32, - pub ss: u32, - pub se: u32, - pub d: ImportType, - pub a: Option, - pub t: bool, -} - -impl From> for ImportSpecifier { - fn from(value: oxc_module_lexer::ImportSpecifier) -> Self { - Self { - n: value.n.map(|n| n.to_string()), - s: value.s, - e: value.e, - ss: value.ss, - se: value.se, - d: value.d, - a: value.a, - t: value.t, - } - } -} - -#[derive(Debug, Clone)] -pub struct ExportSpecifier { - pub n: String, - pub ln: Option, - pub s: u32, - pub e: u32, - pub ls: Option, - pub le: Option, - pub t: bool, -} - -impl From> for ExportSpecifier { - fn from(value: oxc_module_lexer::ExportSpecifier) -> Self { - Self { - n: value.n.to_string(), - ln: value.ln.map(|ln| ln.to_string()), - s: value.s, - e: value.e, - ls: value.ls, - le: value.le, - t: value.t, - } - } -} - -/// # Panics -pub fn parse(source: &str) -> ModuleLexer { - let allocator = Allocator::default(); - let source_type = SourceType::mjs(); - let ret = Parser::new(&allocator, source, source_type).parse(); - let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); - // Copy data over because `ModuleLexer<'a>` can't be returned - ModuleLexer { - imports: module_lexer.imports.into_iter().map(Into::into).collect(), - exports: module_lexer.exports.into_iter().map(Into::into).collect(), - has_module_syntax: module_lexer.has_module_syntax, - facade: module_lexer.facade, - } -} diff --git a/crates/oxc_module_lexer/tests/integration/typescript.rs b/crates/oxc_module_lexer/tests/integration/typescript.rs deleted file mode 100644 index 7be8d39a60a7c..0000000000000 --- a/crates/oxc_module_lexer/tests/integration/typescript.rs +++ /dev/null @@ -1,69 +0,0 @@ -use super::ModuleLexer; - -use oxc_allocator::Allocator; -use oxc_parser::Parser; -use oxc_span::SourceType; - -fn parse(source: &str) -> ModuleLexer { - let allocator = Allocator::default(); - let source_type = SourceType::mjs().with_typescript_definition(true); - let ret = Parser::new(&allocator, source, source_type).parse(); - assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors); - let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); - ModuleLexer { - imports: module_lexer.imports.into_iter().map(Into::into).collect(), - exports: module_lexer.exports.into_iter().map(Into::into).collect(), - has_module_syntax: module_lexer.has_module_syntax, - facade: module_lexer.facade, - } -} - -#[test] -fn import_type_named() { - let source = "import type { foo } from 'foo'"; - let impt = &parse(source).imports[0]; - assert!(impt.t); -} - -#[test] -fn import_type_namespace() { - let source = "import type * as foo from 'foo'"; - let impt = &parse(source).imports[0]; - assert!(impt.t); -} - -#[test] -fn import_type_default() { - let source = "import type foo from 'foo'"; - let impt = &parse(source).imports[0]; - assert!(impt.t); -} - -#[test] -fn dynamic_import_value() { - let source = "import('foo')"; - let impt = &parse(source).imports[0]; - assert!(!impt.t); -} - -#[test] -fn dynamic_import_type() { - let source = "const foo: import('foo')"; - let impt = &parse(source).imports[0]; - assert!(impt.t); - assert_eq!(impt.n.as_ref().unwrap(), "foo"); -} - -#[test] -fn export_type_named() { - let source = "export type { foo } from 'foo'"; - let expt = &parse(source).exports[0]; - assert!(expt.t); -} - -#[test] -fn export_type_namespace() { - let source = "export type * as foo from 'foo'"; - let expt = &parse(source).exports[0]; - assert!(expt.t); -} diff --git a/napi/parser/Cargo.toml b/napi/parser/Cargo.toml index c524b64ea580f..d2e1b43b3775f 100644 --- a/napi/parser/Cargo.toml +++ b/napi/parser/Cargo.toml @@ -22,7 +22,6 @@ doctest = false [dependencies] oxc = { workspace = true, features = ["napi", "serialize", "parser"] } -oxc_module_lexer = { workspace = true } napi = { workspace = true, features = ["async"] } napi-derive = { workspace = true } diff --git a/napi/parser/bindings.js b/napi/parser/bindings.js index 2b09b39f8c35a..4e3a99cf03ac3 100644 --- a/napi/parser/bindings.js +++ b/napi/parser/bindings.js @@ -361,8 +361,6 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -module.exports.moduleLexerAsync = nativeBinding.moduleLexerAsync -module.exports.moduleLexerSync = nativeBinding.moduleLexerSync module.exports.parseAsync = nativeBinding.parseAsync module.exports.parseSync = nativeBinding.parseSync module.exports.parseWithoutReturn = nativeBinding.parseWithoutReturn diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index a36ba4bec6090..323408991814e 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -9,83 +9,6 @@ export interface Comment { end: number } -export interface ModuleLexer { - imports: Array - exports: Array - /** - * ESM syntax detection - * - * The use of ESM syntax: import / export statements and `import.meta` - */ - hasModuleSyntax: boolean - /** Facade modules that only use import / export syntax */ - facade: boolean -} - -/** - * # Panics - * - * * Tokio crashes - */ -export declare function moduleLexerAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise - -export interface ModuleLexerExportSpecifier { - /** Exported name */ - n: string - /** Local name, or undefined. */ - ln?: string - /** Start of exported name */ - s: number - /** End of exported name */ - e: number - /** Start of local name */ - ls?: number - /** End of local name */ - le?: number -} - -export interface ModuleLexerImportSpecifier { - /** - * Module name - * - * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. - * - * For dynamic import expressions, this field will be empty if not a valid JS string. - */ - n?: string - /** Start of module specifier */ - s: number - /** End of module specifier */ - e: number - /** Start of import statement */ - ss: number - /** End of import statement */ - se: number - /** - * Import Type - * * If this import keyword is a dynamic import, this is the start value. - * * If this import keyword is a static import, this is -1. - * * If this import keyword is an import.meta expression, this is -2. - * * If this import is an `export *`, this is -3. - */ - d: number - /** - * If this import has an import assertion, this is the start value - * Otherwise this is `-1`. - */ - a: number -} - -/** - * Outputs the list of exports and locations of import specifiers, - * including dynamic import and import meta handling. - * - * # Panics - * - * * File extension is invalid - */ -export declare function moduleLexerSync(sourceText: string, options?: ParserOptions | undefined | null): ModuleLexer - /** * # Panics * diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index 08add6965e2fd..102fa5437c6cd 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -1,5 +1,3 @@ -mod module_lexer; - use std::sync::Arc; use napi::{bindgen_prelude::AsyncTask, Task}; @@ -14,8 +12,6 @@ use oxc::{ span::SourceType, }; -pub use crate::module_lexer::*; - fn parse<'a>( allocator: &'a Allocator, source_text: &'a str, diff --git a/napi/parser/src/module_lexer.rs b/napi/parser/src/module_lexer.rs deleted file mode 100644 index 3a3f598d01dd7..0000000000000 --- a/napi/parser/src/module_lexer.rs +++ /dev/null @@ -1,169 +0,0 @@ -use napi::{bindgen_prelude::AsyncTask, Task}; -use napi_derive::napi; - -use oxc::allocator::Allocator; -use oxc_module_lexer::ImportType; - -use crate::{parse, ParserOptions}; - -#[napi(object)] -pub struct ModuleLexerImportSpecifier { - /// Module name - /// - /// To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. - /// - /// For dynamic import expressions, this field will be empty if not a valid JS string. - pub n: Option, - - /// Start of module specifier - pub s: u32, - - /// End of module specifier - pub e: u32, - - /// Start of import statement - pub ss: u32, - - /// End of import statement - pub se: u32, - - /// Import Type - /// * If this import keyword is a dynamic import, this is the start value. - /// * If this import keyword is a static import, this is -1. - /// * If this import keyword is an import.meta expression, this is -2. - /// * If this import is an `export *`, this is -3. - pub d: i64, - - /// If this import has an import assertion, this is the start value - /// Otherwise this is `-1`. - pub a: i64, -} - -#[napi(object)] -pub struct ModuleLexerExportSpecifier { - /// Exported name - pub n: String, - - /// Local name, or undefined. - pub ln: Option, - - /// Start of exported name - pub s: u32, - - /// End of exported name - pub e: u32, - - /// Start of local name - pub ls: Option, - - /// End of local name - pub le: Option, -} - -impl From> for ModuleLexerImportSpecifier { - #[allow(clippy::cast_lossless)] - fn from(i: oxc_module_lexer::ImportSpecifier) -> Self { - Self { - n: i.n.map(|n| n.to_string()), - s: i.s, - e: i.e, - ss: i.ss, - se: i.se, - d: match i.d { - ImportType::DynamicImport(start) => start as i64, - ImportType::StaticImport => -1, - ImportType::ImportMeta => -2, - ImportType::ExportStar => -3, - }, - a: i.a.map_or(-1, |a| a as i64), - } - } -} - -impl From> for ModuleLexerExportSpecifier { - fn from(e: oxc_module_lexer::ExportSpecifier) -> Self { - Self { - n: e.n.to_string(), - ln: e.ln.map(|ln| ln.to_string()), - s: e.s, - e: e.e, - ls: e.ls, - le: e.le, - } - } -} - -#[napi(object)] -pub struct ModuleLexer { - pub imports: Vec, - - pub exports: Vec, - - /// ESM syntax detection - /// - /// The use of ESM syntax: import / export statements and `import.meta` - pub has_module_syntax: bool, - - /// Facade modules that only use import / export syntax - pub facade: bool, -} - -#[allow(clippy::needless_pass_by_value)] -fn module_lexer(source_text: &str, options: &ParserOptions) -> ModuleLexer { - let allocator = Allocator::default(); - let ret = parse(&allocator, source_text, options); - let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); - let imports = module_lexer.imports.into_iter().map(ModuleLexerImportSpecifier::from).collect(); - let exports = module_lexer.exports.into_iter().map(ModuleLexerExportSpecifier::from).collect(); - ModuleLexer { - imports, - exports, - has_module_syntax: module_lexer.has_module_syntax, - facade: module_lexer.facade, - } -} - -/// Outputs the list of exports and locations of import specifiers, -/// including dynamic import and import meta handling. -/// -/// # Panics -/// -/// * File extension is invalid -#[napi] -#[allow(clippy::needless_pass_by_value)] -pub fn module_lexer_sync(source_text: String, options: Option) -> ModuleLexer { - let options = options.unwrap_or_default(); - module_lexer(&source_text, &options) -} - -pub struct ResolveTask { - source_text: String, - options: ParserOptions, -} - -#[napi] -impl Task for ResolveTask { - type JsValue = ModuleLexer; - type Output = ModuleLexer; - - fn compute(&mut self) -> napi::Result { - Ok(module_lexer(&self.source_text, &self.options)) - } - - fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result { - Ok(result) - } -} - -/// # Panics -/// -/// * Tokio crashes -#[napi] -#[allow(clippy::needless_pass_by_value)] -pub fn module_lexer_async( - source_text: String, - options: Option, -) -> AsyncTask { - let options = options.unwrap_or_default(); - AsyncTask::new(ResolveTask { source_text, options }) -} diff --git a/napi/parser/test/module_lexer.test.ts b/napi/parser/test/module_lexer.test.ts deleted file mode 100644 index fb266a50cfb98..0000000000000 --- a/napi/parser/test/module_lexer.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { assert, describe, expect, it } from 'vitest'; - -import * as oxc from '../index.js'; - -describe('module lexer', () => { - const code = 'export { foo }'; - - it('matches output', () => { - const ret = oxc.moduleLexerSync(code); - assert(ret.exports.length == 1); - }); - - it('matches output async', async () => { - const ret = await oxc.moduleLexerAsync(code); - assert(ret.exports.length == 1); - }); - - it('returns export *', async () => { - const ret = await oxc.moduleLexerAsync("export * from 'foo';"); - expect(ret).toEqual( - { - imports: [{ n: 'foo', s: 15, e: 18, ss: 0, se: 20, d: -3, a: -1 }], - exports: [], - hasModuleSyntax: true, - facade: true, - }, - ); - }); -});