Skip to content

Commit a3af86a

Browse files
authored
feat(AstAnalyser): add synchronous analyseFile function version (#275)
1 parent a6b6c04 commit a3af86a

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

src/AstAnalyser.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Import Node.js Dependencies
22
import fs from "node:fs/promises";
3+
import fsSync from "node:fs";
34
import path from "node:path";
45

56
// Import Third-party Dependencies
@@ -127,6 +128,52 @@ export class AstAnalyser {
127128
}
128129
}
129130

131+
analyseFileSync(
132+
pathToFile,
133+
options = {}
134+
) {
135+
try {
136+
const {
137+
packageName = null,
138+
module = true,
139+
removeHTMLComments = false,
140+
initialize,
141+
finalize
142+
} = options;
143+
144+
const str = fsSync.readFileSync(pathToFile, "utf-8");
145+
const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile;
146+
147+
const isMin = filePathString.includes(".min") || isMinified(str);
148+
const data = this.analyse(str, {
149+
isMinified: isMin,
150+
module: path.extname(filePathString) === ".mjs" ? true : module,
151+
removeHTMLComments,
152+
initialize,
153+
finalize
154+
});
155+
156+
if (packageName !== null) {
157+
data.dependencies.delete(packageName);
158+
}
159+
160+
return {
161+
ok: true,
162+
dependencies: data.dependencies,
163+
warnings: data.warnings,
164+
isMinified: !data.isOneLineRequire && isMin
165+
};
166+
}
167+
catch (error) {
168+
return {
169+
ok: false,
170+
warnings: [
171+
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
172+
]
173+
};
174+
}
175+
}
176+
130177
/**
131178
* @param {!string} source
132179
* @param {object} options

test/AstAnalyser.spec.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,116 @@ describe("AstAnalyser", (t) => {
365365
});
366366
});
367367

368+
describe("analyseFileSync", () => {
369+
it("remove the packageName from the dependencies list", () => {
370+
const result = getAnalyser().analyseFileSync(
371+
new URL("depName.js", FIXTURE_URL),
372+
{ module: false, packageName: "foobar" }
373+
);
374+
375+
assert.ok(result.ok);
376+
assert.strictEqual(result.warnings.length, 0);
377+
assert.deepEqual([...result.dependencies.keys()],
378+
["open"]
379+
);
380+
});
381+
382+
it("should fail with a parsing error", () => {
383+
const result = getAnalyser().analyseFileSync(
384+
new URL("parsingError.js", FIXTURE_URL),
385+
{ module: false, packageName: "foobar" }
386+
);
387+
388+
assert.strictEqual(result.ok, false);
389+
assert.strictEqual(result.warnings.length, 1);
390+
391+
const parsingError = result.warnings[0];
392+
assert.strictEqual(parsingError.kind, "parsing-error");
393+
});
394+
395+
describe("hooks", () => {
396+
const url = new URL("depName.js", FIXTURE_URL);
397+
398+
describe("initialize", () => {
399+
it("should throw if initialize is not a function", () => {
400+
const res = getAnalyser().analyseFileSync(
401+
url, {
402+
initialize: "foo"
403+
});
404+
405+
assert.strictEqual(res.ok, false);
406+
assert.strictEqual(res.warnings[0].value, "options.initialize must be a function");
407+
assert.strictEqual(res.warnings[0].kind, "parsing-error");
408+
});
409+
410+
it("should call the initialize function", (t) => {
411+
const initialize = t.mock.fn();
412+
413+
getAnalyser().analyseFileSync(url, {
414+
initialize
415+
});
416+
417+
assert.strictEqual(initialize.mock.callCount(), 1);
418+
});
419+
420+
it("should pass the source file as first argument", (t) => {
421+
const initialize = t.mock.fn();
422+
423+
getAnalyser().analyseFileSync(url, {
424+
initialize
425+
});
426+
427+
assert.strictEqual(initialize.mock.calls[0].arguments[0] instanceof SourceFile, true);
428+
});
429+
});
430+
431+
describe("finalize", () => {
432+
it("should throw if finalize is not a function", () => {
433+
const res = getAnalyser().analyseFileSync(
434+
url, {
435+
finalize: "foo"
436+
});
437+
438+
assert.strictEqual(res.ok, false);
439+
assert.strictEqual(res.warnings[0].value, "options.finalize must be a function");
440+
assert.strictEqual(res.warnings[0].kind, "parsing-error");
441+
});
442+
443+
it("should call the finalize function", (t) => {
444+
const finalize = t.mock.fn();
445+
446+
getAnalyser().analyseFileSync(url, {
447+
finalize
448+
});
449+
450+
assert.strictEqual(finalize.mock.callCount(), 1);
451+
});
452+
453+
it("should pass the source file as first argument", (t) => {
454+
const finalize = t.mock.fn();
455+
456+
getAnalyser().analyseFileSync(url, {
457+
finalize
458+
});
459+
460+
assert.strictEqual(finalize.mock.calls[0].arguments[0] instanceof SourceFile, true);
461+
});
462+
});
463+
464+
465+
it("intialize should be called before finalize", () => {
466+
const calls = [];
467+
468+
getAnalyser().analyseFileSync(url, {
469+
initialize: () => calls.push("initialize"),
470+
finalize: () => calls.push("finalize")
471+
});
472+
473+
assert.deepEqual(calls, ["initialize", "finalize"]);
474+
});
475+
});
476+
});
477+
368478
describe("prepareSource", () => {
369479
it("should remove shebang at the start of the file", (t) => {
370480
const source = "#!/usr/bin/env node\nconst hello = \"world\";";

0 commit comments

Comments
 (0)