Skip to content

Commit

Permalink
fix: use acorn to strip comments (unjs#279)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjpearson committed Oct 7, 2024
1 parent 918e835 commit e6d34a2
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 12 deletions.
48 changes: 36 additions & 12 deletions src/syntax.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { promises as fsp } from "node:fs";
import { parse } from "acorn";
import { extname } from "pathe";
import { readPackageJSON } from "pkg-types";
import { ResolveOptions, resolvePath } from "./resolve";
Expand All @@ -10,9 +11,6 @@ const ESM_RE =
const CJS_RE =
/([\s;]|^)(module.exports\b|exports\.\w|require\s*\(|global\.\w)/m;

const MULTI_LINE_COMMENT_RE = /\/\*.+?\*\//gs;
const SINGLE_LINE_COMMENT_RE = /\/\/.*/g;

const BUILTIN_EXTENSIONS = new Set([".mjs", ".cjs", ".node", ".wasm"]);

/**
Expand All @@ -26,6 +24,38 @@ export type DetectSyntaxOptions = {
stripComments?: boolean;
};

interface TokenLocation {
start: number;
end: number;
}

function _getCommentLocations(code: string) {
const locations: TokenLocation[] = [];
parse(code, {
ecmaVersion: "latest",
allowHashBang: true,
allowAwaitOutsideFunction: true,
allowImportExportEverywhere: true,
onComment(_isBlock, _text, start, end) {
locations.push({ start, end })
}
})
return locations;
}

/**
* Strip comments from a string of source code
*
* @param code - The source code to remove comments from.
*/
function stripComments(code: string) {
const locations = _getCommentLocations(code);
for (const location of locations.reverse()) {
code = code.slice(0, location.start) + code.slice(location.end)
}
return code
}

/**
* Determines if a given code string contains ECMAScript module syntax.
*
Expand All @@ -38,9 +68,7 @@ export function hasESMSyntax(
opts: DetectSyntaxOptions = {},
): boolean {
if (opts.stripComments) {
code = code
.replace(SINGLE_LINE_COMMENT_RE, "")
.replace(MULTI_LINE_COMMENT_RE, "");
code = stripComments(code);
}
return ESM_RE.test(code);
}
Expand All @@ -57,9 +85,7 @@ export function hasCJSSyntax(
opts: DetectSyntaxOptions = {},
): boolean {
if (opts.stripComments) {
code = code
.replace(SINGLE_LINE_COMMENT_RE, "")
.replace(MULTI_LINE_COMMENT_RE, "");
code = stripComments(code);
}
return CJS_RE.test(code);
}
Expand All @@ -73,9 +99,7 @@ export function hasCJSSyntax(
*/
export function detectSyntax(code: string, opts: DetectSyntaxOptions = {}) {
if (opts.stripComments) {
code = code
.replace(SINGLE_LINE_COMMENT_RE, "")
.replace(MULTI_LINE_COMMENT_RE, "");
code = stripComments(code);
}
// We strip comments once hence not passing opts down to hasESMSyntax and hasCJSSyntax
const hasESM = hasESMSyntax(code, {});
Expand Down
5 changes: 5 additions & 0 deletions test/syntax.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ const staticTestsWithComments = {
{ hasESM: false, hasCJS: true, isMixed: false },
"/* export * */": { hasESM: false, hasCJS: false, isMixed: false },
"/* \n export * \n */": { hasESM: false, hasCJS: false, isMixed: false },
"/* \n export * \n */ export * from 'foo' /* \n export * \n */": {
hasESM: true,
hasCJS: false,
isMixed: false,
},
};

describe("detectSyntax", () => {
Expand Down

0 comments on commit e6d34a2

Please sign in to comment.