Skip to content

Commit

Permalink
refactor: make SourceParser class heritable + create and use JsSource…
Browse files Browse the repository at this point in the history
…Parser in … (#215)

* refactor: make SourceParser class heritable + create and use JsSourceParser in runASTAnalysis

* chore: kParsingOptions is only for the JS parser
  • Loading branch information
jean-michelet authored Jan 25, 2024
1 parent d26eafc commit 83aa3d5
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 85 deletions.
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import isMinified from "is-minified-code";

// Import Internal Dependencies
import { SourceFile } from "./src/SourceFile.js";
import { SourceParser } from "./src/SourceParser.js";
import { warnings } from "./src/warnings.js";
import { isOneLineExpressionExport } from "./src/utils/index.js";
import { JsSourceParser } from "./src/JsSourceParser.js";

export function runASTAnalysis(
str,
Expand All @@ -22,7 +22,7 @@ export function runASTAnalysis(
removeHTMLComments = false
} = options;

const parser = new SourceParser(str, { removeHTMLComments });
const parser = new JsSourceParser(str, { removeHTMLComments });
const body = parser.parseScript({
isEcmaScriptModule: Boolean(module)
});
Expand Down
61 changes: 61 additions & 0 deletions src/JsSourceParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Import Third-party Dependencies
import * as meriyah from "meriyah";

// Import Internal Dependencies
import { SourceParser } from "./SourceParser.js";

// CONSTANTS
const kParsingOptions = {
next: true,
loc: true,
raw: true,
jsx: true
};

export class JsSourceParser extends SourceParser {
/**
* @param {object} options
* @param {boolean} options.isEcmaScriptModule
*/
parseScript(options = {}) {
const {
isEcmaScriptModule
} = options;

try {
const { body } = meriyah.parseScript(
this.source,
{
...kParsingOptions,
module: isEcmaScriptModule,
globalReturn: !isEcmaScriptModule
}
);

return body;
}
catch (error) {
const isIllegalReturn = error.description.includes("Illegal return statement");

if (error.name === "SyntaxError" && (
error.description.includes("The import keyword") ||
error.description.includes("The export keyword") ||
isIllegalReturn
)) {
const { body } = meriyah.parseScript(
this.source,
{
...kParsingOptions,
module: true,
globalReturn: isIllegalReturn
}
);

return body;
}

throw error;
}
}
}

57 changes: 0 additions & 57 deletions src/SourceParser.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
// Import Third-party Dependencies
import * as meriyah from "meriyah";

// CONSTANTS
const kParsingOptions = {
next: true,
loc: true,
raw: true,
jsx: true
};

export class SourceParser {
/**
* @param {!string} source
Expand Down Expand Up @@ -42,50 +31,4 @@ export class SourceParser {
#removeHTMLComment(str) {
return str.replaceAll(/<!--[\s\S]*?(?:-->)/g, "");
}

/**
* @param {object} options
* @param {boolean} options.isEcmaScriptModule
*/
parseScript(options = {}) {
const {
isEcmaScriptModule
} = options;

try {
const { body } = meriyah.parseScript(
this.source,
{
...kParsingOptions,
module: isEcmaScriptModule,
globalReturn: !isEcmaScriptModule
}
);

return body;
}
catch (error) {
const isIllegalReturn = error.description.includes("Illegal return statement");

if (error.name === "SyntaxError" && (
error.description.includes("The import keyword") ||
error.description.includes("The export keyword") ||
isIllegalReturn
)) {
const { body } = meriyah.parseScript(
this.source,
{
...kParsingOptions,
module: true,
globalReturn: isIllegalReturn
}
);

return body;
}

throw error;
}
}
}

33 changes: 33 additions & 0 deletions test/JsSourceParser.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Import Node.js Dependencies
import { describe, it } from "node:test";

// Import Internal Dependencies
import { JsSourceParser } from "../src/JsSourceParser.js";

describe("JsSourceParser", () => {
describe("parseScript", () => {
it("should not crash even if isEcmaScriptModule 'false' is provided (import keyword)", () => {
new JsSourceParser("import * as foo from \"foo\";").parseScript({
isEcmaScriptModule: false
});
});

it("should not crash even if isEcmaScriptModule 'false' is provided (export keyword)", () => {
new JsSourceParser("export const foo = 5;").parseScript({
isEcmaScriptModule: false
});
});

it("should not crash with a source code containing JSX", () => {
const code = `const Dropzone = forwardRef(({ children, ...params }, ref) => {
const { open, ...props } = useDropzone(params);
useImperativeHandle(ref, () => ({ open }), [open]);
return <Fragment>{children({ ...props, open })}</Fragment>;
});`;

new JsSourceParser(code).parseScript({
isEcmaScriptModule: false
});
});
});
});
26 changes: 0 additions & 26 deletions test/SourceParser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,4 @@ describe("SourceParser", () => {
assert.strictEqual(sp.source, "\nconst yo = 'foo'\n");
});
});

describe("parseScript", () => {
it("should not crash even if isEcmaScriptModule 'false' is provided (import keyword)", () => {
new SourceParser("import * as foo from \"foo\";").parseScript({
isEcmaScriptModule: false
});
});

it("should not crash even if isEcmaScriptModule 'false' is provided (export keyword)", () => {
new SourceParser("export const foo = 5;").parseScript({
isEcmaScriptModule: false
});
});

it("should not crash with a source code containing JSX", () => {
const code = `const Dropzone = forwardRef(({ children, ...params }, ref) => {
const { open, ...props } = useDropzone(params);
useImperativeHandle(ref, () => ({ open }), [open]);
return <Fragment>{children({ ...props, open })}</Fragment>;
});`;

new SourceParser(code).parseScript({
isEcmaScriptModule: false
});
});
});
});

0 comments on commit 83aa3d5

Please sign in to comment.