@nodesecure/tarball
Version:
NodeSecure tarball scanner
110 lines • 4.5 kB
JavaScript
// Import Node.js Dependencies
import path from "node:path";
// Import Third-party Dependencies
import { EntryFilesAnalyser, AstAnalyser } from "@nodesecure/js-x-ray";
import { ManifestManager } from "@nodesecure/mama";
// Import Internal Dependencies
import { filterDependencyKind, analyzeDependencies } from "../utils/index.js";
export class SourceCodeReport {
#isConsumed = false;
warnings = [];
dependencies = Object.create(null);
minified = [];
flags = {
hasExternalCapacity: false
};
get consumed() {
return this.#isConsumed;
}
push(report) {
this.#isConsumed = true;
this.warnings.push(...report.warnings.map((warning) => {
return { ...warning, file: report.file };
}));
if (report.ok) {
if (report.flags.has("fetch")) {
this.flags.hasExternalCapacity = true;
}
this.dependencies[report.file] = Object.fromEntries(report.dependencies);
report.flags.has("is-minified") && this.minified.push(report.file);
}
}
groupAndAnalyseDependencies(mama) {
const files = new Set();
const dependencies = new Set();
const dependenciesInTryBlock = new Set();
for (const [file, fileDeps] of Object.entries(this.dependencies)) {
const filtered = filterDependencyKind([...Object.keys(fileDeps)], path.dirname(file));
[...Object.entries(fileDeps)]
.flatMap(([name, dependency]) => (dependency.inTry ? [name] : []))
.forEach((name) => dependenciesInTryBlock.add(name));
filtered.packages.forEach((name) => dependencies.add(name));
filtered.files.forEach((file) => files.add(file));
}
const { nodeDependencies, thirdPartyDependencies, subpathImportsDependencies, missingDependencies, unusedDependencies, flags } = analyzeDependencies([...dependencies], { mama, tryDependencies: dependenciesInTryBlock });
return {
files,
dependenciesInTryBlock: [...dependenciesInTryBlock],
dependencies: {
nodejs: nodeDependencies,
subpathImports: subpathImportsDependencies,
thirdparty: thirdPartyDependencies,
missing: missingDependencies,
unused: unusedDependencies
},
flags
};
}
}
export class SourceCodeScanner {
#astAnalyser = new AstAnalyser();
#initNewReport;
manifest;
constructor(manifest, options = {}) {
const { reportInitiator = () => new SourceCodeReport() } = options;
this.manifest = manifest;
this.#initNewReport = reportInitiator;
}
async iterate(entries) {
if (entries.manifest.length === 0 &&
entries.javascript.length === 0) {
throw new Error("You must provide at least one file either in manifest or javascript");
}
return entries.manifest.length > 0 ?
this.#iterateWithEntries(entries) :
this.#iterateAll(entries.javascript);
}
async #iterateWithEntries(entries) {
const report = this.#initNewReport();
const { location } = this.manifest;
const efa = new EntryFilesAnalyser({
astAnalyzer: this.#astAnalyser,
rootPath: location,
ignoreENOENT: true
});
const absoluteEntryFiles = entries.manifest.map((filePath) => path.join(location, filePath));
for await (const fileReport of efa.analyse(absoluteEntryFiles)) {
report.push(fileReport);
}
return report.consumed ?
report :
this.#iterateAll(entries.javascript);
}
async #iterateAll(sourceFiles) {
if (sourceFiles.length === 0) {
throw new Error("You must provide at least one javascript source file");
}
const { location, document: { name: packageName, type } } = this.manifest;
const report = this.#initNewReport();
await Promise.allSettled(sourceFiles.map(async (relativeFile) => {
const filePath = path.join(location, relativeFile);
const fileReport = await this.#astAnalyser.analyseFile(filePath, {
packageName,
module: type === "module"
});
report.push({ ...fileReport, file: relativeFile });
}));
return report;
}
}
//# sourceMappingURL=SourceCodeScanner.class.js.map