@darkobits/ts
Version:
Vite-based toolchain for Node projects.
208 lines (207 loc) • 8.02 kB
JavaScript
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