Skip to content

Commit

Permalink
fix(analyze): ignore conmments for imports detection (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
lishaobos authored Jan 11, 2024
1 parent c29618f commit d212da8
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 28 deletions.
58 changes: 33 additions & 25 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,17 @@ 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 findTypeImports(code: string): TypeImport[] {
Expand Down Expand Up @@ -217,17 +223,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];
Expand Down Expand Up @@ -266,17 +269,14 @@ export function findTypeExports(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];
Expand Down Expand Up @@ -359,23 +359,31 @@ 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<T extends TokenLocation>(
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",
Expand All @@ -385,7 +393,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,
Expand Down
36 changes: 33 additions & 3 deletions test/imports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,32 @@ staticTests['import { foo, type Foo } from "foo"'] = {
namedImports: { foo: "foo" },
};

staticTests[
`
// import { foo } from "foo"
import { too } from "too"
/**
* import { zoo } from "zoo"
*/
const start = '/*'
import { ioo } from "ioo"
const end = '*/'
`
] = [
{
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" );': {
Expand All @@ -153,6 +179,8 @@ const dynamicTests = {
'import("abc").then(r => r.default)': {
expression: '"abc"',
},
'// import("abc").then(r => r.default)': [],
'/* import("abc").then(r => r.default) */': [],
};

const TypeTests = {
Expand Down Expand Up @@ -237,10 +265,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);
}
});
}
});
Expand Down

0 comments on commit d212da8

Please sign in to comment.