From 14af29567d6ceee59460fa63aac55cf0a3446e88 Mon Sep 17 00:00:00 2001 From: jean Date: Tue, 19 Mar 2024 14:05:02 +0100 Subject: [PATCH 1/4] feat: keep track of analyzed file to increase performance --- src/AstAnalyser.js | 20 +++++++++++++++++--- test/AstAnalyser.spec.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/AstAnalyser.js b/src/AstAnalyser.js index d957699..aca314c 100644 --- a/src/AstAnalyser.js +++ b/src/AstAnalyser.js @@ -18,6 +18,11 @@ export class AstAnalyser { */ constructor(parser = new JsSourceParser()) { this.parser = parser; + this.analyzedFiles = new Map(); + } + + reset() { + this.analyzedFiles = new Map(); } analyse(str, options = Object.create(null)) { @@ -59,6 +64,13 @@ export class AstAnalyser { pathToFile, options = {} ) { + const filePath = pathToFile instanceof URL ? pathToFile.href.replace(/^file:\/\//, "") : pathToFile; + const filePathString = path.resolve(filePath); + + if (this.analyzedFiles.has(filePathString)) { + return this.analyzedFiles.get(filePathString); + } + try { const { packageName = null, @@ -67,8 +79,6 @@ export class AstAnalyser { } = options; const str = await fs.readFile(pathToFile, "utf-8"); - const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile; - const isMin = filePathString.includes(".min") || isMinified(str); const data = this.analyse(str, { isMinified: isMin, @@ -80,12 +90,16 @@ export class AstAnalyser { data.dependencies.delete(packageName); } - return { + const report = { ok: true, dependencies: data.dependencies, warnings: data.warnings, isMinified: !data.isOneLineRequire && isMin }; + + this.analyzedFiles.set(filePathString, report); + + return report; } catch (error) { return { diff --git a/test/AstAnalyser.spec.js b/test/AstAnalyser.spec.js index 3b792c3..069fc09 100644 --- a/test/AstAnalyser.spec.js +++ b/test/AstAnalyser.spec.js @@ -160,6 +160,44 @@ describe("AstAnalyser", (t) => { ); }); + it("should not analyze the same file twice", async(t) => { + const analyser = new AstAnalyser(new JsSourceParser()); + + t.mock.method(AstAnalyser.prototype, "analyse"); + + const result1 = await analyser.analyseFile( + new URL("depName.js", FIXTURE_URL), + { module: false, packageName: "foobar" } + ); + + const result2 = await analyser.analyseFile( + new URL("depName.js", FIXTURE_URL), + { module: false, packageName: "foobar" } + ); + + const result3 = await analyser.analyseFile( + "test/fixtures/searchRuntimeDependencies/depName.js", + { module: false, packageName: "foobar" } + ); + + assert.deepEqual(result1, result2); + assert.deepEqual(result2, result3); + + let calls = AstAnalyser.prototype.analyse.mock.calls; + assert.strictEqual(calls.length, 1); + + // should remove the cache and call AstAnalyser.analyse again + analyser.reset(); + + await analyser.analyseFile( + "test/fixtures/searchRuntimeDependencies/depName.js", + { module: false, packageName: "foobar" } + ); + + calls = AstAnalyser.prototype.analyse.mock.calls; + assert.strictEqual(calls.length, 2); + }); + it("should fail with a parsing error", async() => { const result = await getAnalyser().analyseFile( new URL("parsingError.js", FIXTURE_URL), From 9a4eeadf010de7fe3ceeeac95de379bc4ae65f3d Mon Sep 17 00:00:00 2001 From: jean Date: Tue, 19 Mar 2024 18:19:31 +0100 Subject: [PATCH 2/4] feat: should skip files that are not part of entry files --- src/AstAnalyser.js | 22 +++++++++++++- test/AstAnalyser.spec.js | 38 ++++++++++++++++++++++++ test/fixtures/entryFiles/src/bar/bar.js | 1 + test/fixtures/entryFiles/src/foo.js | 1 + test/fixtures/entryFiles/src2/bar/bar.js | 1 + test/fixtures/entryFiles/src2/foo.js | 1 + test/fixtures/entryFiles/test/bar/bar.js | 1 + test/fixtures/entryFiles/test/foo.js | 1 + 8 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/entryFiles/src/bar/bar.js create mode 100644 test/fixtures/entryFiles/src/foo.js create mode 100644 test/fixtures/entryFiles/src2/bar/bar.js create mode 100644 test/fixtures/entryFiles/src2/foo.js create mode 100644 test/fixtures/entryFiles/test/bar/bar.js create mode 100644 test/fixtures/entryFiles/test/foo.js diff --git a/src/AstAnalyser.js b/src/AstAnalyser.js index aca314c..8e5f841 100644 --- a/src/AstAnalyser.js +++ b/src/AstAnalyser.js @@ -16,9 +16,10 @@ export class AstAnalyser { * @constructor * @param { SourceParser } [parser] */ - constructor(parser = new JsSourceParser()) { + constructor(parser = new JsSourceParser(), entryFiles = []) { this.parser = parser; this.analyzedFiles = new Map(); + this.entryFiles = entryFiles.map((entry) => path.resolve(entry)); } reset() { @@ -71,6 +72,11 @@ export class AstAnalyser { return this.analyzedFiles.get(filePathString); } + const toSkip = this.#toSkip(filePathString); + if (toSkip) { + return { ok: true, isSkipped: true }; + } + try { const { packageName = null, @@ -141,4 +147,18 @@ export class AstAnalyser { #removeHTMLComment(str) { return str.replaceAll(/)/g, ""); } + + #toSkip(filePathString) { + if (!this.entryFiles.length) { + return false; + } + + for (const dir of this.entryFiles) { + if (filePathString.startsWith(dir)) { + return false; + } + } + + return true; + } } diff --git a/test/AstAnalyser.spec.js b/test/AstAnalyser.spec.js index 069fc09..cd42dd7 100644 --- a/test/AstAnalyser.spec.js +++ b/test/AstAnalyser.spec.js @@ -198,6 +198,44 @@ describe("AstAnalyser", (t) => { assert.strictEqual(calls.length, 2); }); + it("should skip files that are not part of entry files", async(t) => { + const FIXTURE_URL = new URL("fixtures/entryFiles/", import.meta.url); + + const analyser = new AstAnalyser(new JsSourceParser(), [ + "test/fixtures/entryFiles/src", + "test/fixtures/entryFiles/src2" + ]); + + const validTestFiles = [ + "src/foo.js", + "src/bar/bar.js", + "src2/foo.js", + "src2/bar/bar.js" + ]; + + validTestFiles.forEach(async(file) => { + const result = await analyser.analyseFile( + new URL(file, FIXTURE_URL) + ); + + assert.ok(result.ok); + }); + + const invalidTestFiles = [ + "test/foo.js", + "test/bar/bar.js" + ]; + + invalidTestFiles.forEach(async(file) => { + const result = await analyser.analyseFile( + new URL(file, FIXTURE_URL) + ); + + assert.ok(result.ok); + assert.ok(result.isSkipped); + }); + }); + it("should fail with a parsing error", async() => { const result = await getAnalyser().analyseFile( new URL("parsingError.js", FIXTURE_URL), diff --git a/test/fixtures/entryFiles/src/bar/bar.js b/test/fixtures/entryFiles/src/bar/bar.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/src/bar/bar.js @@ -0,0 +1 @@ +require("foobar"); diff --git a/test/fixtures/entryFiles/src/foo.js b/test/fixtures/entryFiles/src/foo.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/src/foo.js @@ -0,0 +1 @@ +require("foobar"); diff --git a/test/fixtures/entryFiles/src2/bar/bar.js b/test/fixtures/entryFiles/src2/bar/bar.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/src2/bar/bar.js @@ -0,0 +1 @@ +require("foobar"); diff --git a/test/fixtures/entryFiles/src2/foo.js b/test/fixtures/entryFiles/src2/foo.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/src2/foo.js @@ -0,0 +1 @@ +require("foobar"); diff --git a/test/fixtures/entryFiles/test/bar/bar.js b/test/fixtures/entryFiles/test/bar/bar.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/test/bar/bar.js @@ -0,0 +1 @@ +require("foobar"); diff --git a/test/fixtures/entryFiles/test/foo.js b/test/fixtures/entryFiles/test/foo.js new file mode 100644 index 0000000..545d26a --- /dev/null +++ b/test/fixtures/entryFiles/test/foo.js @@ -0,0 +1 @@ +require("foobar"); From 61e8ad12d32efb401a669f9c45e46f35e8bdc095 Mon Sep 17 00:00:00 2001 From: jean Date: Tue, 19 Mar 2024 18:25:35 +0100 Subject: [PATCH 3/4] fix: comment typo --- test/AstAnalyser.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AstAnalyser.spec.js b/test/AstAnalyser.spec.js index cd42dd7..84f9495 100644 --- a/test/AstAnalyser.spec.js +++ b/test/AstAnalyser.spec.js @@ -186,7 +186,7 @@ describe("AstAnalyser", (t) => { let calls = AstAnalyser.prototype.analyse.mock.calls; assert.strictEqual(calls.length, 1); - // should remove the cache and call AstAnalyser.analyse again + // should remove the cache analyser.reset(); await analyser.analyseFile( From 3d8234327744a56cad990663a2a5a75247517de7 Mon Sep 17 00:00:00 2001 From: jean Date: Tue, 19 Mar 2024 18:29:47 +0100 Subject: [PATCH 4/4] fix: test equality because analyzed files return the same object instance --- test/AstAnalyser.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AstAnalyser.spec.js b/test/AstAnalyser.spec.js index 84f9495..eabec29 100644 --- a/test/AstAnalyser.spec.js +++ b/test/AstAnalyser.spec.js @@ -180,8 +180,8 @@ describe("AstAnalyser", (t) => { { module: false, packageName: "foobar" } ); - assert.deepEqual(result1, result2); - assert.deepEqual(result2, result3); + assert.equal(result1, result2); + assert.equal(result2, result3); let calls = AstAnalyser.prototype.analyse.mock.calls; assert.strictEqual(calls.length, 1);