diff --git a/changelog.md b/changelog.md index 0349328..9480dad 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +## V2.2.10 +- **[v2]** refine cache logic +- **[v2]** replace fs sync methods to promises + ## V2.2.8 - **[v2]** refine some logs - **[v2]** make hash of outputs stable diff --git a/lib/cache.js b/lib/cache.js index d5986a8..d3726d6 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -1,50 +1,54 @@ const { readFile } = require('fs/promises'); class BuildCache { /** - * @param {(...args: any[]) => void} log + * @param {import('..').Build} build */ - constructor(log) { - this.log = log || ((...args) => console.log(...args)); + constructor(build) { + this.build = build; + /** + * @type {import('..').Build['context']['log']} + */ + this.log = build.context.log; /** * @type {Map} */ - async get(key) { - const cachedData = this.cache.get(key); + async get(absPath) { + const cachedData = this.cache.get(absPath); if (cachedData) { - this.log(`find cache data, check if input changed(${key})...`); - const input = await readFile(key, { encoding: 'utf8' }); + this.log( + `find cache data, check if content changed(${this.build.context.relative(absPath)})...` + ); + const input = await readFile(absPath, { encoding: 'utf8' }); if (input === cachedData.input) { - this.log(`input not changed, return cache(${key})`); + this.log(`content not changed, return cache(${this.build.context.relative(absPath)})`); return cachedData.result; } - this.log(`input changed(${key})`); + this.log(`content changed(${this.build.context.relative(absPath)}), rebuilding...`); return void 0; } - this.log(`cache data not found(${key})`); + this.log(`cache data not found(${this.build.context.relative(absPath)}), building...`); return void 0; } /** - * @description key should be absolute path - * @param {string} key + * @param {string} absPath * @param {import('esbuild').OnLoadResult} result * @param {string} originContent * @returns {Promise} */ - async set(key, result, originContent) { + async set(absPath, result, originContent) { const m = process.memoryUsage.rss(); if (m / 1024 / 1024 > 250) { this.log('memory usage > 250M'); this.clear(); } - const input = originContent || (await readFile(key, { encoding: 'utf8' })); - this.cache.set(key, { input, result }); + const input = originContent || (await readFile(absPath, { encoding: 'utf8' })); + this.cache.set(absPath, { input, result }); } clear() { this.log('clear cache'); diff --git a/lib/plugin.js b/lib/plugin.js index bd073e6..e62c716 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -1,6 +1,5 @@ const path = require('path'); const { createHash } = require('crypto'); -const { readFileSync } = require('fs'); const { readFile, appendFile } = require('fs/promises'); const { getLogger, @@ -105,24 +104,23 @@ export default new Proxy(${classNamesMapString}, { * @return {Promise} */ const prepareBuild = async (build, options) => { - const buildId = getBuildId(build); + const buildId = await getBuildId(build); build.initialOptions.metafile = true; const packageRoot = options.root; const buildRoot = getRootDir(build); const log = getLogger(build); - const cache = new BuildCache(log); const relative = (to) => getRelativePath(build, to); - log(`root of this build(#${buildId}):`, buildRoot); - build.context = { buildId, buildRoot, packageRoot, log, - relative, - cache + relative }; + build.context.cache = new BuildCache(build); + + log(`root of this build(#${buildId}):`, buildRoot); }; /** @@ -181,7 +179,7 @@ const onLoadModulesCss = async (build, options, args) => { log(`loading ${rpath}${args.suffix}`); - log(`checking cache for`, absPath); + log(`checking cache for`, rpath); const cached = await cache.get(absPath); if (cached) { log('return build cache for', rpath); @@ -211,7 +209,7 @@ const onLoadModulesCss = async (build, options, args) => { loader: 'js' }; await cache.set(absPath, result, originCss); - log(`add build result to cache for ${absPath}`); + log(`add build result to cache for ${rpath}`); return result; }; @@ -279,10 +277,10 @@ const onLoadBuiltModulesCss = async ({ pluginData }, build) => { const onEnd = async (build, options, result) => { const { buildId, buildRoot, cache } = build.context; const log = getLogger(build); - log('done'); if (options.inject === true || typeof options.inject === 'string') { const cssContents = []; + const { entryPoints } = build.initialOptions; let entriesArray = []; if (Array.isArray(entryPoints)) { @@ -295,24 +293,32 @@ const onEnd = async (build, options, result) => { }); } const entries = entriesArray.map((p) => (path.isAbsolute(p) ? p : path.resolve(buildRoot, p))); + log('entries:', entries); + let injectTo = null; - Object.keys(result.metafile?.outputs ?? []).forEach((f) => { - if ( - !injectTo && - result.metafile.outputs[f].entryPoint && - entries.includes(path.resolve(buildRoot, result.metafile.outputs[f].entryPoint)) && - path.extname(f) === '.js' - ) { - injectTo = path.resolve(buildRoot, f); - } - if (path.extname(f) === '.css') { - const fullpath = path.resolve(buildRoot, f); - const css = readFileSync(fullpath); - cssContents.push(`${css}\n`); - } - }); + const outputs = Object.keys(result.metafile?.outputs ?? []); + + await Promise.all( + outputs.map(async (f) => { + if ( + !injectTo && + result.metafile.outputs[f].entryPoint && + entries.includes(path.resolve(buildRoot, result.metafile.outputs[f].entryPoint)) && + path.extname(f) === '.js' + ) { + injectTo = path.resolve(buildRoot, f); + } + if (path.extname(f) === '.css') { + const fullpath = path.resolve(buildRoot, f); + const css = await readFile(fullpath, { encoding: 'utf8' }); + cssContents.push(`${css}`); + } + }) + ); + log('inject css to', path.relative(buildRoot, injectTo)); + if (injectTo && cssContents.length) { const allCss = cssContents.join(''); const container = typeof options.inject === 'string' ? options.inject : 'head'; @@ -321,6 +327,8 @@ const onEnd = async (build, options, result) => { await appendFile(injectTo, injectedCode, { encoding: 'utf-8' }); } } + + log('finished'); }; /** diff --git a/lib/utils.js b/lib/utils.js index 97ea320..2649858 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,6 +1,6 @@ const path = require('path'); const { createHash } = require('crypto'); -const { readFileSync } = require('fs'); +const { readFile } = require('fs/promises'); const pluginName = require('../package.json').name.toLowerCase(); const pluginNamespace = `${pluginName}-namespace`; const buildingCssSuffix = `?${pluginName}-building`; @@ -103,9 +103,9 @@ const getRelativePath = (build, to) => { * getBuildId * @description buildId should be stable so that the hash of output files are stable * @param {import('..').Build} build - * @returns {string} + * @returns {Promise} */ -const getBuildId = (build) => { +const getBuildId = async (build) => { const { entryPoints } = build.initialOptions; const buildRoot = getRootDir(build); let entries = []; @@ -118,12 +118,14 @@ const getBuildId = (build) => { entries.push(entryPoints[k]); }); } - const entryContents = entries - .map((p) => { - const absPath = path.isAbsolute(p) ? p : path.resolve(buildRoot, p); - return readFileSync(absPath, { encoding: 'utf8' }).trim(); - }) - .join(''); + const entryContents = ( + await Promise.all( + entries.map(async (p) => { + const absPath = path.isAbsolute(p) ? p : path.resolve(buildRoot, p); + return (await readFile(absPath, { encoding: 'utf8' })).trim(); + }) + ) + ).join(''); return createHash('sha256').update(entryContents).digest('hex'); };