UNPKG

@darkobits/ts

Version:

Vite-based toolchain for Node projects.

208 lines (207 loc) 8.02 kB
import { EOL } from "node:os"; import path from "node:path"; import chalk from "chalk"; import { isCI } from "ci-info"; import { EXTENSIONS } from "../etc/constants.js"; import log from "../lib/log.js"; import { getPackageContext, inferESLintConfigurationStrategy } from "../lib/utils.js"; const defaultPackageScripts = async ({ command, fn, script }) => { const { root, srcDir, outDir, packageJson } = await getPackageContext(); const forwardArgs = process.argv.includes("--") ? process.argv.slice(process.argv.indexOf("--") + 1) : []; const lintRoot = srcDir || process.cwd(); const noOpLintFn = fn(() => log.warn(chalk.dim("No-op; missing ESLint configuration file."))); let lintInstruction = noOpLintFn; let lintFixInstruction = noOpLintFn; const eslintFlags = { format: "codeframe" }; const eslintEnvVars = {}; const eslintConfig = await inferESLintConfigurationStrategy(root); if (eslintConfig) { eslintFlags.config = eslintConfig.configFile; if (eslintConfig.type === "flat") { log.debug(chalk.green("script:lint"), `Using flat ESLint configuration via ${chalk.green(eslintConfig.configFile)}.`); eslintEnvVars.ESLINT_USE_FLAT_CONFIG = "true"; } else { log.debug(chalk.green("script:lint"), `Using legacy ESLint configuration via ${chalk.green(eslintConfig.configFile)}.`); eslintFlags.ext = EXTENSIONS.join(","); } lintInstruction = command("eslint", { args: [lintRoot, eslintFlags], env: eslintEnvVars }); lintFixInstruction = command("eslint", { args: [lintRoot, { ...eslintFlags, fix: true }], env: eslintEnvVars }); } else { log.debug(chalk.green("script:lint"), "Unable to determine ESLint configuration strategy; project may be missing an ESLint configuration file."); } script("lint", lintInstruction, { group: "Lint", description: `Lint the project using ${chalk.white.bold("ESLint")}.`, timing: true }); script("lint.fix", lintFixInstruction, { group: "Lint", description: `Lint the project using ${chalk.white.bold("ESLint")} and automatically fix any fixable errors.`, timing: true }); script("build", [[ command("vite", { args: ["build"], env: eslintEnvVars }), "script:lint" ]], { group: "Build", description: `Build, type-check, and lint the project using ${chalk.white.bold("Vite")}.`, timing: true }); script("build.watch", command("vite", { args: ["build", { watch: true }], env: eslintEnvVars }), { group: "Build", description: `Continuously build and type-check the project using ${chalk.white.bold("Vite")}.` }); script("test", command("vitest", { args: ["run", { passWithNoTests: true }], preserveArgumentCasing: true, env: eslintEnvVars }), { group: "Test", description: `Run unit tests using ${chalk.white.bold("Vitest")}.`, timing: true }); script("test.watch", command("vitest", { args: { passWithNoTests: true }, // This command involves user interaction so we need to use 'inherit'. stdio: "inherit", env: eslintEnvVars, preserveArgumentCasing: true }), { group: "Test", description: `Run unit tests using ${chalk.white.bold("Vitest")} in watch mode.` }); script("test.coverage", command("vitest", { args: ["run", { coverage: true, passWithNoTests: true }], preserveArgumentCasing: true, env: eslintEnvVars }), { group: "Test", description: `Run unit tests using ${chalk.white.bold("Vitest")} and generate a coverage report.`, timing: true }); script("release", command("semantic-release", { args: { extends: "@darkobits/semantic-release-config" } }), { description: `Publish a release from a CI environment using ${chalk.white.bold("semantic-release")}.`, group: "Release", timing: true }); script("release.local", command("semantic-release", { args: { ci: false, extends: "@darkobits/semantic-release-config" } }), { description: `Publish a release locally using ${chalk.white.bold("semantic-release")}.`, group: "Release", timing: true }); const standardVersionCmd = "standard-version"; const createBumpScript = ({ releaseType, args, description }) => { script(releaseType ? `bump.${releaseType}` : "bump", command(standardVersionCmd, { args: { // This will map to @darkobits/conventional-changelog-preset. // See: https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-preset-loader // preset: '@darkobits/preset', releaseCommitMessageFormat: "chore(release): {{currentTag}}\n[skip ci]", ...args } }), { group: "Bump", description: [[ "Generate a change log entry and tagged commit for a", releaseType, `release using ${chalk.white.bold("standard-version")}.` ].filter(Boolean).join(" "), description].filter(Boolean).join(EOL), timing: true }); }; createBumpScript({ description: "The release type will be automatically computed by reviewing commits since the last release." }); createBumpScript({ releaseType: "first", args: { firstRelease: true } }); createBumpScript({ releaseType: "major", args: { releaseAs: "major" } }); createBumpScript({ releaseType: "minor", args: { releaseAs: "minor" } }); createBumpScript({ releaseType: "patch", args: { releaseAs: "patch" } }); createBumpScript({ releaseType: "beta", args: { prerelease: "beta" } }); script("deps.check", command("npm-check-updates", { args: { dep: "prod,peer,dev", format: "group,repo", interactive: true, // Run on the root package and any workspaces. root: true, // Only set this flag to true if package.json declares a "workspaces" // field. Otherwise, npm-check-updates will fail. workspaces: Reflect.has(packageJson, "workspaces") }, stdio: "inherit", // This CLI exits with a non-zero code if the user issues a SIGTERM to quit // interactive mode without performing updates. This instructs Execa to // ignore that. reject: false }), { group: "Dependency Management", description: `Check for newer versions of installed dependencies using ${chalk.white.bold("npm-check-updates")}.` }); script("prepare", isCI ? fn(() => log.info(chalk.green("script:prepare"), chalk.yellow(`CI environment detected. Skipping ${chalk.bold("prepare")} script.`))) : [ // By using strings to reference these scripts, we will always use the most // recent value from the registry, allowing consumers to overwrite them. "script:build", "script:test" ], { group: "Lifecycle", description: "Runs immediately after dependencies are installed to ensure the project builds and tests pass.", timing: !isCI }); script("start", fn(async () => { var _a; const fullOutDir = path.resolve(outDir); const fullSrcDir = path.resolve(root, srcDir); const unscopedName = ((_a = packageJson.name) == null ? void 0 : _a.split("/").pop()) ?? ""; const entrypoint = packageJson.bin ? packageJson.bin[unscopedName] : packageJson.main; const resolvedEntrypointInSrc = entrypoint && path.resolve(entrypoint).replace(fullOutDir, fullSrcDir); if (!resolvedEntrypointInSrc) throw new Error('[script:start] No "bin" (string) or "main" declarations in package.json.'); log.info(chalk.green("script:start"), chalk.gray("Using entrypoint:"), chalk.green(resolvedEntrypointInSrc)); const tsxCommandThunk = command("tsx", { args: ["watch", resolvedEntrypointInSrc, ...forwardArgs], stdio: "inherit" }); return tsxCommandThunk(); }, { name: "tsx" }), { group: "Lifecycle", description: `Execute the project's "main" or "bin" entrypoint in watch mode using ${chalk.bold.white("tsx")}.` }); }; export { defaultPackageScripts }; //# sourceMappingURL=package-scripts.js.map