dependency-cruiser
Version:
Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.
163 lines (152 loc) • 4.32 kB
JavaScript
// @ts-check
import { readFile, mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import {
brotliCompressSync,
brotliDecompressSync,
constants as zlibConstants,
} from "node:zlib";
import { optionsAreCompatible } from "./options-compatible.mjs";
import MetadataStrategy from "./metadata-strategy.mjs";
import ContentStrategy from "./content-strategy.mjs";
import { scannableExtensions } from "#extract/transpile/meta.mjs";
import { bus } from "#utl/bus.mjs";
/**
* @import { IRevisionData, ICruiseResult } from "../../types/dependency-cruiser.mjs";
* @import { IStrictCruiseOptions } from "../../types/strict-options.mjs";
* @import { cacheStrategyType } from "../../types/cache-options.mjs";
*/
const CACHE_FILE_NAME = "cache.json";
const EMPTY_CACHE = {
modules: [],
summary: {
error: 0,
warn: 0,
info: 0,
ignore: 0,
totalCruised: 0,
violations: [],
optionsUsed: {},
},
};
// see ./cache-rationales.md#cache-format-versioning for rationale & bump instructions
const CACHE_FORMAT_VERSION = 16.2;
export default class Cache {
/** @type {(IRevisionData | null)=} */
#revisionData;
#cacheStrategy;
#compress;
/**
* @param {cacheStrategyType=} pCacheStrategy
* @param {boolean=} pCompress
*/
constructor(pCacheStrategy, pCompress) {
this.#revisionData = null;
this.#cacheStrategy =
pCacheStrategy === "content"
? new ContentStrategy()
: new MetadataStrategy();
this.#compress = pCompress ?? false;
}
/**
*
* @param {ICruiseResult} pCachedCruiseResult
* @returns {boolean}
*/
cacheFormatVersionCompatible(pCachedCruiseResult) {
return (
(pCachedCruiseResult?.revisionData?.cacheFormatVersion ?? 1) >=
CACHE_FORMAT_VERSION
);
}
/**
* @param {IStrictCruiseOptions} pCruiseOptions
* @param {ICruiseResult} pCachedCruiseResult
* @param {IRevisionData=} pRevisionData
* @returns {Promise<boolean>}
*/
async canServeFromCache(pCruiseOptions, pCachedCruiseResult, pRevisionData) {
this.#revisionData =
pRevisionData ??
(await this.#cacheStrategy.getRevisionData(
".",
pCachedCruiseResult,
pCruiseOptions,
{
extensions: new Set(
scannableExtensions.concat(pCruiseOptions.extraExtensionsToScan),
),
},
));
this.#revisionData.cacheFormatVersion = CACHE_FORMAT_VERSION;
bus.debug("cache: compare");
return (
this.cacheFormatVersionCompatible(pCachedCruiseResult) &&
this.#cacheStrategy.revisionDataEqual(
pCachedCruiseResult.revisionData,
this.#revisionData,
) &&
optionsAreCompatible(
pCachedCruiseResult.summary.optionsUsed,
pCruiseOptions,
)
);
}
/**
* @param {string} pCacheFolder
* @returns {Promise<ICruiseResult>}
*/
async read(pCacheFolder) {
try {
let lPayload = "";
if (this.#compress === true) {
const lCompressedPayload = await readFile(
join(pCacheFolder, CACHE_FILE_NAME),
);
const lPayloadAsBuffer = brotliDecompressSync(lCompressedPayload);
lPayload = lPayloadAsBuffer.toString("utf8");
} else {
lPayload = await readFile(join(pCacheFolder, CACHE_FILE_NAME), "utf8");
}
return JSON.parse(lPayload);
} catch (pError) {
return EMPTY_CACHE;
}
}
/**
* @param {string} pPayload
* @param {boolean} pCompress
* @return {Buffer|string}
*/
#compact(pPayload, pCompress) {
if (pCompress) {
// see ./cache-rationales.md#cache-compression for rationale
return brotliCompressSync(pPayload, {
params: {
[zlibConstants.BROTLI_PARAM_QUALITY]:
zlibConstants.BROTLI_MIN_QUALITY,
},
});
}
return pPayload;
}
/**
* @param {string} pCacheFolder
* @param {ICruiseResult} pCruiseResult
* @param {IRevisionData=} pRevisionData
*/
async write(pCacheFolder, pCruiseResult, pRevisionData) {
let lRevisionData = pRevisionData ?? this.#revisionData;
await mkdir(pCacheFolder, { recursive: true });
const lUncompressedPayload = JSON.stringify(
this.#cacheStrategy.prepareRevisionDataForSaving(
pCruiseResult,
lRevisionData,
),
);
let lPayload = this.#compact(lUncompressedPayload, this.#compress);
// relying on writeFile defaults to 'do the right thing' (i.e. utf8
// when the payload is a string; raw buffer otherwise)
await writeFile(join(pCacheFolder, CACHE_FILE_NAME), lPayload);
}
}