Skip to content

Commit

Permalink
manual rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Jan 11, 2024
1 parent c29618f commit 3318e45
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 28 deletions.
67 changes: 39 additions & 28 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ export interface TypeImport extends Omit<ESMImport, "type"> {
}

export interface ESMExport {
_type?: "declaration" | "named" | "default" | "star";
type: "declaration" | "named" | "default" | "star";
_type?: "declaration" | "named" | "default" | "namedDefault" | "star";
type: "declaration" | "named" | "default" | "namedDefault" | "star";
code: string;
start: number;
end: number;
name?: string;
defaultName?: string;
names: string[];
specifier?: string;
}
Expand Down Expand Up @@ -80,15 +81,22 @@ const EXPORT_NAMED_DESTRUCT =
/\bexport\s+(let|var|const)\s+(?:{(?<exports1>[^}]+?)[\s,]*}|\[(?<exports2>[^\]]+?)[\s,]*])\s+=/gm;
const EXPORT_STAR_RE =
/\bexport\s*(\*)(\s*as\s+(?<name>[\w$]+)\s+)?\s*(\s*from\s*["']\s*(?<specifier>(?<="\s*)[^"]*[^\s"](?=\s*")|(?<='\s*)[^']*[^\s'](?=\s*'))\s*["'][^\n;]*)?/g;
const EXPORT_DEFAULT_RE = /\bexport\s+default\s+/g;
const EXPORT_DEFAULT_RE =
/\bexport\s+default\s+(async function|function|class|true|false|(\W\D)|\d)|\bexport\s+default\s+(?!async function|function|class|true|false|\W|\d)(?<defaultName>\w+)/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 @@ -177,6 +185,7 @@ export function findExports(code: string): ESMExport[] {
code,
{ type: "named" },
);

for (const namedExport of destructuredExports) {
// @ts-expect-error groups
namedExport.exports = namedExport.exports1 || namedExport.exports2;
Expand Down Expand Up @@ -217,17 +226,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 +272,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 +362,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 +396,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
9 changes: 9 additions & 0 deletions test/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ describe("findExports", () => {
"export async function* foo ()": { type: "declaration", names: ["foo"] },
"export async function *foo ()": { type: "declaration", names: ["foo"] },
"export const $foo = () => {}": { type: "declaration", names: ["$foo"] },
"export default something": {
type: "default",
name: "default",
defaultName: "something",
names: ["default"],
},
"export { foo as default }": {
type: "default",
name: "default",
Expand Down Expand Up @@ -95,6 +101,9 @@ describe("findExports", () => {
if (test.specifier) {
expect(match.specifier).toEqual(test.specifier);
}
if (test.defaultName) {
expect(match.defaultName).toEqual(test.defaultName);
}
});
}
it("handles multiple exports", () => {
Expand Down

0 comments on commit 3318e45

Please sign in to comment.