diff --git a/eslint.config.js b/eslint.config.js index cc4fd76..383ed92 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,3 @@ -var gulpConfig = require("eslint-config-gulp"); +import gulpConfig from "eslint-config-gulp"; -module.exports = [...gulpConfig.default]; +export default [...gulpConfig]; diff --git a/index.js b/index.js index 8620443..5b48939 100755 --- a/index.js +++ b/index.js @@ -1,76 +1,86 @@ #!/usr/bin/env node -const fs = require(`fs`); -const path = require(`path`); -const yaml = require(`js-yaml`); -const yargs = require(`yargs`); -const { Semver } = require(`sver`); -const remoteGitTags = require(`remote-git-tags`); -const boilerplateUpdate = require(`boilerplate-update`); -const { execSync } = require("child_process"); +import fs from "node:fs"; +import path from "node:path"; +import { execSync } from "node:child_process"; +import { parseArgs } from "node:util"; -const REMOTE_URL = `https://github.com/gulpjs/.boilerplate`; +import yaml from "js-yaml"; +import { Semver } from "sver"; +import remoteGitTags from "remote-git-tags"; +import boilerplateUpdate from "boilerplate-update"; + +import packageJSON from "./package.json" with { type: "json" }; + +const REMOTE_URL = "https://github.com/gulpjs/.boilerplate"; function trimV(tag) { - return tag[0] === `v` ? tag.slice(1) : tag; + return tag[0] === "v" ? tag.slice(1) : tag; } // This function makes assumption about the layout of .boilerplate // at specific tags to determine a starting point for updating -function guessStartTag() { +async function guessStartTag() { // `.jscsrc` was used before we added Azure Pipelines - if (fs.existsSync(`.jscsrc`)) { - return `1.0.0`; + if (fs.existsSync(".jscsrc")) { + return "1.0.0"; } // `.ci` was added to support Azure Pipelines - if (fs.existsSync(`.ci`)) { - return `2.0.0`; + if (fs.existsSync(".ci")) { + return "2.0.0"; } // If we are in the 3.x release stream, tiny changes were made to the workflow.yml try { - const devFile = fs.readFileSync(`.github/workflows/dev.yml`, `utf-8`); + const devFile = fs.readFileSync(".github/workflows/dev.yml", "utf-8"); const workflow = yaml.safeLoad(devFile); // We started 3.0.0 with `on: [push, pull_request]` but quickly changed // to avoid running the workflow on pushing tags if (Array.isArray(workflow.on)) { - return `3.0.0`; + return "3.0.0"; } - // In 3.0.1, we didn`t check that the event was a push and that failed PRs - if (!workflow.jobs.prettier.if) { - return `3.0.1`; + // In 3.0.1, we didn't check that the event was a push and that failed PRs + if (workflow.jobs.prettier && !workflow.jobs.prettier.if) { + return "3.0.1"; } - const pkg = require(path.join(process.cwd(), `package.json`)); + const { default: pkg } = await import( + path.join(process.cwd(), "package.json"), + { + with: { type: "json" }, + } + ); // In 3.0.2, we updated the node version in package.json but forgot to remove some unused files - if (pkg.engines.node === `>=10.13.0`) { - const hasEslintignore = fs.existsSync(`.eslintignore`); + if (pkg.engines.node === ">=10.13.0") { + const hasEslintignore = fs.existsSync(".eslintignore"); // We forgot to remove .eslintignore and some other files so we check for that if (hasEslintignore) { - return `3.0.2`; + return "3.0.2"; } } - const releaseWorkflow = fs.existsSync(`.github/workflows/release.yml`); + const hasReleaseWorkflow = fs.existsSync(".github/workflows/release.yml"); // In 3.1.0, we add the release.yml workflow file - if (!releaseWorkflow) { - return `3.0.3`; + if (!hasReleaseWorkflow) { + return "3.0.3"; } - if (releaseWorkflow) { - return `3.1.0`; + const hasNpmrc = fs.existsSync(".npmrc"); + if (hasReleaseWorkflow && hasNpmrc) { + return "3.1.0"; } // 4.0.0 had a bug with repository metadata in package.json - return `4.0.1`; + return "4.0.1"; } catch (err) { + console.error(err); // Always fallback to 1.0.0 - return `1.0.0`; + return "1.0.0"; } } @@ -88,31 +98,46 @@ async function guessEndTag() { return versions.sort(Semver.compare).pop(); } -function opts(yargs) { - yargs.option(`start-tag`, { - alias: `s`, - describe: `Git tag of starting template (guessed if empty)`, - type: `string`, - }); - - yargs.option(`end-tag`, { - alias: `e`, - describe: `Git tag of ending template (guessed if empty)`, - type: `string`, - }); -} - -async function guessTags(argv) { - let startTag = argv.startTag; +const options = { + help: { + type: "boolean", + short: "h", + describe: "Show help", + spacing: 5, + }, + version: { + type: "boolean", + short: "v", + describe: "Show version number", + spacing: 2, + }, + "start-tag": { + type: "string", + short: "s", + describe: "Git tag of starting template (guessed if empty)", + spacing: 0, + }, + "end-tag": { + type: "string", + short: "s", + describe: "Git tag of ending template (guessed if empty)", + spacing: 2, + }, +}; + +async function guessTags(values) { + let startTag = values["start-tag"]; if (!startTag) { - startTag = guessStartTag(); + startTag = await guessStartTag(); + console.log(`Guessed start-tag: ${startTag}`); } else { startTag = trimV(startTag); } - let endTag = argv.endTag; + let endTag = values["end-tag"]; if (!endTag) { endTag = await guessEndTag(); + console.log(`Guessed end-tag: ${endTag}`); } else { endTag = trimV(endTag); } @@ -123,19 +148,49 @@ async function guessTags(argv) { }; } -async function run(argv) { - if (!argv.startTag) { - return Promise.reject(`--start-tag is required & couldn't be guessed`); +async function run(values) { + if (values.help) { + let help = `update-template + + Update a gulp repository to our template. + + Options:`; + for (const [flag, config] of Object.entries(options)) { + const leadingSpaces = Array.from(Array(config.spacing), () => " ").join( + "", + ); + const trailingSpaces = config.type === "boolean" ? "" : " "; + help += "\n"; + help += ` --${flag}, -${config.short} ${leadingSpaces}[${config.type}]${trailingSpaces} ${config.describe}`; + } + console.log(help); + return; } - if (!argv.endTag) { - return Promise.reject(`--end-tag is required & couldn't be guessed`); + if (values.version) { + console.log(packageJSON.version); + return; + } + + const { startTag, endTag } = await guessTags(values); + + if (!startTag) { + return Promise.reject("--start-tag is required & couldn't be guessed"); + } + + if (!endTag) { + return Promise.reject("--end-tag is required & couldn't be guessed"); + } + + if (startTag === endTag) { + console.log(`Tags match (${startTag}), nothing to apply`); + return; } var { promise: boilerplateUpdatePromise } = await boilerplateUpdate({ remoteUrl: REMOTE_URL, - startVersion: argv.startTag, - endVersion: argv.endTag, + startVersion: startTag, + endVersion: endTag, resolveConflicts: { stdio: "inherit", }, @@ -144,7 +199,7 @@ async function run(argv) { await boilerplateUpdatePromise; - if (argv.endTag === `4.0.1`) { + if (endTag === "4.0.1") { console.log("fixing npm package..."); execSync("npm pkg fix"); console.log("updating lockfile..."); @@ -153,6 +208,14 @@ async function run(argv) { } } -yargs.command(`$0`, `Update a gulp repository to our template`, opts, run, [ - guessTags, -]).argv; +try { + const { values } = parseArgs({ options, strict: true }); + await run(values); +} catch (err) { + if (err.message) { + console.error(err.message); + } else { + console.error(err); + } + process.exit(1); +} diff --git a/package-lock.json b/package-lock.json index 58c51a7..d641e9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,7 @@ "boilerplate-update": "npm:@phated/boilerplate-update@^0.37.6", "js-yaml": "^3.14.0", "remote-git-tags": "^3.0.0", - "sver": "^1.8.3", - "yargs": "^15.3.1" + "sver": "^1.8.3" }, "bin": { "update-template": "index.js" @@ -844,15 +843,6 @@ "qified": "^0.10.1" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -920,17 +910,6 @@ "node": ">= 10" } }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, "node_modules/code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -1059,15 +1038,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -7531,15 +7501,6 @@ "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/pacote": { "version": "11.1.11", "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.1.11.tgz", @@ -7589,6 +7550,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7804,12 +7766,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -8383,12 +8339,6 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC" - }, "node_modules/wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -8451,60 +8401,18 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", @@ -8514,71 +8422,6 @@ "node": ">=10" } }, - "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 98a4437..3e1ac86 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "engines": { "node": ">= 22.15.0" }, + "type": "module", "main": "./index.js", "bin": { "update-template": "index.js" @@ -33,8 +34,7 @@ "boilerplate-update": "npm:@phated/boilerplate-update@^0.37.6", "js-yaml": "^3.14.0", "remote-git-tags": "^3.0.0", - "sver": "^1.8.3", - "yargs": "^15.3.1" + "sver": "^1.8.3" }, "devDependencies": { "eslint": "10.11.0", diff --git a/test/index.js b/test/index.js index d41fb64..a0cd965 100644 --- a/test/index.js +++ b/test/index.js @@ -1,8 +1,8 @@ -var cp = require("node:child_process"); -var assert = require("node:assert"); -var { describe, it } = require("node:test"); +import cp from "node:child_process"; +import assert from "node:assert"; +import { describe, it } from "node:test"; -var command = require.resolve("../"); +var command = import.meta.resolve("../index.js"); describe("update-template", function () { // Just a dummy test to make CI pass