diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 24b1358..f15e903 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,18 +1,18 @@ -name: tests on: - push + jobs: - build: + test: runs-on: ubuntu-latest strategy: matrix: node-version: - - 14.x - - 18.x - - 19.x + - 20.x + - 24.x + - latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - run: npm install-test diff --git a/CHANGELOG.md b/CHANGELOG.md index eb45425..8afb896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ faucet-pipeline-core version history ==================================== +v2.1.0 +------ + +_2025-10-26_ + +notable changes for end users: + +* added support for faucet-pipeline-assets and faucet-pipeline-css +* dropped support for Node 19 and below + +no significant changes for developers + + v2.0.0 ------ diff --git a/lib/manager.js b/lib/manager.js index f3e8c23..3630419 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -30,9 +30,9 @@ module.exports = class AssetManager { } return createFile(filepath, data). - then(_ => this.manifest && + then(() => this.manifest && this._updateManifest(originalPath, filepath, targetDir)). - then(_ => { + then(() => { reportFileStatus(originalPath, this.referenceDir, error); if(error && this.exitOnError) { throw error; diff --git a/lib/plugins.js b/lib/plugins.js index 80805e8..5df0c38 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -4,9 +4,17 @@ let { loadExtension, abort, repr } = require("./util"); // common plugins included for convenience let DEFAULTS = [{ + key: "assets", + bucket: "static", + plugin: "faucet-pipeline-assets" +}, { key: "js", bucket: "scripts", plugin: "faucet-pipeline-js" +}, { + key: "css", + bucket: "styles", + plugin: "faucet-pipeline-css" }, { key: "sass", bucket: "styles", diff --git a/lib/util/files/finder.js b/lib/util/files/finder.js index 279fc48..5764be7 100644 --- a/lib/util/files/finder.js +++ b/lib/util/files/finder.js @@ -1,9 +1,5 @@ -let fs = require("fs"); +let { readdir, stat } = require("fs/promises"); let path = require("path"); -let { promisify } = require("util"); - -let stat = promisify(fs.stat); -let readDir = promisify(fs.readdir); module.exports = class FileFinder { constructor(directory, { skipDotfiles, filter = () => true } = {}) { @@ -36,7 +32,7 @@ function tree(filepath, referenceDir = filepath) { return [path.relative(referenceDir, filepath)]; } - return readDir(filepath). + return readdir(filepath). then(entries => { let res = Promise.all(entries.map(entry => { return tree(path.join(filepath, entry), referenceDir); diff --git a/lib/util/files/index.js b/lib/util/files/index.js index 33fb517..d067696 100644 --- a/lib/util/files/index.js +++ b/lib/util/files/index.js @@ -1,42 +1,29 @@ "use strict"; -let { abort, repr } = require("../"); -let fs = require("fs"); +let { writeFile } = require("fs/promises"); +let { mkdirSync } = require("fs"); let path = require("path"); -let { promisify } = require("util"); -let KNOWN = {}; // avoids redundant `mkdirp` invocations -let LOCKS = {}; - -let writeFile = promisify(fs.writeFile); +let KNOWN = new Set(); // avoids redundant `mkdir` invocations +let LOCKS = new Map(); // avoids concurrent write operations and creates target directory if necessary module.exports = function createFile(filepath, contents) { - let lock = LOCKS[filepath]; + let lock = LOCKS.get(filepath); if(lock) { // defer - return lock.then(_ => createFile(filepath, contents)); + return lock.then(() => createFile(filepath, contents)); } // create directory if necessary - if(!KNOWN[filepath]) { - KNOWN[filepath] = true; + if(!KNOWN.has(filepath)) { + KNOWN.add(filepath); // NB: `sync` avoids race condition for subsequent operations - mkdirpSync(path.dirname(filepath)); + mkdirSync(path.dirname(filepath), { recursive: true }); } let prom = writeFile(filepath, contents); - LOCKS[filepath] = prom; - return prom.then(_ => { - delete LOCKS[filepath]; + LOCKS.set(filepath, prom); + return prom.then(() => { + LOCKS.delete(filepath); }); }; - -function mkdirpSync(directory) { - try { - // NB: `recursive` option was introduced in Node v10.12.0 - fs.mkdirSync(directory, { recursive: true }); - } catch(err) { - abort(`ERROR: auto-creating ${repr(directory)} requires ` + - "Node v10.12.0 or above"); - } -} diff --git a/lib/util/index.js b/lib/util/index.js index b118ef3..cf619be 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -2,10 +2,8 @@ let path = require("path"); let crypto = require("crypto"); -let { promisify } = require("util"); exports.abort = abort; -exports.promisify = promisify; // deprecated exports.repr = repr; // reports success or failure for a given file path (typically regarding diff --git a/lib/util/runner.js b/lib/util/runner.js index 2b75e5c..d2ce80f 100644 --- a/lib/util/runner.js +++ b/lib/util/runner.js @@ -8,7 +8,7 @@ module.exports = class SerializedRunner { run(...args) { if(!this._pending) { // prevent concurrent execution this._pending = augment(this.asyncOp(...args)). - finally(_ => { + finally(() => { this._pending = null; }); } @@ -25,10 +25,10 @@ module.exports = class SerializedRunner { let res = this.run(...args); if(enqueue) { this._queued = res = augment(res). - finally(_ => { + finally(() => { this._queued = null; }). - then(_ => this.run(...args)); + then(() => this.run(...args)); } return res; } diff --git a/package.json b/package.json index 5bf5e23..03e1c72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "faucet-pipeline-core", - "version": "2.0.0", + "version": "2.1.0", "description": "faucet-pipeline's core library", "author": "FND", "contributors": [ @@ -25,7 +25,7 @@ "lint": "eslint --cache lib bin/* test test/bin/* && echo ✓" }, "engines": { - "node": ">= 12" + "node": ">= 20" }, "dependencies": { "browserslist": "~4.21.4", diff --git a/test/test_manager.js b/test/test_manager.js index 883ad9c..6375827 100644 --- a/test/test_manager.js +++ b/test/test_manager.js @@ -7,7 +7,7 @@ let assert = require("assert"); let assertSame = assert.strictEqual; -describe("asset manager", _ => { +describe("asset manager", () => { let root = path.resolve(__dirname, "fixtures"); let cwd; let { exit } = process; diff --git a/test/test_manifest.js b/test/test_manifest.js index f4fa43a..041cc36 100644 --- a/test/test_manifest.js +++ b/test/test_manifest.js @@ -7,7 +7,7 @@ let assert = require("assert"); let assertSame = assert.strictEqual; -describe("manifest", _ => { +describe("manifest", () => { let root = path.resolve(__dirname, "fixtures"); let cwd; @@ -23,18 +23,18 @@ describe("manifest", _ => { it("maps original to actual file names with deterministic serialization", () => { let manifest = new Manifest(root); return manifest.set("foo.png", "foo-abc123.png"). - then(_ => { + then(() => { assertSame(JSON.stringify(manifest), '{"foo.png":"/foo-abc123.png"}'); return manifest.set("bar.css", "bar-def456.css"); }). - then(_ => { + then(() => { assertSame(JSON.stringify(manifest), '{"bar.css":"/bar-def456.css","foo.png":"/foo-abc123.png"}'); return manifest.set("xox.js", "xox-ghi789.js"); }). - then(_ => { + then(() => { assertSame(JSON.stringify(manifest), // eslint-disable-next-line max-len '{"bar.css":"/bar-def456.css","foo.png":"/foo-abc123.png","xox.js":"/xox-ghi789.js"}'); }); diff --git a/test/test_plugins.js b/test/test_plugins.js index 259dfe3..c8d3442 100644 --- a/test/test_plugins.js +++ b/test/test_plugins.js @@ -9,10 +9,18 @@ let { deepStrictEqual: assertDeep } = assert; let ROOT = path.resolve(__dirname, "fixtures"); let DEFAULTS = { + assets: { + bucket: "static", + plugin: "faucet-pipeline-assets" + }, js: { bucket: "scripts", plugin: "faucet-pipeline-js" }, + css: { + bucket: "styles", + plugin: "faucet-pipeline-css" + }, sass: { bucket: "styles", plugin: "faucet-pipeline-sass" @@ -30,7 +38,7 @@ let DEFAULTS = { let { NODE_PATH } = process.env; let CUSTOM_NODE_PATH = path.resolve(ROOT, "node_modules"); -describe("plugin registration", _ => { +describe("plugin registration", () => { before(() => { updateNodePath(NODE_PATH, CUSTOM_NODE_PATH); }); @@ -105,7 +113,7 @@ describe("plugin registration", _ => { }); }); -describe("plugin resolution", _ => { +describe("plugin resolution", () => { let { exit } = process; before(() => { diff --git a/test/test_runner.js b/test/test_runner.js index 93694e6..8297f70 100644 --- a/test/test_runner.js +++ b/test/test_runner.js @@ -4,7 +4,7 @@ let SerializedRunner = require("../lib/util/runner"); let { strictEqual: assertSame, deepStrictEqual: assertDeep } = require("assert"); -describe("watch mode", _ => { +describe("watch mode", () => { it("avoids concurrent compilation, queueing recompilation", () => { let bundle = new MockBundle(); @@ -13,7 +13,7 @@ describe("watch mode", _ => { bundle.compile(); // skipped due to queue limit let prevLog; // keeps track of compilation sequence return bundle.compile(). // skipped due to queue limit - then(_ => { + then(() => { let log = bundle.executionLog; assertSame(log.length, 2); // compiled, then recompiled once prevLog = [].concat(log); @@ -23,7 +23,7 @@ describe("watch mode", _ => { bundle.compile(); // skipped due to queue limit return bundle.compile(); // skipped due to queue limit }). - then(_ => { + then(() => { let log = bundle.executionLog; assertSame(log.length, 4); // compiled, then recompiled once assertDeep(log.slice(0, prevLog.length), prevLog); @@ -31,7 +31,7 @@ describe("watch mode", _ => { return bundle.compile(); // starts compilation }). - then(_ => { + then(() => { let log = bundle.executionLog; assertSame(log.length, 5); assertDeep(log.slice(0, prevLog.length), prevLog); @@ -57,7 +57,7 @@ class MockBundle { _compile(id) { return wait(1). - then(_ => { + then(() => { if(id) { this.executionLog.push(id); } @@ -67,6 +67,6 @@ class MockBundle { function wait(delay) { return new Promise(resolve => { - setTimeout(_ => { resolve(); }, delay); + setTimeout(() => { resolve(); }, delay); }); } diff --git a/test/test_server.js b/test/test_server.js index f1de90c..e7f77a4 100644 --- a/test/test_server.js +++ b/test/test_server.js @@ -6,7 +6,7 @@ let assert = require("assert"); let assertSame = assert.strictEqual; -describe("server host parsing", _ => { +describe("server host parsing", () => { let { exit } = process; before(() => { diff --git a/test/test_util.js b/test/test_util.js index 7bc251f..ba36a99 100644 --- a/test/test_util.js +++ b/test/test_util.js @@ -11,7 +11,7 @@ let assertDeep = assert.deepStrictEqual; let FIXTURES_PATH = path.resolve(__dirname, "fixtures"); -describe("fingerprinting", _ => { +describe("fingerprinting", () => { it("generates a content-dependent hash", () => { let fingerprint = generateFingerprint("/path/to/foo.js", "lorem ipsum"); assertSame(fingerprint, "/path/to/foo-80a751fde577028640c419000e33eba6.js"); @@ -26,7 +26,7 @@ describe("fingerprinting", _ => { }); }); -describe("FileFinder", _ => { +describe("FileFinder", () => { it("finds all files within a folder", () => { let fileFinder = new FileFinder(FIXTURES_PATH);