dependency-cruiser
Version:
Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.
135 lines (123 loc) • 4.08 kB
JavaScript
import { extname } from "node:path";
export function dependenciesEqual(pLeftDependency) {
// As we're using this to compare (typescript) pre-compilation dependencies
// with post-compilation dependencies we do not consider the moduleSystem.
//
// In typescript the module system will typically be es6. Compiled down to
// javascript it can be es6, but also cjs (depends on the `module` setting
// in your tsconfig). In the latter case, we're still looking at the same
// dependency even though the module systems differ.
return (pRightDependency) =>
pLeftDependency.module === pRightDependency.module &&
pLeftDependency.dynamic === pRightDependency.dynamic &&
pLeftDependency.exoticRequire === pRightDependency.exoticRequire;
}
export function detectPreCompilationNess(pTSDependencies, pJSDependencies) {
return pTSDependencies.map((pTSDependency) =>
pJSDependencies.some(dependenciesEqual(pTSDependency))
? { ...pTSDependency, preCompilationOnly: false }
: {
...pTSDependency,
preCompilationOnly: true,
dependencyTypes: (pTSDependency.dependencyTypes || []).concat(
"pre-compilation-only",
),
},
);
}
const PROTOCOL_ONLY_BUILTINS = new Set([
// module.builtinModules.filter(m=>m.startsWith('node:'))
"node:sea",
"node:sqlite",
"node:test",
"node:test/reporters",
// module.builtinModules.filter(m=>m.startsWith('bun:'))
"bun:ffi",
"bun:jsc",
"bun:sqlite",
"bun:test",
"bun:wrap",
]);
/**
* Given a module name returns the canonical module name.
* @param {string} pProtocol
* @param {string} pModuleName
* @returns {string}
*/
function getCanonicalModuleName(pModuleName, pProtocol) {
const lModuleWithProtocol = pProtocol + pModuleName;
const lIsJsIshProtocol = pProtocol === "node:" || pProtocol === "bun:";
const lIsProtocolOnlyModule = PROTOCOL_ONLY_BUILTINS.has(lModuleWithProtocol);
if (!lIsJsIshProtocol || lIsProtocolOnlyModule) {
return lModuleWithProtocol;
}
return pModuleName;
}
/**
* Given a module string returns in an object
* - the module name
* - the protocol (when encoded in the string)
* - the mimeType (when encoded in the string)
*
* See https://nodejs.org/api/esm.html#esm_urls
*
* would've loved to use url.URL here, but that doesn't extract the mime type
* (if there's a default node API that does this I'm all ears)
*
* @param {string} pString
* @returns {{module:string; protocol?:string; mimeType?:string}}
*/
// eslint-disable-next-line complexity
export function extractModuleAttributes(pString) {
let lReturnValue = { module: pString };
const lModuleAttributes = pString.match(
// eslint-disable-next-line security/detect-unsafe-regex
/^(?<protocol>node:|file:|data:|bun:)(?:(?<mimeType>[^,]+),)?(?<module>.+)$/,
);
if (lModuleAttributes?.groups) {
lReturnValue.module = getCanonicalModuleName(
lModuleAttributes?.groups.module,
lModuleAttributes?.groups.protocol,
);
if (lModuleAttributes?.groups?.protocol) {
lReturnValue.protocol = lModuleAttributes.groups.protocol;
}
if (lModuleAttributes?.groups?.mimeType) {
lReturnValue.mimeType = lModuleAttributes.groups.mimeType;
}
}
return lReturnValue;
}
/**
* returns pFilenameString stripped of any 'query parameters' e.g.
*
* "hello/there?thing=smurf" => "hello/there"
* "boink/boink/t.gif?resource" => "boink/boink/t.gif"
* "no/thing/after.js" => "no/thing/after.js"
*
* @param {string} pFilenameString string to strip the query parameters from
* @returns {string} the stripped string
*/
export function stripQueryParameters(pFilenameString) {
// url.parse(pFilenameString).pathname did this, but it's
// deprecated, hence this funky RE replace.
return pFilenameString.replace(/\?.+$/, "");
}
const TS_COMPATIBLE_EXTENSIONS = new Set([
".ts",
".tsx",
".mts",
".cts",
".js",
".mjs",
".cjs",
".vue",
]);
/**
* Returns true if the file name has a TypeScript compatible extension
* @param {string} pFileName
* @returns {boolean}
*/
export function isTypeScriptCompatible(pFileName) {
return TS_COMPATIBLE_EXTENSIONS.has(extname(pFileName));
}