UNPKG

@nodesecure/tarball

Version:
110 lines 4.5 kB
// 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