Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: ESM! #127

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
6 changes: 2 additions & 4 deletions bin/faucet
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env node
"use strict";

let { faucetDispatch } = require("../lib");
let { parseCLI } = require("../lib/cli");
import { faucetDispatch } from "../lib/index.js";
import { parseCLI } from "../lib/cli.js";

parseCLI().
then(({ referenceDir, config, options }) => {
Expand Down
12 changes: 5 additions & 7 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use strict";

let { readConfig } = require("./config");
let { abort, repr } = require("./util");
let parseArgs = require("minimist");
import { readConfig } from "./config.js";
import { abort, repr } from "./util/index.js";
import parseArgs from "minimist";

let HELP = `
Usage:
Expand All @@ -27,7 +25,7 @@ Options:
serve generated files via HTTP with live reloading
`.trim();

exports.parseCLI = async function parseCLI(argv = process.argv.slice(2), help = HELP) {
export async function parseCLI(argv = process.argv.slice(2), help = HELP) {
argv = parseArgs(argv, {
boolean: ["watch", "fingerprint", "sourcemaps", "compact"],
alias: {
Expand Down Expand Up @@ -58,4 +56,4 @@ exports.parseCLI = async function parseCLI(argv = process.argv.slice(2), help =
let rootDir = process.cwd();
let { referenceDir, config } = await readConfig(rootDir, argv.config);
return { referenceDir, config, options };
};
}
10 changes: 4 additions & 6 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"use strict";
import path from "path";

let path = require("path");

exports.readConfig = async function readConfig(rootDir, filepath = "faucet.config.js") {
export async function readConfig(rootDir, filepath = "faucet.config.js") {
let configPath = path.resolve(rootDir, filepath);
return {
referenceDir: path.dirname(configPath),
config: await require(configPath)
config: await import(configPath)
};
};
}
26 changes: 12 additions & 14 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use strict";
import { liveServer, staticServer } from "./server.js";
import { pluginsByBucket } from "./plugins.js";
import { AssetManager } from "./manager.js";
import { resolvePath } from "./util/resolve.js";
import { abort, repr } from "./util/index.js";
import { SerializedRunner } from "./util/runner.js";
import browserslist from "browserslist";

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");

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

Expand Down Expand Up @@ -51,19 +49,19 @@ exports.faucetDispatch = async function faucetDispatch(referenceDir, config,
abort("ERROR: serve and liveserve must not be used together");
}
if(serve) {
server.static(serve, assetManager.manifest.webRoot);
staticServer(serve, assetManager.manifest.webRoot);
} else if(liveserve) {
server.live(liveserve, assetManager.manifest.webRoot);
liveServer(liveserve, assetManager.manifest.webRoot);
}
};
}

function buildStep(plugins) {
return files => Promise.all(plugins.map(plugin => plugin(files))).
then(() => files);
}

async function makeWatcher(watchDirs, referenceDir) {
let niteOwl = await require("nite-owl");
let niteOwl = await import("nite-owl");

if(watchDirs) {
watchDirs = watchDirs.map(dir => resolvePath(dir, referenceDir,
Expand Down
16 changes: 7 additions & 9 deletions lib/manager.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"use strict";
import { Manifest } from "./manifest.js";
import { createFile } from "./util/files/index.js";
import { resolvePath } from "./util/resolve.js";
import { reportFileStatus, abort, generateFingerprint } from "./util/index.js";
import path from "path";

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

exports.AssetManager = class AssetManager {
export class AssetManager {
constructor(referenceDir, { manifestConfig, fingerprint, exitOnError } = {}) {
this.referenceDir = referenceDir;
this.fingerprint = fingerprint;
Expand Down Expand Up @@ -57,4 +55,4 @@ exports.AssetManager = class AssetManager {
actualPath = path.relative(referenceDir, actualPath);
return this.manifest.set(originalPath, actualPath, targetDir);
}
};
}
14 changes: 6 additions & 8 deletions lib/manifest.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"use strict";
import { createFile } from "./util/files/index.js";
import { resolvePath } from "./util/resolve.js";
import { abort } from "./util/index.js";
import path from "path";

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

exports.Manifest = class Manifest {
export class Manifest {
constructor(referenceDir, { target, key, value, baseURI, webRoot } = {}) {
if(value && (baseURI || webRoot)) {
abort("ERROR: `value` must not be used with `baseURI` and/or `webRoot`");
Expand Down Expand Up @@ -52,4 +50,4 @@ exports.Manifest = class Manifest {
return memo;
}, {});
}
};
}
15 changes: 4 additions & 11 deletions lib/plugins.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

let { loadExtension, abort, repr } = require("./util");
import { loadExtension, abort, repr } from "./util/index.js";

// common plugins included for convenience
let DEFAULTS = [{
Expand All @@ -26,15 +24,10 @@ let DEFAULT_KEYS = DEFAULTS.reduce((memo, plugin) => {
}, new Set());
let BUCKETS = new Set(["static", "scripts", "styles", "markup"]);

module.exports = {
pluginsByBucket,
_determinePlugins: determinePlugins
};

// returns plugin functions grouped by bucket and filtered by relevance (based
// on configuration)
async function pluginsByBucket(config, defaults) {
let plugins = await determinePlugins(config.plugins, defaults);
export async function pluginsByBucket(config, defaults) {
let plugins = await _determinePlugins(config.plugins, defaults);
let buckets = [...BUCKETS].reduce((memo, bucket) => {
memo[bucket] = [];
return memo;
Expand All @@ -60,7 +53,7 @@ async function pluginsByBucket(config, defaults) {
// `plugins` is an array of plugins, each either a package identifier or a
// `{ key, bucket, plugin }` object`, with `key` being the configuration key and
// `plugin` being either a function or a package identifier
async function determinePlugins(plugins = [], defaults = DEFAULTS) {
export async function _determinePlugins(plugins = [], defaults = DEFAULTS) {
let registry = {};
// NB: default plugins are resolved lazily because eager loading would
// result in them becoming a hard dependency rather than a convenience
Expand Down
26 changes: 11 additions & 15 deletions lib/server.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
let { loadExtension, abort, repr } = require("./util");
import { loadExtension, abort, repr } from "./util/index.js";

let DEFAULTS = {
host: "localhost",
port: 3000
};

exports.static = async (config, webroot) => {
let donny = await loadExtension("donny", "failed to activate server");
let [host, port] = parseHost(config);
export async function liveServer(config, root) {
let server = await loadExtension("live-server", "failed to activate live-server");
let [host, port] = _parseHost(config);
server.start({ port, host, root, open: false });
}

export async function staticServer(config, webroot) {
let donny = await loadExtension("donny", "failed to activate server");
let [host, port] = _parseHost(config);
await donny({ port, bind: host, 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) {
export function _parseHost(config) {
let { host, port } = DEFAULTS;
if(config === true) {
return [host, port];
Expand Down
8 changes: 4 additions & 4 deletions lib/util/files/finder.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
let { readdir, stat } = require("fs/promises");
let path = require("path");
import { readdir, stat } from "fs/promises";
import path from "path";

exports.FileFinder = class FileFinder {
export class FileFinder {
constructor(directory, { skipDotfiles, filter = () => true } = {}) {
this.directory = directory;
this.filter = filename => {
Expand All @@ -23,7 +23,7 @@ exports.FileFinder = class FileFinder {
return filesWithinDirectory(this.directory, filepaths).
then(filepaths => filepaths.filter(this.filter));
}
};
}

function tree(filepath, referenceDir = filepath) {
return stat(filepath).
Expand Down
12 changes: 5 additions & 7 deletions lib/util/files/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use strict";

let { writeFile } = require("fs/promises");
let { mkdirSync } = require("fs");
let path = require("path");
import { writeFile } from "fs/promises";
import { mkdirSync } from "fs";
import path from "path";

let KNOWN = new Set(); // avoids redundant `mkdir` invocations
let LOCKS = new Map();

// avoids concurrent write operations and creates target directory if necessary
exports.createFile = function createFile(filepath, contents) {
export function createFile(filepath, contents) {
let lock = LOCKS.get(filepath);
if(lock) { // defer
return lock.then(() => createFile(filepath, contents));
Expand All @@ -26,4 +24,4 @@ exports.createFile = function createFile(filepath, contents) {
return prom.then(() => {
LOCKS.delete(filepath);
});
};
}
29 changes: 12 additions & 17 deletions lib/util/index.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,40 @@
"use strict";

let path = require("path");
let crypto = require("crypto");

exports.abort = abort;
exports.repr = repr;
import path from "path";
import crypto from "crypto";

// reports success or failure for a given file path (typically regarding
// compilation or write operations)
exports.reportFileStatus = (filepath, referenceDir, error) => {
export function reportFileStatus(filepath, referenceDir, error) {
let ref = path.relative(referenceDir, filepath);
console.error(error ? `✗ ${ref}: ${error.message || error}` : `✓ ${ref}`);
};
}

// attempts to load a module, prompting the user to install the corresponding
// package if it is unavailable
exports.loadExtension = async (pkg, errorMessage, supplier = pkg) => {
export async function loadExtension(pkg, errorMessage, supplier = pkg) {
try {
return await require(pkg);
return await import(pkg);
} catch(err) {
if(err.code !== "MODULE_NOT_FOUND") {
if(err.code !== "ERR_MODULE_NOT_FOUND") {
throw err;
}
abort(`${errorMessage} - please install ${repr(supplier)}`);
}
};
}

exports.generateFingerprint = (filepath, data) => {
export function generateFingerprint(filepath, data) {
let filename = path.basename(filepath);
let ext = filename.indexOf(".") === -1 ? "" : "." + filename.split(".").pop();
let name = ext.length === 0 ? filename : path.basename(filepath, ext);
let hash = generateHash(data);
return path.join(path.dirname(filepath), `${name}-${hash}${ext}`);
};
}

function abort(msg, code = 1) {
export function abort(msg, code = 1) {
console.error(msg);
process.exit(code);
}

function repr(value, jsonify = true) {
export function repr(value, jsonify = true) {
if(jsonify) {
value = JSON.stringify(value);
}
Expand Down
15 changes: 6 additions & 9 deletions lib/util/resolve.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
"use strict";
import { abort, repr } from "./index.js";
import { statSync } from "fs";
import path from "path";

let { abort, repr } = require("./");
let fs = require("fs");
let path = require("path");

exports.resolvePath = function resolvePath(filepath, referenceDir,
{ enforceRelative } = {}) {
export function resolvePath(filepath, referenceDir, { enforceRelative } = {}) {
if(/^\.?\.\//.test(filepath)) { // starts with `./` or `../`
return path.resolve(referenceDir, filepath);
} else if(enforceRelative) {
Expand All @@ -19,11 +16,11 @@ exports.resolvePath = function resolvePath(filepath, referenceDir,
// resolution algorithm)
let resolved = path.resolve(referenceDir, "node_modules", filepath);
try {
fs.statSync(resolved); // ensures file/directory exists
statSync(resolved); // ensures file/directory exists
} catch(_err) {
abort(`ERROR: could not resolve ${repr(filepath)}`);
}
return resolved;
}
}
};
}
6 changes: 2 additions & 4 deletions lib/util/runner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

exports.SerializedRunner = class SerializedRunner {
export class SerializedRunner {
constructor(asyncOp) {
this.asyncOp = asyncOp;
}
Expand Down Expand Up @@ -32,7 +30,7 @@ exports.SerializedRunner = class SerializedRunner {
}
return res;
}
};
}

function augment(promise) {
promise.finally = always;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"bugs": {
"url": "https://github.com/faucet-pipeline/faucet-pipeline-core/issues"
},
"type": "module",
"main": "lib/index.js",
"bin": {
"faucet": "bin/faucet"
Expand Down
4 changes: 1 addition & 3 deletions test/bin/extract-sourcemap
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env node
"use strict";

let { readFileSync } = require("fs");
import { readFileSync } from "fs";

let URL_PATTERN = /sourceMappingURL=data:(\S+)/;
let MAP_PATTERN = /;base64,(\S+)$/;
Expand Down
8 changes: 3 additions & 5 deletions test/fixtures/node_modules/faucet-pipeline-dummy/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading