Skip to content

Commit

Permalink
feat: support only ESM (#950)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Node.js 12.20+ is required and CommonJS is unsupported
  • Loading branch information
ybiquitous committed May 6, 2021
1 parent 6afdb1d commit f7271d6
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 150 deletions.
12 changes: 5 additions & 7 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
#!/usr/bin/env node
const yargs = require("yargs");
import yargs from "yargs";
import { hideBin } from "yargs/helpers"; // eslint-disable-line sort-imports

function cli() {
// eslint-disable-next-line no-unused-expressions
yargs
export default async function cli() {
yargs(hideBin(process.argv)) // eslint-disable-line no-unused-expressions
.usage("$0 <command>")
.command(require("./init"))
.command(await import("./init.js")) // eslint-disable-line node/no-unsupported-features/es-syntax
.demandCommand(1)
.strict()
.alias("help", "h")
.alias("version", "v").argv;
}

cli();

module.exports = cli;
4 changes: 1 addition & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
const { init } = require("./init");

module.exports.init = init;
export { init } from "./init.js";
67 changes: 36 additions & 31 deletions lib/init.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
const path = require("path");
const { promisify } = require("util");
const fs = require("fs");
const originalPackage = require("../package.json");
const { defaultLogger } = require("./logger");
import { promises as fsPromises } from "fs";
import { join, relative, resolve } from "path"; // eslint-disable-line sort-imports
import { inspect } from "util";
import { defaultLogger } from "./logger.js"; // eslint-disable-line sort-imports

const copy = promisify(fs.copyFile);
const write = promisify(fs.writeFile);
const read = promisify(fs.readFile);
const mkdir = promisify(fs.mkdir);
const { copyFile, mkdir, readFile, writeFile } = fsPromises;

/**
* @param {unknown} value
*/
const emphasize = (value) => inspect(value, { colors: true });

const PACKAGE_DIR = new URL("..", import.meta.url).pathname;

/**
* @param {string} elem
* @param {...string} elems
*/
const packagePath = (elem, ...elems) => path.join(...[__dirname, "..", elem, ...elems]);
const packagePath = (elem, ...elems) => join(PACKAGE_DIR, elem, ...elems);

/**
* @param {string} baseDir
Expand All @@ -25,37 +28,37 @@ const initCommand = (baseDir, logger) => {
* @param {string} elem
* @param {...string} elems
*/
const currentPath = (elem, ...elems) => path.join(...[baseDir, elem, ...elems]);
const currentPath = (elem, ...elems) => join(baseDir, elem, ...elems);

/**
* @param {string} fileName
*/
const readFile = (fileName) => read(currentPath(fileName), "utf8");
const read = (fileName) => readFile(currentPath(fileName), "utf8");

/**
* @param {string} src
* @param {string} dest
*/
const copyFile = async (src, dest) => {
await mkdir(path.resolve(dest, ".."), { recursive: true });
await copy(src, dest);
logger(`=> \`${path.relative(baseDir, dest)}\` was updated`);
const copy = async (src, dest) => {
await mkdir(resolve(dest, ".."), { recursive: true });
await copyFile(src, dest);
logger(`=> ${emphasize(relative(baseDir, dest))} was updated`);
};

/**
* @param {string} fileName
* @param {string} fileContent
*/
const writeFile = async (fileName, fileContent) => {
const write = async (fileName, fileContent) => {
const file = currentPath(fileName);
await write(file, `${fileContent}\n`);
logger(`=> \`${path.relative(baseDir, file)}\` was updated`);
await writeFile(file, `${fileContent}\n`);
logger(`=> ${emphasize(relative(baseDir, file))} was updated`);
};

return {
// eslint-disable-next-line max-statements, max-lines-per-function
async updatePackageFile() {
const packageInfo = JSON.parse(await readFile("package.json"));
const packageInfo = JSON.parse(await read("package.json"));

// update 'scripts'
if (!("scripts" in packageInfo)) {
Expand All @@ -68,6 +71,11 @@ const initCommand = (baseDir, logger) => {
scripts["test:watch"] = `${scripts.test} --watch`;
scripts["test:coverage"] = `${scripts.test} --coverage`;

const originalPackage = JSON.parse(
// @ts-expect-error
await readFile(new URL("../package.json", import.meta.url))
);

for (const [key, script] of Object.entries(originalPackage.scripts)) {
if (!(key === "test" || key.startsWith("test:"))) {
scripts[key] = script;
Expand All @@ -87,13 +95,15 @@ const initCommand = (baseDir, logger) => {
updateOtherKey("standard-version");
updateOtherKey("remarkConfig");

// eslint-disable-next-line require-atomic-updates
packageInfo.commitlint = {
extends: ["@commitlint/config-conventional"],
rules: {
"body-max-line-length": [1, "always", 100],
},
};

// eslint-disable-next-line require-atomic-updates
packageInfo.eslintConfig = {
root: true,
extends: ["ybiquitous"],
Expand All @@ -104,22 +114,22 @@ const initCommand = (baseDir, logger) => {
// Delete husky v4 configuration.
delete packageInfo.husky;

await writeFile("package.json", JSON.stringify(packageInfo, null, 2));
await write("package.json", JSON.stringify(packageInfo, null, 2));
},

/**
* @param {string} name
* @param {...string} names
*/
async writePackageFile(name, ...names) {
await copyFile(packagePath(name, ...names), currentPath(name, ...names));
await copy(packagePath(name, ...names), currentPath(name, ...names));
},
};
};

/** @type {import("../types/ybiq").InitCommand} */
// eslint-disable-next-line max-statements
async function init({ cwd = process.cwd(), logger = defaultLogger } = {}) {
export async function init({ cwd = process.cwd(), logger = defaultLogger } = {}) {
const cmd = initCommand(cwd, logger);

await cmd.updatePackageFile();
Expand All @@ -133,16 +143,11 @@ async function init({ cwd = process.cwd(), logger = defaultLogger } = {}) {
await cmd.writePackageFile(".husky", "post-commit");
await cmd.writePackageFile(".husky", "pre-commit");
}
module.exports.init = init;

module.exports.command = "init";
export const command = "init";

module.exports.describe = `Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\``;
export const describe = "Setup npm project, e.g. update 'package.json'";

module.exports.handler = async () => {
export const handler = async () => {
await init();
};
4 changes: 2 additions & 2 deletions lib/logger.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { EOL } = require("os");
import { EOL } from "os";

/** @type {import("../types/ybiq").Logger} */
module.exports.defaultLogger = (msg) => {
export const defaultLogger = (msg) => {
process.stdout.write(`${msg}${EOL}`);
};
2 changes: 1 addition & 1 deletion package-lock.json

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

20 changes: 15 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"utility",
"tool"
],
"type": "module",
"main": "lib/index.js",
"bin": "lib/cli.js",
"types": "types/ybiq.d.ts",
Expand All @@ -28,7 +29,7 @@
".husky/pre-commit"
],
"engines": {
"node": ">=12.13.0"
"node": "^12.20 || >=14.13"
},
"dependencies": {
"@commitlint/cli": "^12.1.1",
Expand All @@ -53,9 +54,9 @@
"typescript": "^4.2.4"
},
"scripts": {
"test": "jest --coverage && npm run test:types",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --coverage && npm run test:types",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
"test:types": "tsc --project test/tsconfig.test.json --noEmit",
"lint:js": "eslint .",
"lint:js:fix": "npm run lint:js -- --fix",
Expand Down Expand Up @@ -135,8 +136,17 @@
"extends": [
"ybiquitous/node"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"env": {
"es2020": true
},
"rules": {
"import/no-internal-modules": "off"
"import/extensions": "off",
"import/no-internal-modules": "off",
"import/prefer-default-export": "off"
},
"overrides": [
{
Expand Down
48 changes: 8 additions & 40 deletions test/__snapshots__/help.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ exports[`with "--help" option 1`] = `
"cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -20,11 +16,7 @@ exports[`with "-h" option 1`] = `
"cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -37,11 +29,7 @@ exports[`with arguments [] 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -56,11 +44,7 @@ exports[`with arguments []: stderr 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -75,11 +59,7 @@ exports[`with arguments [unknown, xyz] 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -94,11 +74,7 @@ exports[`with arguments [unknown, xyz]: stderr 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -113,11 +89,7 @@ exports[`with arguments [unknown] 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand All @@ -132,11 +104,7 @@ exports[`with arguments [unknown]: stderr 1`] = `
cli.js <command>
Commands:
cli.js init Setup npm project:
- Update \`package.json\`
- Create \`.editorconfig\`
- Create \`.remarkignore\`
- Create \`.github/workflows/*.yml\`
cli.js init Setup npm project, e.g. update 'package.json'
Options:
-h, --help Show help [boolean]
Expand Down
4 changes: 2 additions & 2 deletions test/help.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const pkg = require("../package.json");
const exec = require("./helpers/exec");
import { exec } from "./helpers/exec.js";
import { pkg } from "./helpers/pkg.js";

[[], ["unknown"], ["unknown", "xyz"]].forEach((args) => {
test(`with arguments [${args.join(", ")}]`, async () => {
Expand Down
23 changes: 7 additions & 16 deletions test/helpers/exec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const cp = require("child_process");
import { execFile as originalExecFile } from "child_process";
import { promisify } from "util";

module.exports = function exec(commandFile, ...args) {
const execFile = promisify(originalExecFile);

export function exec(commandFile, ...args) {
const options = {
env: { ...process.env, LC_ALL: "C" },
};
Expand All @@ -11,17 +14,5 @@ module.exports = function exec(commandFile, ...args) {
options.cwd = lastArg.cwd;
newArgs = args.slice(0, lastArgIndex);
}
return new Promise((resolve, reject) => {
cp.execFile(commandFile, newArgs, options, (error, stdout, stderr) => {
if (error) {
/* eslint-disable no-param-reassign */
error.stdout = stdout;
error.stderr = stderr;
/* eslint-enable no-param-reassign */
reject(error);
} else {
resolve({ stdout, stderr });
}
});
});
};
return execFile(commandFile, newArgs, options);
}
3 changes: 3 additions & 0 deletions test/helpers/pkg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { readFileSync } from "fs";

export const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
Loading

0 comments on commit f7271d6

Please sign in to comment.