Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use strict";

let { readConfig } = require("./config");
let { abort, repr } = require("./util");
let { parseArgs } = require("node:util");
let path = require("node:path");

let HELP = `
Usage:
Expand All @@ -23,8 +23,6 @@ Options:
reduce output size (where supported)
--serve [HOST:]PORT
serve generated files via HTTP
--liveserve [HOST:]PORT
serve generated files via HTTP with live reloading
`.trim();

exports.parseCLI = async function parseCLI() {
Expand All @@ -37,7 +35,8 @@ exports.parseCLI = async function parseCLI() {
},
config: {
type: "string",
short: "c"
short: "c",
default: "faucet.config.js"
},
watch: {
type: "boolean",
Expand Down Expand Up @@ -69,21 +68,14 @@ exports.parseCLI = async function parseCLI() {
abort(HELP, 0);
}

let options = {
watch: values.watch,
fingerprint: values.fingerprint,
sourcemaps: values.sourcemaps,
compact: values.compact,
serve: values.serve,
liveserve: values.liveserve
};

if(options.watch && options.fingerprint) { // for convenience
console.error("you might consider disabling fingerprinting in watch " +
"mode to avoid littering your file system with obsolete bundles");
if(values.liveserve) {
abort("The --liveserve option was removed. Please use --serve instead", 0);
}

let rootDir = process.cwd();
let { referenceDir, config } = await readConfig(rootDir, values.config);
return { referenceDir, config, options };
let configPath = path.resolve(process.cwd(), values.config);
return {
referenceDir: path.dirname(configPath),
config: await require(configPath),
options: values
};
};
11 changes: 0 additions & 11 deletions lib/config.js

This file was deleted.

38 changes: 14 additions & 24 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,22 @@
let server = require("./server");
let { pluginsByBucket } = require("./plugins");
let { AssetManager } = require("./manager");
let { resolvePath } = require("./util/resolve");
let { abort, repr } = require("./util");
let { SerializedRunner } = require("./util/runner");
let browserslist = require("browserslist");
let { abort, repr, resolvePath, loadExtension } = require("./util");
let { SerializedRunner } = require("./runner");

exports.faucetDispatch = async function faucetDispatch(referenceDir, config,
{ watch, fingerprint, sourcemaps, compact, serve, liveserve }) {
config = await config;

{ watch, fingerprint, sourcemaps, compact, serve }) {
let assetManager = new AssetManager(referenceDir, {
manifestConfig: config.manifest,
fingerprint,
exitOnError: !watch
});
let browsers = browserslist.findConfig(referenceDir) || {};
if(browsers.substr) {
browsers = [browsers];
}

let plugins = await pluginsByBucket(config);
// initialize plugins with corresponding configuration
let buckets = Object.keys(plugins).reduce((memo, bucket) => {
memo[bucket] = plugins[bucket].map(({ plugin, config }) => {
return plugin(config, assetManager, { browsers, sourcemaps, compact });
return plugin(config, assetManager, { sourcemaps, compact });
});
return memo;
}, {});
Expand All @@ -39,21 +31,18 @@ exports.faucetDispatch = async function faucetDispatch(referenceDir, config,
runner.run();

if(watch) {
makeWatcher(config.watchDirs, referenceDir).
then(watcher => {
watcher.on("edit", filepaths => {
runner.rerun(filepaths);
});
});
if(fingerprint) {
console.error("you might consider disabling fingerprinting in watch " +
"mode to avoid littering your file system with obsolete bundles");
}
let watcher = await makeWatcher(config.watchDirs, referenceDir);
watcher.on("edit", filepaths => {
runner.rerun(filepaths);
});
}

if(serve && liveserve) {
abort("ERROR: serve and liveserve must not be used together");
}
if(serve) {
server.static(serve, assetManager.manifest.webRoot);
} else if(liveserve) {
server.live(liveserve, assetManager.manifest.webRoot);
}
};

Expand All @@ -63,7 +52,8 @@ function buildStep(plugins) {
}

async function makeWatcher(watchDirs, referenceDir) {
let niteOwl = await require("nite-owl");
// XXX: Rename to faucet-pipeline-watch?
let niteOwl = await loadExtension("nite-owl", "failed to activate watching");

if(watchDirs) {
watchDirs = watchDirs.map(dir => resolvePath(dir, referenceDir,
Expand Down
29 changes: 19 additions & 10 deletions lib/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

let { Manifest } = require("./manifest");
let { createFile } = require("./util/files");
let { resolvePath } = require("./util/resolve");
let { reportFileStatus, abort, generateFingerprint } = require("./util");
let { reportFileStatus, abort, resolvePath } = require("./util");
let path = require("path");
let crypto = require("crypto");

exports.AssetManager = class AssetManager {
constructor(referenceDir, { manifestConfig, fingerprint, exitOnError } = {}) {
Expand Down Expand Up @@ -43,18 +43,27 @@ exports.AssetManager = class AssetManager {
});
}

get packagesDir() {
let memo = this._packagesDir;
if(!memo) {
memo = this._packagesDir = this.resolvePath("./node_modules");
}
return memo;
}

_updateManifest(originalPath, actualPath, targetDir) {
let { referenceDir } = this;
originalPath = path.relative(referenceDir, originalPath);
actualPath = path.relative(referenceDir, actualPath);
return this.manifest.set(originalPath, actualPath, targetDir);
}
};

function generateFingerprint(filepath, data) {
let filename = path.basename(filepath);
let ext = filename.indexOf(".") === -1 ? "" : "." + filename.split(".").pop();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not using path.extname here? (See potentially related discussion for faucet-static/static-images.)

(Though we might reasonably opt not to risk changing the implementation here, due to backwards-compatibility concerns.)

let name = ext.length === 0 ? filename : path.basename(filepath, ext);
let hash = generateHash(data);
return path.join(path.dirname(filepath), `${name}-${hash}${ext}`);
}

// exported for testing
exports.generateFingerprint = generateFingerprint;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
exports.generateFingerprint = generateFingerprint;
exports._generateFingerprint = generateFingerprint;

That makes it explicit; cf. _determinePlugins.


function generateHash(str) {
let hash = crypto.createHash("md5");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we perhaps change this to SHA-256? I realize that might be a backwards-compatibility concern though...

My results suggest that you should probably not be using MD5. MD5 is slower than SHA-256 and not as safe.

JavaScript hashing speed comparison: MD5 versus SHA-256

hash.update(str);
return hash.digest("hex");
}
7 changes: 3 additions & 4 deletions lib/manifest.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use strict";

let { createFile } = require("./util/files");
let { resolvePath } = require("./util/resolve");
let { abort } = require("./util");
let { abort, resolvePath } = require("./util");
let path = require("path");

exports.Manifest = class Manifest {
Expand All @@ -12,6 +11,7 @@
}
this.webRoot = webRoot = resolvePath(webRoot || "./", referenceDir,
{ enforceRelative: true });
this.baseURI = baseURI || "/";

if(target) {
this.filepath = resolvePath(target, referenceDir, { enforceRelative: true });
Expand All @@ -27,8 +27,7 @@
if(value) {
this.valueTransform = value;
} else {
baseURI = baseURI || "/";
this.valueTransform = filepath => baseURI + path.relative(webRoot, filepath);
this.valueTransform = filepath => this.baseURI + path.relative(webRoot, filepath);

Check warning on line 30 in lib/manifest.js

View workflow job for this annotation

GitHub Actions / build (18.x)

This line has a length of 94. Maximum allowed is 90

Check warning on line 30 in lib/manifest.js

View workflow job for this annotation

GitHub Actions / build (23.x)

This line has a length of 94. Maximum allowed is 90

Check warning on line 30 in lib/manifest.js

View workflow job for this annotation

GitHub Actions / build (22.x)

This line has a length of 94. Maximum allowed is 90

Check warning on line 30 in lib/manifest.js

View workflow job for this annotation

GitHub Actions / build (20.x)

This line has a length of 94. Maximum allowed is 90
}
}

Expand Down
4 changes: 4 additions & 0 deletions lib/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ let DEFAULTS = [{
key: "static",
bucket: "static",
plugin: "faucet-pipeline-static"
}, {
key: "assets",
bucket: "static",
plugin: "faucet-pipeline-static"
}, {
key: "images",
bucket: "static",
Expand Down
21 changes: 2 additions & 19 deletions lib/util/runner.js → lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exports.SerializedRunner = class SerializedRunner {

run(...args) {
if(!this._pending) { // prevent concurrent execution
this._pending = augment(this.asyncOp(...args)).
this._pending = this.asyncOp(...args).
finally(() => {
this._pending = null;
});
Expand All @@ -24,7 +24,7 @@ exports.SerializedRunner = class SerializedRunner {
let enqueue = this._pending;
let res = this.run(...args);
if(enqueue) {
this._queued = res = augment(res).
this._queued = res = res.
finally(() => {
this._queued = null;
}).
Expand All @@ -33,20 +33,3 @@ exports.SerializedRunner = class SerializedRunner {
return res;
}
};

function augment(promise) {
promise.finally = always;
return promise;
}

// poor man's `Promise#finally` polyfill
function always(fn) {
return this.
then(res => {
fn();
return res;
}, err => {
fn();
throw err;
});
}
7 changes: 0 additions & 7 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ exports.static = async (config, webroot) => {
console.error(`serving ${repr(webroot)} at http://${host}:${port}`);
};

exports.live = async (config, root) => {
let liveServer = await loadExtension("live-server", "failed to activate live-server");
let [host, port] = parseHost(config);

liveServer.start({ port, host, root, open: false });
};

exports._parseHost = parseHost;

function parseHost(config) {
Expand Down
26 changes: 26 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface FaucetPlugin<T> {
(config: T[], assetManager: AssetManager, options: FaucetPluginOptions): FaucetPluginFunc
}

export interface FaucetPluginFunc {
(filepaths: string[]): Promise<unknown>
}

export interface FaucetPluginOptions {
sourcemaps: boolean,
compact: boolean
}

export interface AssetManager {
resolvePath: (path: string, opts?: ResolvePathOpts) => string
writeFile: (targetPath: string, content: Buffer, options: WriteFileOpts) => Promise<unknown>
}

export interface ResolvePathOpts {
enforceRelative?: boolean
}

export interface WriteFileOpts {
targetDir: string,
fingerprint?: boolean
}
File renamed without changes.
59 changes: 0 additions & 59 deletions lib/util/files/finder.js

This file was deleted.

Loading