diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cac104e1..3246153b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,11 +1,29 @@ { - "tasks": [{ - "type": "shell", - "label": "cargo test build", - "command": "cargo", - "args": ["test", "--no-run"], - "problemMatcher": [ - "$rustc" - ] - }] + "tasks": [ + { + "type": "shell", + "label": "cargo test build", + "command": "cargo", + "args": [ + "test", + "--no-run" + ], + "problemMatcher": [ + "$rustc" + ] + }, + { + "type": "cargo", + "label": "rust: cargo test", + "command": "test", + "problemMatcher": [ + "$rustc", + "$rust-panic" + ], + "group": { + "kind": "test", + "isDefault": true + } + } + ] } diff --git a/src/generation/sorting/module_specifiers.rs b/src/generation/sorting/module_specifiers.rs index a621db07..0da5e805 100644 --- a/src/generation/sorting/module_specifiers.rs +++ b/src/generation/sorting/module_specifiers.rs @@ -1,39 +1,45 @@ use std::cmp::Ordering; +use ModuleSpecifierInfo::*; pub fn cmp_module_specifiers(a: &str, b: &str, cmp_text: impl Fn(&str, &str) -> Ordering) -> Ordering { let a_info = get_module_specifier_info(a); let b_info = get_module_specifier_info(b); + cmp_module_spec_info(&a_info, &b_info, cmp_text) +} + +fn cmp_module_spec_info(a: &ModuleSpecifierInfo, b: &ModuleSpecifierInfo, cmp_text: impl Fn(&str, &str) -> Ordering) -> Ordering { + match (a, b) { + (Node_ { text: lhs }, Node_ { text: rhs }) => cmp_text(lhs, rhs), + (Node_ { .. }, _) => Ordering::Less, + (_, Node_ { .. }) => Ordering::Greater, + + (Npm_ { text: lhs }, Npm_ { text: rhs }) => cmp_text(lhs, rhs), + (Npm_ { .. }, _) => Ordering::Less, + (_, Npm_ { .. }) => Ordering::Greater, - match &a_info { - ModuleSpecifierInfo::Absolute { text: a_text } => match b_info { - ModuleSpecifierInfo::Absolute { text: b_text } => cmp_text(a_text, b_text), - ModuleSpecifierInfo::Relative { .. } => Ordering::Less, - }, - ModuleSpecifierInfo::Relative { - relative_count: a_relative_count, - folder_text: a_folder_text, - } => match &b_info { - ModuleSpecifierInfo::Absolute { .. } => Ordering::Greater, - ModuleSpecifierInfo::Relative { - relative_count: b_relative_count, - folder_text: b_folder_text, - } => match a_relative_count.cmp(b_relative_count) { - Ordering::Greater => Ordering::Less, - Ordering::Less => Ordering::Greater, - Ordering::Equal => cmp_text(a_folder_text, b_folder_text), - }, - }, + (Absolute { text: lhs }, Absolute { text: rhs }) => cmp_text(lhs, rhs), + (Absolute { .. }, _) => Ordering::Less, + (_, Absolute { .. }) => Ordering::Greater, + + (Relative { count: a_count, text: lhs }, Relative { count: b_count, text: rhs }) => b_count.cmp(a_count).then(cmp_text(lhs, rhs)), } } #[derive(Debug, PartialEq)] enum ModuleSpecifierInfo<'a> { Absolute { text: &'a str }, - Relative { relative_count: usize, folder_text: &'a str }, + Relative { count: usize, text: &'a str }, + Npm_ { text: &'a str }, + Node_ { text: &'a str }, } fn get_module_specifier_info(text: &str) -> ModuleSpecifierInfo<'_> { let no_quotes_text = &text[1..text.len() - 1]; + if let Some(text) = no_quotes_text.strip_prefix("npm:") { + return Npm_ { text }; + } else if let Some(text) = no_quotes_text.strip_prefix("node:") { + return Node_ { text }; + } let parts = no_quotes_text.split('/').collect::>(); if parts[0] == "." || parts[0] == ".." { let mut relative_count = 0; @@ -51,12 +57,12 @@ fn get_module_specifier_info(text: &str) -> ModuleSpecifierInfo<'_> { start_index += part.len() + next_slash_width; } - ModuleSpecifierInfo::Relative { - relative_count, - folder_text: &no_quotes_text[start_index..], + Relative { + count: relative_count, + text: &no_quotes_text[start_index..], } } else { - ModuleSpecifierInfo::Absolute { text: no_quotes_text } + Absolute { text: no_quotes_text } } } @@ -71,6 +77,7 @@ mod test { assert_eq!(cmp_module_specifiers("'b'", "'a'", |a, b| a.cmp(b)), Ordering::Greater); assert_eq!(cmp_module_specifiers("'a'", "'a'", |a, b| a.cmp(b)), Ordering::Equal); assert_eq!(cmp_module_specifiers("'a'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'./a'", "'a'", |a, b| a.cmp(b)), Ordering::Greater); assert_eq!(cmp_module_specifiers("'./a'", "'./a'", |a, b| a.cmp(b)), Ordering::Equal); assert_eq!(cmp_module_specifiers("'../a'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); @@ -78,61 +85,65 @@ mod test { assert_eq!(cmp_module_specifiers("'../../a'", "'../a'", |a, b| a.cmp(b)), Ordering::Less); assert_eq!(cmp_module_specifiers("'../a'", "'../../a'", |a, b| a.cmp(b)), Ordering::Greater); assert_eq!(cmp_module_specifiers("'..'", "'test'", |a, b| a.cmp(b)), Ordering::Greater); + + assert_eq!(cmp_module_specifiers("'npm:a'", "''", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:b'", "'a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'./b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:b'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'npm:b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'npm:a'", "'npm:a'", |a, b| a.cmp(b)), Ordering::Equal); + assert_eq!(cmp_module_specifiers("'npm:b'", "'npm:a'", |a, b| a.cmp(b)), Ordering::Greater); + assert_eq!(cmp_module_specifiers("'npm:a'", "'node:a'", |a, b| a.cmp(b)), Ordering::Greater); + assert_eq!(cmp_module_specifiers("'npm:a'", "'node:b'", |a, b| a.cmp(b)), Ordering::Greater); + assert_eq!(cmp_module_specifiers("'npm:b'", "'node:a'", |a, b| a.cmp(b)), Ordering::Greater); + + assert_eq!(cmp_module_specifiers("'node:a'", "''", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:b'", "'a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'./b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:b'", "'./a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'npm:b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'npm:a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:b'", "'npm:a'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:a'", "'node:a'", |a, b| a.cmp(b)), Ordering::Equal); + assert_eq!(cmp_module_specifiers("'node:a'", "'node:b'", |a, b| a.cmp(b)), Ordering::Less); + assert_eq!(cmp_module_specifiers("'node:b'", "'node:a'", |a, b| a.cmp(b)), Ordering::Greater); } #[test] fn it_should_get_module_specifier_info_when_empty() { - let result = get_module_specifier_info("''"); - assert_eq!(result, ModuleSpecifierInfo::Absolute { text: "" }); + assert_eq!(get_module_specifier_info("''"), Absolute { text: "" }); + assert_eq!(get_module_specifier_info("\"\""), Absolute { text: "" }); } #[test] fn it_should_get_module_specifier_info_for_absolute() { - // double quotes - let result = get_module_specifier_info(r#""testing/asdf""#); - assert_eq!(result, ModuleSpecifierInfo::Absolute { text: "testing/asdf" }); - - // single quotes - let result = get_module_specifier_info("'testing'"); - assert_eq!(result, ModuleSpecifierInfo::Absolute { text: "testing" }); + assert_eq!(get_module_specifier_info("'testing'"), Absolute { text: "testing" }); + assert_eq!(get_module_specifier_info("\"testing/asdf\""), Absolute { text: "testing/asdf" }); } #[test] fn it_should_get_module_specifier_info_for_relative() { - let result = get_module_specifier_info("'./a'"); - assert_eq!( - result, - ModuleSpecifierInfo::Relative { - relative_count: 0, - folder_text: "a", - } - ); - - let result = get_module_specifier_info("'./../testing-test/t'"); - assert_eq!( - result, - ModuleSpecifierInfo::Relative { - relative_count: 1, - folder_text: "testing-test/t", - } - ); - - let result = get_module_specifier_info("'../asdf'"); - assert_eq!( - result, - ModuleSpecifierInfo::Relative { - relative_count: 1, - folder_text: "asdf", - } - ); - - let result = get_module_specifier_info("'../../../test'"); - assert_eq!( - result, - ModuleSpecifierInfo::Relative { - relative_count: 3, - folder_text: "test", - } - ); + assert_eq!(get_module_specifier_info("'./a'"), Relative { count: 0, text: "a" }); + assert_eq!(get_module_specifier_info("'./../ab-cd/t'"), Relative { count: 1, text: "ab-cd/t" }); + assert_eq!(get_module_specifier_info("'../asdf'"), Relative { count: 1, text: "asdf" }); + assert_eq!(get_module_specifier_info("'../../../test'"), Relative { count: 3, text: "test" }); + } + + #[test] + fn it_should_get_module_specifier_info_for_npm() { + assert_eq!(get_module_specifier_info("'npm:foo'"), Npm_ { text: "foo" }); + assert_eq!(get_module_specifier_info("\"npm:foo/bar\""), Npm_ { text: "foo/bar" }); + } + + #[test] + fn it_should_get_module_specifier_info_for_node() { + assert_eq!(get_module_specifier_info("'node:foo'"), Node_ { text: "foo" }); + assert_eq!(get_module_specifier_info("\"node:foo/bar\""), Node_ { text: "foo/bar" }); } } diff --git a/tests/specs/declarations/module/Module_SortExportDeclarations_Node.txt b/tests/specs/declarations/module/Module_SortExportDeclarations_Node.txt new file mode 100644 index 00000000..cbcb1887 --- /dev/null +++ b/tests/specs/declarations/module/Module_SortExportDeclarations_Node.txt @@ -0,0 +1,23 @@ +== should put node exports first == +export { a } from "./test"; +export { b } from "npm:test"; +export { c } from "test"; +export { d } from "node:test"; +export { e } from "node:test"; + +[expect] +export { d } from "node:test"; +export { e } from "node:test"; +export { b } from "npm:test"; +export { c } from "test"; +export { a } from "./test"; + +== should sort node exports by suffix == +export { a } from "node:bbb"; +export { b } from "node:aaa"; +export { c } from "node:ccc"; + +[expect] +export { b } from "node:aaa"; +export { a } from "node:bbb"; +export { c } from "node:ccc"; diff --git a/tests/specs/declarations/module/Module_SortExportDeclarations_Npm.txt b/tests/specs/declarations/module/Module_SortExportDeclarations_Npm.txt new file mode 100644 index 00000000..bd8017a2 --- /dev/null +++ b/tests/specs/declarations/module/Module_SortExportDeclarations_Npm.txt @@ -0,0 +1,21 @@ +== should put npm exports first == +export { a } from "./test"; +export { b } from "test"; +export { c } from "npm:test"; +export { d } from "npm:test"; + +[expect] +export { c } from "npm:test"; +export { d } from "npm:test"; +export { b } from "test"; +export { a } from "./test"; + +== should sort npm exports by suffix == +export { c } from "npm:ccc"; +export { a } from "npm:aaa"; +export { b } from "npm:bbb"; + +[expect] +export { a } from "npm:aaa"; +export { b } from "npm:bbb"; +export { c } from "npm:ccc"; diff --git a/tests/specs/declarations/module/Module_SortImportDeclarations_Node.txt b/tests/specs/declarations/module/Module_SortImportDeclarations_Node.txt new file mode 100644 index 00000000..dac7bee9 --- /dev/null +++ b/tests/specs/declarations/module/Module_SortImportDeclarations_Node.txt @@ -0,0 +1,23 @@ +== should put node imports first == +import a from "./test"; +import b from "npm:test"; +import c from "test"; +import d from "node:test"; +import e from "node:test"; + +[expect] +import d from "node:test"; +import e from "node:test"; +import b from "npm:test"; +import c from "test"; +import a from "./test"; + +== should sort node imports by suffix == +import a from "node:bbb"; +import b from "node:aaa"; +import c from "node:ccc"; + +[expect] +import b from "node:aaa"; +import a from "node:bbb"; +import c from "node:ccc"; diff --git a/tests/specs/declarations/module/Module_SortImportDeclarations_Npm.txt b/tests/specs/declarations/module/Module_SortImportDeclarations_Npm.txt new file mode 100644 index 00000000..f8849a06 --- /dev/null +++ b/tests/specs/declarations/module/Module_SortImportDeclarations_Npm.txt @@ -0,0 +1,21 @@ +== should put npm imports first == +import a from "./test"; +import b from "test"; +import c from "npm:test"; +import d from "npm:test"; + +[expect] +import c from "npm:test"; +import d from "npm:test"; +import b from "test"; +import a from "./test"; + +== should sort npm imports by suffix == +import a from "npm:bbb"; +import b from "npm:aaa"; +import c from "npm:ccc"; + +[expect] +import b from "npm:aaa"; +import a from "npm:bbb"; +import c from "npm:ccc";