From 3f1c95af6d3d73f91ea999e2d6246f52641cabdd Mon Sep 17 00:00:00 2001 From: lishaobos Date: Wed, 1 Mar 2023 17:05:37 +0800 Subject: [PATCH 1/4] fix: ignore match comments --- src/_utils.ts | 19 +++++++++++++------ test/imports.test.ts | 31 ++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 89820be..62f5de0 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -28,15 +28,22 @@ export function isObject(value) { } export function matchAll(regex, string, addition) { - const matches = []; - for (const match of string.matchAll(regex)) { - matches.push({ + const comments = [...string.matchAll(/\/\/.*|\/\*[\S\s]*?\*\//g)].map( + (match) => [match.index, match.index + match[0].length] + ); + + return [...string.matchAll(regex)] + .filter( + (match) => + !comments.some( + ([start, end]) => start <= match.index && match.index <= end + ) + ) + .map((match) => ({ ...addition, ...match.groups, code: match[0], start: match.index, end: match.index + match[0].length, - }); - } - return matches; + })); } diff --git a/test/imports.test.ts b/test/imports.test.ts index 3426937..b3e58ca 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -120,6 +120,28 @@ staticTests['import { foo, type Foo } from "foo"'] = { namedImports: { foo: "foo" }, }; +staticTests[` + // import { foo } from "foo" + import { too } from "too" + + /** + * import { zoo } from "zoo" + */ + + import { ioo } from "ioo" +`] = [ + { + specifier: "too", + type: "static", + namedImports: { too: 'too' }, + }, + { + specifier: "ioo", + type: "static", + namedImports: { ioo: 'ioo' }, + } +]; + // -- Dynamic import -- const dynamicTests = { 'const { test, /* here */, another, } = await import ( "module-name" );': { @@ -137,6 +159,7 @@ const dynamicTests = { 'import("abc").then(r => r.default)': { expression: '"abc"', }, + '// import("abc").then(r => r.default)': [], }; describe("findStaticImports", () => { @@ -170,10 +193,12 @@ describe("findDynamicImports", () => { for (const [input, test] of Object.entries(dynamicTests)) { it(input.replace(/\n/g, "\\n"), () => { const matches = findDynamicImports(input); - expect(matches.length).to.equal(1); + expect(matches.length).to.equal(Array.isArray(test) ? test.length : 1); const match = matches[0]; - expect(match.type).to.equal("dynamic"); - expect(match.expression.trim()).to.equal(test.expression); + if (match) { + expect(match.type).to.equal("dynamic"); + expect(match.expression.trim()).to.equal(test.expression); + } }); } }); From b59ff6dd301c7e5aace29c88e0fbdcb7f9768c79 Mon Sep 17 00:00:00 2001 From: lishaobos Date: Fri, 3 Mar 2023 10:44:37 +0800 Subject: [PATCH 2/4] fix: use acorn match import statement --- src/_utils.ts | 19 ++++++------------- src/analyze.ts | 39 +++++++++++++++++++-------------------- test/imports.test.ts | 3 +++ 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 62f5de0..89820be 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -28,22 +28,15 @@ export function isObject(value) { } export function matchAll(regex, string, addition) { - const comments = [...string.matchAll(/\/\/.*|\/\*[\S\s]*?\*\//g)].map( - (match) => [match.index, match.index + match[0].length] - ); - - return [...string.matchAll(regex)] - .filter( - (match) => - !comments.some( - ([start, end]) => start <= match.index && match.index <= end - ) - ) - .map((match) => ({ + const matches = []; + for (const match of string.matchAll(regex)) { + matches.push({ ...addition, ...match.groups, code: match[0], start: match.index, end: match.index + match[0].length, - })); + }); + } + return matches; } diff --git a/src/analyze.ts b/src/analyze.ts index eec5b10..4a2f41f 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -72,11 +72,11 @@ const EXPORT_DEFAULT_RE = /\bexport\s+default\s+/g; const TYPE_RE = /^\s*?type\s/; export function findStaticImports(code: string): StaticImport[] { - return matchAll(ESM_STATIC_IMPORT_RE, code, { type: "static" }); + return _filterStatement(_tryGetLocations(code, 'import'), matchAll(ESM_STATIC_IMPORT_RE, code, { type: "static" })); } export function findDynamicImports(code: string): DynamicImport[] { - return matchAll(DYNAMIC_IMPORT_RE, code, { type: "dynamic" }); + return _filterStatement(_tryGetLocations(code, 'import'), matchAll(DYNAMIC_IMPORT_RE, code, { type: "dynamic" })); } export function parseStaticImport(matched: StaticImport): ParsedStaticImport { @@ -185,17 +185,14 @@ export function findExports(code: string): ESMExport[] { if (exports.length === 0) { return []; } - const exportLocations = _tryGetExportLocations(code); + const exportLocations = _tryGetLocations(code, 'export'); if (exportLocations && exportLocations.length === 0) { return []; } return ( - exports - // Filter false positive export matches - .filter( - (exp) => !exportLocations || _isExportStatement(exportLocations, exp) - ) + // Filter false positive export matches + _filterStatement(exportLocations, exports) // Prevent multiple exports of same function, only keep latest iteration of signatures .filter((exp, index, exports) => { const nextExport = exports[index + 1]; @@ -251,23 +248,25 @@ interface TokenLocation { end: number; } -function _isExportStatement(exportsLocation: TokenLocation[], exp: ESMExport) { - return exportsLocation.some((location) => { - // AST token inside the regex match - return exp.start <= location.start && exp.end >= location.end; - // AST Token start or end is within the regex match - // return (exp.start <= location.start && location.start <= exp.end) || - // (exp.start <= location.end && location.end <= exp.end) - }); +function _filterStatement(locations: TokenLocation[] | undefined, statements: T[]): T[] { + return statements.filter(exp => { + return !locations || locations.some((location) => { + // AST token inside the regex match + return exp.start <= location.start && exp.end >= location.end; + // AST Token start or end is within the regex match + // return (exp.start <= location.start && location.start <= exp.end) || + // (exp.start <= location.end && location.end <= exp.end) + }); + }) } -function _tryGetExportLocations(code: string) { +function _tryGetLocations(code: string, label: string) { try { - return _getExportLocations(code); + return _getLocations(code, label); } catch {} } -function _getExportLocations(code: string) { +function _getLocations(code: string, label: string) { const tokens = tokenizer(code, { ecmaVersion: "latest", sourceType: "module", @@ -277,7 +276,7 @@ function _getExportLocations(code: string) { }); const locations: TokenLocation[] = []; for (const token of tokens) { - if (token.type.label === "export") { + if (token.type.label === label) { locations.push({ start: token.start, end: token.end, diff --git a/test/imports.test.ts b/test/imports.test.ts index b3e58ca..22b0664 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -128,7 +128,9 @@ staticTests[` * import { zoo } from "zoo" */ + const start = '/*' import { ioo } from "ioo" + const end = '*/' `] = [ { specifier: "too", @@ -160,6 +162,7 @@ const dynamicTests = { expression: '"abc"', }, '// import("abc").then(r => r.default)': [], + '/* import("abc").then(r => r.default) */': [], }; describe("findStaticImports", () => { From fd0761b25b2d9f4794bbd656334aecc496559e06 Mon Sep 17 00:00:00 2001 From: lishaobos Date: Sat, 11 Mar 2023 21:06:41 +0800 Subject: [PATCH 3/4] fix: code format --- src/analyze.ts | 38 +++++++++++++++++++++++++------------- test/imports.test.ts | 12 +++++++----- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/analyze.ts b/src/analyze.ts index 4a2f41f..6d42804 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -72,11 +72,17 @@ const EXPORT_DEFAULT_RE = /\bexport\s+default\s+/g; const TYPE_RE = /^\s*?type\s/; export function findStaticImports(code: string): StaticImport[] { - return _filterStatement(_tryGetLocations(code, 'import'), matchAll(ESM_STATIC_IMPORT_RE, code, { type: "static" })); + return _filterStatement( + _tryGetLocations(code, "import"), + matchAll(ESM_STATIC_IMPORT_RE, code, { type: "static" }) + ); } export function findDynamicImports(code: string): DynamicImport[] { - return _filterStatement(_tryGetLocations(code, 'import'), matchAll(DYNAMIC_IMPORT_RE, code, { type: "dynamic" })); + return _filterStatement( + _tryGetLocations(code, "import"), + matchAll(DYNAMIC_IMPORT_RE, code, { type: "dynamic" }) + ); } export function parseStaticImport(matched: StaticImport): ParsedStaticImport { @@ -185,7 +191,7 @@ export function findExports(code: string): ESMExport[] { if (exports.length === 0) { return []; } - const exportLocations = _tryGetLocations(code, 'export'); + const exportLocations = _tryGetLocations(code, "export"); if (exportLocations && exportLocations.length === 0) { return []; } @@ -248,16 +254,22 @@ interface TokenLocation { end: number; } -function _filterStatement(locations: TokenLocation[] | undefined, statements: T[]): T[] { - return statements.filter(exp => { - return !locations || locations.some((location) => { - // AST token inside the regex match - return exp.start <= location.start && exp.end >= location.end; - // AST Token start or end is within the regex match - // return (exp.start <= location.start && location.start <= exp.end) || - // (exp.start <= location.end && location.end <= exp.end) - }); - }) +function _filterStatement( + locations: TokenLocation[] | undefined, + statements: T[] +): T[] { + return statements.filter((exp) => { + return ( + !locations || + locations.some((location) => { + // AST token inside the regex match + return exp.start <= location.start && exp.end >= location.end; + // AST Token start or end is within the regex match + // return (exp.start <= location.start && location.start <= exp.end) || + // (exp.start <= location.end && location.end <= exp.end) + }) + ); + }); } function _tryGetLocations(code: string, label: string) { diff --git a/test/imports.test.ts b/test/imports.test.ts index 22b0664..77b4fff 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -120,7 +120,8 @@ staticTests['import { foo, type Foo } from "foo"'] = { namedImports: { foo: "foo" }, }; -staticTests[` +staticTests[ + ` // import { foo } from "foo" import { too } from "too" @@ -131,17 +132,18 @@ staticTests[` const start = '/*' import { ioo } from "ioo" const end = '*/' -`] = [ +` +] = [ { specifier: "too", type: "static", - namedImports: { too: 'too' }, + namedImports: { too: "too" }, }, { specifier: "ioo", type: "static", - namedImports: { ioo: 'ioo' }, - } + namedImports: { ioo: "ioo" }, + }, ]; // -- Dynamic import -- From f70b3bd6b08637a0c66b91c7749a9a6a6e2fbe7a Mon Sep 17 00:00:00 2001 From: lishaobos Date: Mon, 13 Mar 2023 14:03:33 +0800 Subject: [PATCH 4/4] fix: resolve merge error --- src/analyze.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/analyze.ts b/src/analyze.ts index 5c80f89..ac3bbc6 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -178,17 +178,14 @@ export function findExports(code: string): ESMExport[] { if (exports.length === 0) { return []; } - const exportLocations = _tryGetExportLocations(code); + const exportLocations = _tryGetLocations(code, "export"); if (exportLocations && exportLocations.length === 0) { return []; } return ( - exports - // Filter false positive export matches - .filter( - (exp) => !exportLocations || _isExportStatement(exportLocations, exp) - ) + // Filter false positive export matches + _filterStatement(exportLocations, exports) // Prevent multiple exports of same function, only keep latest iteration of signatures .filter((exp, index, exports) => { const nextExport = exports[index + 1];