alex-c-line 2.6.2 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/index.cjs +1 -1
- package/dist/index.cjs +109 -72
- package/dist/index.js +66 -29
- package/package.json +11 -10
package/dist/configs/index.cjs
CHANGED
|
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
require("@alextheman/utility");
|
|
25
25
|
let zod = require("zod");
|
|
26
|
-
zod = __toESM(zod);
|
|
26
|
+
zod = __toESM(zod, 1);
|
|
27
27
|
let _alextheman_utility_internal = require("@alextheman/utility/internal");
|
|
28
28
|
//#region src/configs/helpers/preCommit/definePreCommitConfig.ts
|
|
29
29
|
const preCommitStepOptionsSchema = zod.default.strictObject({ arguments: zod.default.array(zod.default.string()).optional() });
|
package/dist/index.cjs
CHANGED
|
@@ -22,17 +22,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
24
|
let commander = require("commander");
|
|
25
|
-
let
|
|
25
|
+
let _alextheman_utility_v6 = require("@alextheman/utility/v6");
|
|
26
26
|
let chalk = require("chalk");
|
|
27
|
-
chalk = __toESM(chalk);
|
|
27
|
+
chalk = __toESM(chalk, 1);
|
|
28
28
|
let boxen = require("boxen");
|
|
29
|
-
boxen = __toESM(boxen);
|
|
29
|
+
boxen = __toESM(boxen, 1);
|
|
30
30
|
let figlet = require("figlet");
|
|
31
|
-
figlet = __toESM(figlet);
|
|
31
|
+
figlet = __toESM(figlet, 1);
|
|
32
32
|
let env_paths = require("env-paths");
|
|
33
|
-
env_paths = __toESM(env_paths);
|
|
33
|
+
env_paths = __toESM(env_paths, 1);
|
|
34
34
|
let node_path = require("node:path");
|
|
35
|
-
node_path = __toESM(node_path);
|
|
35
|
+
node_path = __toESM(node_path, 1);
|
|
36
|
+
let _alextheman_utility = require("@alextheman/utility");
|
|
36
37
|
let _inquirer_prompts = require("@inquirer/prompts");
|
|
37
38
|
let node_fs_promises = require("node:fs/promises");
|
|
38
39
|
let dotenv = require("dotenv");
|
|
@@ -40,15 +41,16 @@ let execa = require("execa");
|
|
|
40
41
|
let node_url = require("node:url");
|
|
41
42
|
let _alextheman_utility_internal = require("@alextheman/utility/internal");
|
|
42
43
|
let zod = require("zod");
|
|
43
|
-
zod = __toESM(zod);
|
|
44
|
+
zod = __toESM(zod, 1);
|
|
44
45
|
let node_module = require("node:module");
|
|
46
|
+
let toml = require("toml");
|
|
45
47
|
let gray_matter = require("gray-matter");
|
|
46
|
-
gray_matter = __toESM(gray_matter);
|
|
48
|
+
gray_matter = __toESM(gray_matter, 1);
|
|
47
49
|
let _alextheman_utility_node = require("@alextheman/utility/node");
|
|
48
50
|
let axios = require("axios");
|
|
49
|
-
axios = __toESM(axios);
|
|
51
|
+
axios = __toESM(axios, 1);
|
|
50
52
|
let supports_color = require("supports-color");
|
|
51
|
-
supports_color = __toESM(supports_color);
|
|
53
|
+
supports_color = __toESM(supports_color, 1);
|
|
52
54
|
let node_crypto = require("node:crypto");
|
|
53
55
|
let semver = require("semver");
|
|
54
56
|
//#region src/utility/miscellaneous/centerLine.ts
|
|
@@ -82,7 +84,7 @@ async function createAlexCLineArtwork(options) {
|
|
|
82
84
|
//#region src/cli/commands/artwork/log.ts
|
|
83
85
|
function artworkLog(program) {
|
|
84
86
|
program.command("log").description("Log the alex-c-line artwork to the console.").option("--subtitle-text <subtitleText>", "Customise the subtitle text.").option("--subtitle-color <subtitleColor>", "Customise the subtitle color.").action(async ({ subtitleText, subtitleColor = "green" }) => {
|
|
85
|
-
if (subtitleColor !== "green" && subtitleColor !== "white") throw new
|
|
87
|
+
if (subtitleColor !== "green" && subtitleColor !== "white") throw new _alextheman_utility_v6.DataError({ subtitleColor }, "INVALID_SUBTITLE_COLOR", "Subtitle color must either be green or white.");
|
|
86
88
|
const chalkColour = {
|
|
87
89
|
green: chalk.default.green,
|
|
88
90
|
white: chalk.default.white
|
|
@@ -120,8 +122,8 @@ function cache(program) {
|
|
|
120
122
|
loadCommands(program.command("cache").description("Commands related to the alex-c-line cache"), { cachePath });
|
|
121
123
|
}
|
|
122
124
|
//#endregion
|
|
123
|
-
//#region src/utility/constants/
|
|
124
|
-
const
|
|
125
|
+
//#region src/utility/constants/ERROR_PREFIX.ts
|
|
126
|
+
const ERROR_PREFIX = "❌ ERROR:";
|
|
125
127
|
//#endregion
|
|
126
128
|
//#region src/utility/envFile/upsertDotenvFile.ts
|
|
127
129
|
async function upsertDotenvFile(contents, envFilePath) {
|
|
@@ -132,14 +134,14 @@ async function upsertDotenvFile(contents, envFilePath) {
|
|
|
132
134
|
async function addVariable(program, envFileContents, file) {
|
|
133
135
|
const newVariableName = await (0, _inquirer_prompts.input)({ message: "Please enter the name of the environment variable you would like to add." });
|
|
134
136
|
if (newVariableName in envFileContents) program.error(`
|
|
135
|
-
${
|
|
137
|
+
${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
|
|
136
138
|
Variable name already exists. If you wish to edit this variable, please select it from the initial menu instead.
|
|
137
139
|
`, {
|
|
138
140
|
exitCode: 2,
|
|
139
141
|
code: "DUPLICATE_ENVIRONMENT_VARIABLE_NAME"
|
|
140
142
|
});
|
|
141
143
|
if (/[ \t\r\n]/.test(newVariableName)) program.error(_alextheman_utility.normaliseIndents`
|
|
142
|
-
${
|
|
144
|
+
${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
|
|
143
145
|
Environment variables are not allowed to have whitespace.
|
|
144
146
|
`, {
|
|
145
147
|
exitCode: 2,
|
|
@@ -274,7 +276,7 @@ function checkLockfileVersionDiscrepancy(program) {
|
|
|
274
276
|
const { version: packageVersion } = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.resolve(process.cwd(), "package.json"), "utf-8"));
|
|
275
277
|
const { version: packageLockVersion } = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.resolve(process.cwd(), "package-lock.json"), "utf-8"));
|
|
276
278
|
if (packageVersion !== packageLockVersion) {
|
|
277
|
-
console.error(`${
|
|
279
|
+
console.error(`${ERROR_PREFIX} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
|
|
278
280
|
process.exitCode = 1;
|
|
279
281
|
return;
|
|
280
282
|
}
|
|
@@ -287,7 +289,7 @@ function gitPostMergeCleanup(program) {
|
|
|
287
289
|
program.command("git-post-merge-cleanup").alias("git-cleanup").description("Run after merging into a given branch to quickly clean up").argument("[branch]", "The branch you want to merge into", "main").option("--rebase", "Enable if your repository mainly rebases into main", true).action(async (branch, { rebase }) => {
|
|
288
290
|
console.info(`Running git-post-merge-cleanup in ${rebase ? "rebase" : "merge"} mode...`);
|
|
289
291
|
const { stdout: currentBranch } = await execa.execa`git branch --show-current`;
|
|
290
|
-
if (currentBranch === branch) program.error(`${
|
|
292
|
+
if (currentBranch === branch) program.error(`${ERROR_PREFIX} Cannot run cleanup on ${branch} branch!`, {
|
|
291
293
|
exitCode: 1,
|
|
292
294
|
code: "INVALID_BRANCH"
|
|
293
295
|
});
|
|
@@ -303,7 +305,7 @@ function gitPostMergeCleanup(program) {
|
|
|
303
305
|
const { stdout: changes } = await execa.execa`git diff ${branch}..${currentBranch}`;
|
|
304
306
|
if (changes) {
|
|
305
307
|
await execa.execa`git checkout ${currentBranch}`;
|
|
306
|
-
program.error(`${
|
|
308
|
+
program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
|
|
307
309
|
exitCode: 1,
|
|
308
310
|
code: "CHANGES_NOT_MERGED"
|
|
309
311
|
});
|
|
@@ -313,7 +315,7 @@ function gitPostMergeCleanup(program) {
|
|
|
313
315
|
const { stdout: branchDeletedMessage, exitCode } = await (0, execa.execa)({ reject: false })`git branch --delete ${currentBranch}`;
|
|
314
316
|
if (exitCode !== 0) {
|
|
315
317
|
await execa.execa`git checkout ${currentBranch}`;
|
|
316
|
-
program.error(`${
|
|
318
|
+
program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
|
|
317
319
|
exitCode: 1,
|
|
318
320
|
code: "CHANGES_NOT_MERGED"
|
|
319
321
|
});
|
|
@@ -323,8 +325,8 @@ function gitPostMergeCleanup(program) {
|
|
|
323
325
|
});
|
|
324
326
|
}
|
|
325
327
|
//#endregion
|
|
326
|
-
//#region src/utility/constants/
|
|
327
|
-
const
|
|
328
|
+
//#region src/utility/constants/WARNING_PREFIX.ts
|
|
329
|
+
const WARNING_PREFIX = "WARNING:";
|
|
328
330
|
//#endregion
|
|
329
331
|
//#region src/utility/fileSystem/readdirSafe.ts
|
|
330
332
|
async function readdirSafe(path) {
|
|
@@ -361,7 +363,7 @@ function internalMediaGenerate(program) {
|
|
|
361
363
|
if (resolution) return await runManimCommand`manim -qh -r ${resolution} ${file}`;
|
|
362
364
|
return await runManimCommand`manim -qh ${file}`;
|
|
363
365
|
} catch (error) {
|
|
364
|
-
if (error instanceof execa.ExecaError) program.error(`${
|
|
366
|
+
if (error instanceof execa.ExecaError) program.error(`${ERROR_PREFIX} An error has occurred with Manim while rendering ${relativePath}.`, {
|
|
365
367
|
exitCode: error.exitCode ?? 1,
|
|
366
368
|
code: "MANIM_ERROR"
|
|
367
369
|
});
|
|
@@ -380,7 +382,7 @@ function internalMediaGenerate(program) {
|
|
|
380
382
|
const statResult = await (0, node_fs_promises.stat)(target);
|
|
381
383
|
if (statResult.isFile()) await renderFile(target);
|
|
382
384
|
else if (statResult.isDirectory()) await readDirectory(target);
|
|
383
|
-
else console.warn(`${
|
|
385
|
+
else console.warn(`${WARNING_PREFIX} Not a file or directory.`);
|
|
384
386
|
});
|
|
385
387
|
}
|
|
386
388
|
//#endregion
|
|
@@ -401,10 +403,10 @@ async function findPackageRoot(startDirectory, packageName) {
|
|
|
401
403
|
if (parent === directory) break;
|
|
402
404
|
directory = parent;
|
|
403
405
|
}
|
|
404
|
-
throw new
|
|
406
|
+
throw new _alextheman_utility_v6.DataError({ packageName }, "PACKAGE_ROOT_NOT_FOUND", `Could not find package root for ${packageName}`);
|
|
405
407
|
}
|
|
406
408
|
//#endregion
|
|
407
|
-
//#region src/utility/constants/
|
|
409
|
+
//#region src/utility/constants/ALEX_C_LINE_PACKAGE_ROOT.ts
|
|
408
410
|
const __filename$3 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
|
|
409
411
|
const ALEX_C_LINE_PACKAGE_ROOT = findPackageRoot(node_path.default.dirname(__filename$3), "alex-c-line");
|
|
410
412
|
//#endregion
|
|
@@ -583,16 +585,16 @@ async function findTgzFile(packagePath, packageManager) {
|
|
|
583
585
|
const tgzFiles = (await (0, node_fs_promises.readdir)(packagePath)).filter((fileName) => {
|
|
584
586
|
return fileName.endsWith(".tgz");
|
|
585
587
|
});
|
|
586
|
-
if (tgzFiles.length === 0) throw new
|
|
588
|
+
if (tgzFiles.length === 0) throw new _alextheman_utility_v6.DataError({ tgzFiles }, "TGZ_FILE_NOT_FOUND", "Could not find any .tgz files");
|
|
587
589
|
const expectedTgzFileName = await (0, _alextheman_utility_internal.getExpectedTgzName)(packagePath, packageManager);
|
|
588
590
|
const amountOfMatchingFiles = tgzFiles.filter((fileName) => {
|
|
589
591
|
return fileName === expectedTgzFileName;
|
|
590
592
|
}).length;
|
|
591
|
-
if (amountOfMatchingFiles === 0) throw new
|
|
593
|
+
if (amountOfMatchingFiles === 0) throw new _alextheman_utility_v6.DataError({
|
|
592
594
|
expectedTgzFileName,
|
|
593
595
|
amountOfMatchingFiles
|
|
594
596
|
}, "EXPECTED_FILE_NOT_FOUND", "Could not find a .tgz file with the expected file name.");
|
|
595
|
-
if (amountOfMatchingFiles > 1) throw new
|
|
597
|
+
if (amountOfMatchingFiles > 1) throw new _alextheman_utility_v6.DataError({
|
|
596
598
|
expectedTgzFileName,
|
|
597
599
|
amountOfMatchingFiles
|
|
598
600
|
}, "AMBIGUOUS_RESOLUTION", "There are too many .tgz files with the expected file name.");
|
|
@@ -618,27 +620,27 @@ function localPackageUse(program) {
|
|
|
618
620
|
PrivateConfigFileName.ES_MODULES_JAVASCRIPT,
|
|
619
621
|
PrivateConfigFileName.STANDARD_JAVASCRIPT
|
|
620
622
|
]);
|
|
621
|
-
if (!configPath) throw new
|
|
623
|
+
if (!configPath) throw new _alextheman_utility_v6.DataError({ configPath }, "ALEX_C_LINE_PRIVATE_CONFIG_NOT_FOUND", "Could not find the path to the alex-c-line private config file (should be `.alex-c-line.private.config.js`). Does it exist?");
|
|
622
624
|
const { localPackage: { enableCache, localPackages } } = await loadAlexCLinePrivateConfig(configPath);
|
|
623
625
|
const localPackage = localPackages[packageName];
|
|
624
|
-
if (!localPackage) throw new
|
|
626
|
+
if (!localPackage) throw new _alextheman_utility_v6.DataError({
|
|
625
627
|
packageName,
|
|
626
628
|
configPath
|
|
627
629
|
}, "PACKAGE_NOT_FOUND", `Could not find ${packageName} in your private config.`);
|
|
628
630
|
const { packageManager, prepareScript = "build", dependencyGroup = "dependencies", keepOldTarballs } = localPackage;
|
|
629
631
|
const packageInfo = await (0, _alextheman_utility_internal.getPackageJsonContents)(process.cwd());
|
|
630
|
-
if (packageInfo === null) throw new
|
|
632
|
+
if (packageInfo === null) throw new _alextheman_utility_v6.DataError({ currentDirectory: process.cwd() }, "MISSING_CURRENT_REPOSITORY_PACKAGE_JSON", "Could not find package.json in the current location");
|
|
631
633
|
const dependencies = (0, _alextheman_utility_internal.getDependenciesFromGroup)(packageInfo, dependencyGroup);
|
|
632
|
-
if (!(packageName in dependencies) && packageName !== "alex-c-line") throw new
|
|
634
|
+
if (!(packageName in dependencies) && packageName !== "alex-c-line") throw new _alextheman_utility_v6.DataError({
|
|
633
635
|
packageName,
|
|
634
636
|
dependencyGroup,
|
|
635
637
|
packagePath: process.cwd()
|
|
636
638
|
}, "PACKAGE_NOT_FOUND", `Could not find ${packageName} in the ${dependencyGroup} of your package.json.`);
|
|
637
639
|
const localPackagePath = node_path.default.resolve(process.cwd(), localPackage.path);
|
|
638
640
|
const localPackageInfo = await (0, _alextheman_utility_internal.getPackageJsonContents)(localPackagePath);
|
|
639
|
-
if (localPackageInfo === null) throw new
|
|
641
|
+
if (localPackageInfo === null) throw new _alextheman_utility_v6.DataError({ localPackagePath }, "MISSING_PACKAGE_REPOSITORY_PACKAGE_JSON", "Could not find package.json in the package repository.");
|
|
640
642
|
const localPackageRepositoryName = (0, _alextheman_utility.parseZodSchema)(zod.default.string(), localPackageInfo.name);
|
|
641
|
-
if (localPackageRepositoryName !== packageName) throw new
|
|
643
|
+
if (localPackageRepositoryName !== packageName) throw new _alextheman_utility_v6.DataError({
|
|
642
644
|
providedPackageName: packageName,
|
|
643
645
|
localPackagePath,
|
|
644
646
|
localPackageRepositoryName
|
|
@@ -655,7 +657,7 @@ function localPackageUse(program) {
|
|
|
655
657
|
stdio: "inherit",
|
|
656
658
|
reject: false
|
|
657
659
|
});
|
|
658
|
-
if (exitCode !== 0) program.error(`${
|
|
660
|
+
if (exitCode !== 0) program.error(`${ERROR_PREFIX} An error occurred during the local \`alex-c-line\` run.`, {
|
|
659
661
|
exitCode,
|
|
660
662
|
code: "LOCAL_ALEX_C_LINE_ERROR"
|
|
661
663
|
});
|
|
@@ -718,6 +720,43 @@ function localPackage(program) {
|
|
|
718
720
|
loadCommands(program.command("local-package").description("Manage the use of local packages in your JavaScript project."), { localPackageUse });
|
|
719
721
|
}
|
|
720
722
|
//#endregion
|
|
723
|
+
//#region src/utility/constants/SUCCESS_PREFIX.ts
|
|
724
|
+
const SUCCESS_PREFIX = chalk.default.green("✓");
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region src/cli/commands/pyproject/check/preferExactDependencyVersions.ts
|
|
727
|
+
const pyprojectSchema = zod.default.object({
|
|
728
|
+
project: zod.default.object({ dependencies: zod.default.array(zod.default.string()).optional() }),
|
|
729
|
+
"dependency-groups": zod.default.object({ dev: zod.default.array(zod.default.string()).optional() })
|
|
730
|
+
}).partial();
|
|
731
|
+
async function preferExactDependencyVersions(program) {
|
|
732
|
+
const data = (0, _alextheman_utility.parseZodSchema)(pyprojectSchema, (0, toml.parse)(await (0, node_fs_promises.readFile)("pyproject.toml", "utf-8")));
|
|
733
|
+
const sections = [data.project?.dependencies ?? [], data["dependency-groups"]?.dev ?? []];
|
|
734
|
+
const violations = [];
|
|
735
|
+
for (const dependencies of sections) for (const dependency of dependencies) if (!dependency.includes("==")) violations.push(dependency);
|
|
736
|
+
if (violations.length !== 0) program.error(`${ERROR_PREFIX} Non-exact dependencies found:\n\n${violations.join("\n")}`, {
|
|
737
|
+
code: "NON_EXACT_DEPENDENCIES_FOUND",
|
|
738
|
+
exitCode: 2
|
|
739
|
+
});
|
|
740
|
+
console.info(`${SUCCESS_PREFIX} All dependencies are exactly pinned`);
|
|
741
|
+
}
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/cli/commands/pyproject/check/index.ts
|
|
744
|
+
const RuleName$1 = { PREFER_EXACT_DEPENDENCY_VERSIONS: "prefer-exact-dependency-versions" };
|
|
745
|
+
function pyprojectCheck(program) {
|
|
746
|
+
program.command("check").description("Run checks on your pyproject.toml file").option("--rules <rules>", "The name of the rule to check", (rawRules) => {
|
|
747
|
+
const rawRuleNamesArray = rawRules.split(",");
|
|
748
|
+
return (0, _alextheman_utility.parseZodSchema)(zod.default.array(zod.default.enum(RuleName$1)), rawRuleNamesArray);
|
|
749
|
+
}).action(async ({ rules }) => {
|
|
750
|
+
if (rules?.includes("prefer-exact-dependency-versions")) await preferExactDependencyVersions(program);
|
|
751
|
+
console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
//#endregion
|
|
755
|
+
//#region src/cli/commands/pyproject/index.ts
|
|
756
|
+
function pyproject(program) {
|
|
757
|
+
loadCommands(program.command("pyproject").description("Manage the pyproject.toml file."), { pyprojectCheck });
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
721
760
|
//#region src/cli/commands/root/pre-commit/createStepRunner.ts
|
|
722
761
|
const runCommandAndLogToConsole = (0, execa.execa)({
|
|
723
762
|
stdio: "inherit",
|
|
@@ -755,7 +794,7 @@ function createStepRunner(program) {
|
|
|
755
794
|
//#endregion
|
|
756
795
|
//#region src/cli/commands/root/pre-commit/getCommandArguments.ts
|
|
757
796
|
function getCommandArguments(program, script, scripts, args) {
|
|
758
|
-
if (!(script in (scripts ?? {}))) program.error(`${
|
|
797
|
+
if (!(script in (scripts ?? {}))) program.error(`${ERROR_PREFIX} Could not find script \`${script}\` in package.json.`, {
|
|
759
798
|
exitCode: 1,
|
|
760
799
|
code: "SCRIPT_NOT_FOUND"
|
|
761
800
|
});
|
|
@@ -779,16 +818,16 @@ async function loadAlexCLineConfig(filePath) {
|
|
|
779
818
|
function preCommit(program) {
|
|
780
819
|
program.command("pre-commit").description("Run the pre-commit scripts specified in the alex-c-line config (v2 experiment).").option("--allow-no-staged-changes", "Run even if nothing is staged").option("--no-update-index").option("--update-index", "Update the git index after the run").action(async (options) => {
|
|
781
820
|
const configPath = await findAlexCLineConfig(process.cwd());
|
|
782
|
-
if (!configPath) throw new
|
|
821
|
+
if (!configPath) throw new _alextheman_utility_v6.DataError({ configPath }, "ALEX_C_LINE_CONFIG_NOT_FOUND", "Could not find the path to the alex-c-line config file. Does it exist?");
|
|
783
822
|
const { preCommit: preCommitConfig } = await loadAlexCLineConfig(configPath);
|
|
784
|
-
if (!preCommitConfig) throw new
|
|
823
|
+
if (!preCommitConfig) throw new _alextheman_utility_v6.DataError({
|
|
785
824
|
configPath,
|
|
786
825
|
preCommitConfig
|
|
787
826
|
}, "PRE_COMMIT_CONFIG_NOT_FOUND", "Could not find the pre-commit config in alex-c-line config.");
|
|
788
827
|
const { allowNoStagedChanges = options?.allowNoStagedChanges, updateIndex = options?.updateIndex } = preCommitConfig;
|
|
789
828
|
const { exitCode: diffExitCode } = await (0, execa.execa)({ reject: false })`git diff --cached --quiet`;
|
|
790
829
|
switch (diffExitCode) {
|
|
791
|
-
case 128: program.error(`${
|
|
830
|
+
case 128: program.error(`${ERROR_PREFIX} Not currently in a Git repository`, {
|
|
792
831
|
exitCode: 1,
|
|
793
832
|
code: "GIT_DIFF_FAILED"
|
|
794
833
|
});
|
|
@@ -799,7 +838,7 @@ function preCommit(program) {
|
|
|
799
838
|
}
|
|
800
839
|
const { packageManager: packagePackageManager, scripts } = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.join(process.cwd(), "package.json"), "utf8"));
|
|
801
840
|
const rawPackageManager = preCommitConfig.packageManager ?? (typeof packagePackageManager === "string" ? packagePackageManager.split("@")[0] : void 0);
|
|
802
|
-
const packageManager = (0, _alextheman_utility.parseZodSchema)(zod.default.enum(_alextheman_utility_internal.PackageManager), rawPackageManager, new
|
|
841
|
+
const packageManager = (0, _alextheman_utility.parseZodSchema)(zod.default.enum(_alextheman_utility_internal.PackageManager), rawPackageManager, new _alextheman_utility_v6.DataError({ packageManager: rawPackageManager }, "UNSUPPORTED_PACKAGE_MANAGER", `This package manager is not currently supported. Only the following are supported: ${Object.values(_alextheman_utility_internal.PackageManager).join(", ")}`));
|
|
803
842
|
const stepRunner = createStepRunner(program);
|
|
804
843
|
for (const step of preCommitConfig.steps) if (typeof step === "function") await step(stepRunner);
|
|
805
844
|
else if (typeof step === "string") await stepRunner(packageManager, getCommandArguments(program, step, scripts));
|
|
@@ -843,7 +882,7 @@ async function createPullRequestTemplatesFromTemplates(config) {
|
|
|
843
882
|
const templateVariables = getTemplateVariables$1(config);
|
|
844
883
|
const { category } = config;
|
|
845
884
|
const templatesPath = node_path.default.join(await findPackageRoot(node_path.default.dirname(__filename$2), "alex-c-line"), "templates", "pullRequest");
|
|
846
|
-
if (!(await (0, node_fs_promises.readdir)(templatesPath)).includes(category)) throw new
|
|
885
|
+
if (!(await (0, node_fs_promises.readdir)(templatesPath)).includes(category)) throw new _alextheman_utility_v6.DataError({ category }, "CATEGORY_NOT_FOUND", "Category folder not found in the templates folder.");
|
|
847
886
|
const categoryPath = node_path.default.join(templatesPath, category);
|
|
848
887
|
const allCategoryTemplateNames = (await (0, node_fs_promises.readdir)(categoryPath)).filter((name) => {
|
|
849
888
|
return name.endsWith(".md");
|
|
@@ -855,7 +894,7 @@ async function createPullRequestTemplatesFromTemplates(config) {
|
|
|
855
894
|
const placeholders = (0, _alextheman_utility.parseZodSchema)(zod.default.array(zod.default.string()).default([]), data.placeholders);
|
|
856
895
|
let finalContent = content;
|
|
857
896
|
for (const placeholder of placeholders) {
|
|
858
|
-
if (!(placeholder in templateVariables)) throw new
|
|
897
|
+
if (!(placeholder in templateVariables)) throw new _alextheman_utility_v6.DataError({ placeholder }, "INVALID_PLACEHOLDER", "The placeholder found in frontmatter can not be found in the metadata.");
|
|
859
898
|
finalContent = finalContent.replaceAll(`{{${placeholder}}}`, templateVariables[placeholder]);
|
|
860
899
|
}
|
|
861
900
|
allTemplates[templateName] = finalContent;
|
|
@@ -874,7 +913,7 @@ function templatePullRequestCreate(program) {
|
|
|
874
913
|
const { template: { pullRequest: config } = {} } = configPath ? await loadAlexCLineConfig(configPath) : {};
|
|
875
914
|
const packageInfo = await (0, _alextheman_utility_internal.getPackageJsonContents)(process.cwd());
|
|
876
915
|
const { name: projectName } = commandLineOptions.projectName || config?.projectName ? { name: commandLineOptions.projectName ?? config?.projectName } : (0, _alextheman_utility.parseZodSchema)(zod.default.object({ name: zod.default.string() }), packageInfo);
|
|
877
|
-
if (!projectName) throw new
|
|
916
|
+
if (!projectName) throw new _alextheman_utility_v6.DataError({ projectName }, "PROJECT_NAME_NOT_FOUND", "Could not resolve project name.");
|
|
878
917
|
const parsedOptions = parseTemplatePullRequestConfig((0, _alextheman_utility.removeUndefinedFromObject)({
|
|
879
918
|
category: commandLineOptions.category ?? config?.category ?? "general",
|
|
880
919
|
projectType: commandLineOptions.projectType ?? (config?.category === "general" ? config?.projectType : void 0),
|
|
@@ -900,12 +939,9 @@ function templatePullRequest(program) {
|
|
|
900
939
|
loadCommands(program.command("pull-request").description("Manage the pull request templates."), { templatePullRequestCreate });
|
|
901
940
|
}
|
|
902
941
|
//#endregion
|
|
903
|
-
//#region src/utility/constants/successPrefix.ts
|
|
904
|
-
const successPrefix = chalk.default.green("✓");
|
|
905
|
-
//#endregion
|
|
906
942
|
//#region src/utility/errors/convertDataErrorToProgramError.ts
|
|
907
943
|
function convertDataErrorToProgramError(dataError, program, options) {
|
|
908
|
-
program.error(`${
|
|
944
|
+
program.error(`${ERROR_PREFIX} ${dataError.message}`, {
|
|
909
945
|
exitCode: options?.exitCode ?? 1,
|
|
910
946
|
code: dataError.code
|
|
911
947
|
});
|
|
@@ -920,7 +956,7 @@ const ReleaseStatus = {
|
|
|
920
956
|
//#region src/utility/markdownTemplates/releaseNote/parseReleaseStatus.ts
|
|
921
957
|
function parseReleaseStatus(data) {
|
|
922
958
|
const normalisedStringifiedData = (typeof data === "string" ? data : String(data)).toUpperCase().replaceAll(" ", "_");
|
|
923
|
-
if (!Object.keys(ReleaseStatus).includes(normalisedStringifiedData)) new
|
|
959
|
+
if (!Object.keys(ReleaseStatus).includes(normalisedStringifiedData)) new _alextheman_utility_v6.DataError({ data }, "INVALID_RELEASE_STATUS", "Invalid release status. The release status must be one of \"In progress\" or \"Released\"");
|
|
924
960
|
return ReleaseStatus[normalisedStringifiedData];
|
|
925
961
|
}
|
|
926
962
|
//#endregion
|
|
@@ -953,7 +989,7 @@ function normaliseMarkdown(markdownString) {
|
|
|
953
989
|
//#region src/utility/markdownTemplates/releaseNote/getReleaseStatus.ts
|
|
954
990
|
function getReleaseStatus(content) {
|
|
955
991
|
const releaseStatus = getMarkdownBlock(content, ...getMarkdownCommentPair("alex-c-line-release-status"));
|
|
956
|
-
if (releaseStatus === null) throw new
|
|
992
|
+
if (releaseStatus === null) throw new _alextheman_utility_v6.DataError({ releaseStatus }, "RELEASE_STATUS_NOT_FOUND", "Could not find release status in document.");
|
|
957
993
|
return (0, _alextheman_utility.parseZodSchema)(zod.default.enum(ReleaseStatus), normaliseMarkdown(releaseStatus.split(":")[1]));
|
|
958
994
|
}
|
|
959
995
|
//#endregion
|
|
@@ -962,14 +998,14 @@ const __filename$1 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__
|
|
|
962
998
|
async function validateReleaseDocument(projectName, version, content, allowedReleaseStatus = ["In progress", "Released"]) {
|
|
963
999
|
if (!normaliseMarkdown(content).startsWith(normaliseMarkdown(_alextheman_utility.normaliseIndents`
|
|
964
1000
|
# ${version} (${(0, _alextheman_utility.kebabToCamel)(version.type, { startWithUpper: true })} Release)
|
|
965
|
-
`))) throw new
|
|
1001
|
+
`))) throw new _alextheman_utility_v6.DataError({ heading: content.split("\n").slice(0, 3).join("\n") }, "INVALID_HEADING", _alextheman_utility.normaliseIndents`
|
|
966
1002
|
Expected heading to be:
|
|
967
1003
|
|
|
968
1004
|
# ${version} (${(0, _alextheman_utility.kebabToCamel)(version.type, { startWithUpper: true })} Release)
|
|
969
1005
|
`);
|
|
970
1006
|
const releaseStatus = getReleaseStatus(content);
|
|
971
1007
|
const allowedReleaseStatuses = (0, _alextheman_utility.removeDuplicates)(Array.isArray(allowedReleaseStatus) ? allowedReleaseStatus : [allowedReleaseStatus]);
|
|
972
|
-
if (!allowedReleaseStatuses.includes(releaseStatus)) throw new
|
|
1008
|
+
if (!allowedReleaseStatuses.includes(releaseStatus)) throw new _alextheman_utility_v6.DataError({ releaseStatus }, "INVALID_RELEASE_STATUS", _alextheman_utility.normaliseIndents`
|
|
973
1009
|
Invalid release status.
|
|
974
1010
|
Current: "${releaseStatus}"
|
|
975
1011
|
Expected: ${allowedReleaseStatuses.length === 1 ? `"${allowedReleaseStatus}"` : `one of: ${allowedReleaseStatuses.map((status) => {
|
|
@@ -979,19 +1015,19 @@ async function validateReleaseDocument(projectName, version, content, allowedRel
|
|
|
979
1015
|
const summary = getMarkdownBlock(content, ...releaseSummaryHeaders);
|
|
980
1016
|
const templateContent = await (0, node_fs_promises.readFile)(node_path.default.join(await findPackageRoot(node_path.default.dirname(__filename$1), "alex-c-line"), "templates", "releases", `${version.type}.md`), "utf-8");
|
|
981
1017
|
const templateSummary = getMarkdownBlock(templateContent, ...releaseSummaryHeaders)?.replaceAll(`{{projectName}}`, projectName);
|
|
982
|
-
if (!templateSummary) throw new
|
|
983
|
-
if (!summary) throw new
|
|
1018
|
+
if (!templateSummary) throw new _alextheman_utility_v6.DataError({ templateContent }, "SUMMARY_NOT_FOUND", "Expected to find a release summary but it was not found.");
|
|
1019
|
+
if (!summary) throw new _alextheman_utility_v6.DataError({ content }, "SUMMARY_NOT_FOUND", _alextheman_utility.normaliseIndents`
|
|
984
1020
|
Expected to find a release summary but it was not found. Expected release summary to be:
|
|
985
1021
|
|
|
986
1022
|
${templateSummary}
|
|
987
1023
|
`);
|
|
988
|
-
if (normaliseMarkdown(summary) !== normaliseMarkdown(templateSummary)) throw new
|
|
1024
|
+
if (normaliseMarkdown(summary) !== normaliseMarkdown(templateSummary)) throw new _alextheman_utility_v6.DataError({ summary }, "INVALID_SUMMARY", _alextheman_utility.normaliseIndents`
|
|
989
1025
|
Summary does not match what was expected. Expected release summary to be:
|
|
990
1026
|
|
|
991
1027
|
${templateSummary}
|
|
992
1028
|
`);
|
|
993
|
-
if (!content.includes("## Description of Changes")) throw new
|
|
994
|
-
if (version.type === "major" && !content.includes("## Migration Notes")) throw new
|
|
1029
|
+
if (!content.includes("## Description of Changes")) throw new _alextheman_utility_v6.DataError({ content }, "DESCRIPTION_NOT_FOUND", "Expected to find a description of changes but it was not found.");
|
|
1030
|
+
if (version.type === "major" && !content.includes("## Migration Notes")) throw new _alextheman_utility_v6.DataError({ content }, "MIGRATION_NOTES_NOT_FOUND", "Major version notes must have migration notes as major versions are expected to be breaking changes that require users to migrate and refactor their code.");
|
|
995
1031
|
}
|
|
996
1032
|
//#endregion
|
|
997
1033
|
//#region src/cli/commands/template/releaseNote/check.ts
|
|
@@ -1006,9 +1042,9 @@ function templateReleaseNoteCheck(program) {
|
|
|
1006
1042
|
}).join("."));
|
|
1007
1043
|
try {
|
|
1008
1044
|
await validateReleaseDocument(name, documentVersion, fileContents, expectedReleaseStatus);
|
|
1009
|
-
console.info(`${
|
|
1045
|
+
console.info(`${SUCCESS_PREFIX} Release document is valid!`);
|
|
1010
1046
|
} catch (error) {
|
|
1011
|
-
if (
|
|
1047
|
+
if (_alextheman_utility_v6.DataError.check(error)) convertDataErrorToProgramError(error, program, { exitCode: 2 });
|
|
1012
1048
|
else throw error;
|
|
1013
1049
|
}
|
|
1014
1050
|
});
|
|
@@ -1031,7 +1067,7 @@ function replaceMarkdownPlaceholders(rawContent, templateVariables) {
|
|
|
1031
1067
|
const placeholders = (0, _alextheman_utility.parseZodSchema)(zod.default.array(zod.default.string()).default([]), data.placeholders);
|
|
1032
1068
|
let finalContent = content;
|
|
1033
1069
|
for (const placeholder of placeholders) {
|
|
1034
|
-
if (!(placeholder in templateVariables)) throw new
|
|
1070
|
+
if (!(placeholder in templateVariables)) throw new _alextheman_utility_v6.DataError({ placeholder }, "INVALID_PLACEHOLDER", "The placeholder found in frontmatter can not be found in the metadata.");
|
|
1035
1071
|
finalContent = finalContent.replaceAll(`{{${placeholder}}}`, templateVariables[placeholder]);
|
|
1036
1072
|
}
|
|
1037
1073
|
return finalContent;
|
|
@@ -1082,7 +1118,7 @@ function templateReleaseNoteCreate(program) {
|
|
|
1082
1118
|
try {
|
|
1083
1119
|
return new _alextheman_utility.VersionNumber(rawValue);
|
|
1084
1120
|
} catch (error) {
|
|
1085
|
-
if (
|
|
1121
|
+
if (_alextheman_utility_v6.DataError.check(error) && error.code === "INVALID_VERSION") return (0, _alextheman_utility.parseVersionType)(rawValue);
|
|
1086
1122
|
throw error;
|
|
1087
1123
|
}
|
|
1088
1124
|
}).description("Create release notes based on the current version in package.json.").action(async (target) => {
|
|
@@ -1098,7 +1134,7 @@ function templateReleaseNoteCreate(program) {
|
|
|
1098
1134
|
await (0, node_fs_promises.mkdir)(node_path.default.dirname(releaseNotePath), { recursive: true });
|
|
1099
1135
|
await (0, node_fs_promises.writeFile)(releaseNotePath, releaseNoteTemplate, { flag: "wx" });
|
|
1100
1136
|
} catch (error) {
|
|
1101
|
-
if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${
|
|
1137
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${ERROR_PREFIX} Release notes already exist.`, {
|
|
1102
1138
|
exitCode: 1,
|
|
1103
1139
|
code: "RELEASE_NOTE_EXISTS"
|
|
1104
1140
|
});
|
|
@@ -1126,8 +1162,8 @@ function templateReleaseNotePath(program) {
|
|
|
1126
1162
|
function templateReleaseNoteSetStatus(program) {
|
|
1127
1163
|
program.command("set-status").description("Change the release status on a given release document initially generated from the `create-release-note` command.").argument("<documentPath>", "The path to the document").argument("[status]", "The status to set the document to", parseReleaseStatus, ReleaseStatus.RELEASED).action(async (documentPath, status) => {
|
|
1128
1164
|
const packageInfo = await (0, _alextheman_utility_internal.getPackageJsonContents)(process.cwd());
|
|
1129
|
-
const { name } = (0, _alextheman_utility.parseZodSchema)(zod.default.object({ name: zod.default.string() }), packageInfo, new
|
|
1130
|
-
if (!documentPath.endsWith("md")) throw new
|
|
1165
|
+
const { name } = (0, _alextheman_utility.parseZodSchema)(zod.default.object({ name: zod.default.string() }), packageInfo, new _alextheman_utility_v6.DataError({ name: packageInfo?.name }, "INVALID_PACKAGE_JSON", "Invalid package.json - expected package.json to contain a `name` property."));
|
|
1166
|
+
if (!documentPath.endsWith("md")) throw new _alextheman_utility_v6.DataError({ documentPath }, "INVALID_FILE_PATH", "Invalid file path. Path must lead to a .md file.");
|
|
1131
1167
|
const versionNumber = new _alextheman_utility.VersionNumber(node_path.default.basename(documentPath).split(".").filter((part) => {
|
|
1132
1168
|
return part !== "md";
|
|
1133
1169
|
}).join("."));
|
|
@@ -1136,7 +1172,7 @@ function templateReleaseNoteSetStatus(program) {
|
|
|
1136
1172
|
await validateReleaseDocument(name, versionNumber, initialDocument);
|
|
1137
1173
|
const [userEditableSectionStart, userEditableSectionEnd] = getMarkdownCommentPair("user-editable-section");
|
|
1138
1174
|
const editableSection = getMarkdownBlock(initialDocument, userEditableSectionStart, userEditableSectionEnd);
|
|
1139
|
-
if (editableSection === null) throw new
|
|
1175
|
+
if (editableSection === null) throw new _alextheman_utility_v6.DataError({
|
|
1140
1176
|
startMarker: userEditableSectionStart,
|
|
1141
1177
|
endMarker: userEditableSectionEnd
|
|
1142
1178
|
}, "EDITABLE_SECTION_NOT_FOUND", "Could not find editable section in the provided document.");
|
|
@@ -1168,7 +1204,7 @@ function template(program) {
|
|
|
1168
1204
|
//#endregion
|
|
1169
1205
|
//#region package.json
|
|
1170
1206
|
var name = "alex-c-line";
|
|
1171
|
-
var version$1 = "2.
|
|
1207
|
+
var version$1 = "2.7.1";
|
|
1172
1208
|
var description = "Command-line tool with commands to streamline the developer workflow.";
|
|
1173
1209
|
//#endregion
|
|
1174
1210
|
//#region src/utility/updates/checkUpdate.ts
|
|
@@ -1237,7 +1273,7 @@ function parseZodSchemaForProgram(program, schema, data) {
|
|
|
1237
1273
|
try {
|
|
1238
1274
|
return (0, _alextheman_utility.parseZodSchema)(schema, data);
|
|
1239
1275
|
} catch (error) {
|
|
1240
|
-
if (
|
|
1276
|
+
if (_alextheman_utility_v6.DataError.check(error)) convertDataErrorToProgramError(error, program);
|
|
1241
1277
|
throw error;
|
|
1242
1278
|
}
|
|
1243
1279
|
}
|
|
@@ -1317,12 +1353,12 @@ async function noFileDependencies(program) {
|
|
|
1317
1353
|
};
|
|
1318
1354
|
if (Object.keys(allFileDependencies.dependencies ?? {}).length === 0) delete allFileDependencies.dependencies;
|
|
1319
1355
|
if (Object.keys(allFileDependencies.devDependencies ?? {}).length === 0) delete allFileDependencies.devDependencies;
|
|
1320
|
-
if (Object.keys(allFileDependencies).length !== 0) program.error(`${
|
|
1356
|
+
if (Object.keys(allFileDependencies).length !== 0) program.error(`${ERROR_PREFIX} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
|
|
1321
1357
|
`, {
|
|
1322
1358
|
exitCode: 2,
|
|
1323
1359
|
code: "FILE_DEPENDENCIES_FOUND"
|
|
1324
1360
|
});
|
|
1325
|
-
console.info(`${
|
|
1361
|
+
console.info(`${SUCCESS_PREFIX} No file dependencies found!`);
|
|
1326
1362
|
}
|
|
1327
1363
|
//#endregion
|
|
1328
1364
|
//#region src/cli/commands/package-json/check/noPreReleaseDependencies.ts
|
|
@@ -1340,7 +1376,7 @@ async function noPreReleaseDependencies(program) {
|
|
|
1340
1376
|
for (const [dependencyName, dependencyVersionRange] of Object.entries(dependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDependencies[dependencyName] = dependencyVersionRange;
|
|
1341
1377
|
for (const [dependencyName, dependencyVersionRange] of Object.entries(devDependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDevDependencies[dependencyName] = dependencyVersionRange;
|
|
1342
1378
|
if (Object.keys(preReleaseDependencies).length !== 0 || Object.keys(preReleaseDevDependencies).length !== 0) program.error(_alextheman_utility.normaliseIndents`
|
|
1343
|
-
${
|
|
1379
|
+
${ERROR_PREFIX} Pre-release version pinning is not allowed. Found the following violations:
|
|
1344
1380
|
|
|
1345
1381
|
` + JSON.stringify({
|
|
1346
1382
|
dependencies: preReleaseDependencies,
|
|
@@ -1349,7 +1385,7 @@ async function noPreReleaseDependencies(program) {
|
|
|
1349
1385
|
exitCode: 2,
|
|
1350
1386
|
code: "UNEXPECTED_PRE_RELEASE_VERSION"
|
|
1351
1387
|
});
|
|
1352
|
-
console.info(`${
|
|
1388
|
+
console.info(`${SUCCESS_PREFIX} No pre-release versions found!`);
|
|
1353
1389
|
}
|
|
1354
1390
|
//#endregion
|
|
1355
1391
|
//#region src/cli/commands/package-json/check/index.ts
|
|
@@ -1364,7 +1400,7 @@ function packageJsonCheck(program) {
|
|
|
1364
1400
|
}).action(async ({ rules }) => {
|
|
1365
1401
|
if (rules?.includes("no-pre-release-dependencies")) await noPreReleaseDependencies(program);
|
|
1366
1402
|
if (rules?.includes("no-file-dependencies")) await noFileDependencies(program);
|
|
1367
|
-
console.info(`${
|
|
1403
|
+
console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
|
|
1368
1404
|
});
|
|
1369
1405
|
}
|
|
1370
1406
|
//#endregion
|
|
@@ -1382,6 +1418,7 @@ function createCommands(program) {
|
|
|
1382
1418
|
internal,
|
|
1383
1419
|
localPackage,
|
|
1384
1420
|
packageJson,
|
|
1421
|
+
pyproject,
|
|
1385
1422
|
root,
|
|
1386
1423
|
template,
|
|
1387
1424
|
update,
|
|
@@ -1393,7 +1430,7 @@ function createCommands(program) {
|
|
|
1393
1430
|
//#region src/utility/errors/formatError.ts
|
|
1394
1431
|
function formatError(error) {
|
|
1395
1432
|
if (error instanceof execa.ExecaError) {
|
|
1396
|
-
const dataError = new
|
|
1433
|
+
const dataError = new _alextheman_utility_v6.DataError({
|
|
1397
1434
|
cwd: error.cwd,
|
|
1398
1435
|
command: error.command,
|
|
1399
1436
|
exitCode: error.exitCode,
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
-
import { DataError
|
|
4
|
+
import { DataError } from "@alextheman/utility/v6";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import boxen from "boxen";
|
|
7
7
|
import figlet from "figlet";
|
|
8
8
|
import envPaths from "env-paths";
|
|
9
9
|
import path from "node:path";
|
|
10
|
+
import { ONE_DAY_IN_MILLISECONDS, VersionNumber, fillArray, getStringsAndInterpolations, interpolate, isTemplateStringsArray, kebabToCamel, normaliseIndents, omitProperties, parseBoolean, parseVersionType, parseZodSchema, parseZodSchemaAsync, removeDuplicates, removeUndefinedFromObject, stringifyDotenv } from "@alextheman/utility";
|
|
10
11
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
11
12
|
import { access, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
12
13
|
import { parse } from "dotenv";
|
|
@@ -14,6 +15,7 @@ import { ExecaError, execa } from "execa";
|
|
|
14
15
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
16
|
import { DependencyGroup, PackageManager, getDependenciesFromGroup, getExpectedTgzName, getPackageJsonContents, packageJsonNotFoundError } from "@alextheman/utility/internal";
|
|
16
17
|
import z from "zod";
|
|
18
|
+
import { parse as parse$1 } from "toml";
|
|
17
19
|
import matter from "gray-matter";
|
|
18
20
|
import { parseFilePath } from "@alextheman/utility/node";
|
|
19
21
|
import axios from "axios";
|
|
@@ -89,8 +91,8 @@ function cache(program) {
|
|
|
89
91
|
loadCommands(program.command("cache").description("Commands related to the alex-c-line cache"), { cachePath });
|
|
90
92
|
}
|
|
91
93
|
//#endregion
|
|
92
|
-
//#region src/utility/constants/
|
|
93
|
-
const
|
|
94
|
+
//#region src/utility/constants/ERROR_PREFIX.ts
|
|
95
|
+
const ERROR_PREFIX = "❌ ERROR:";
|
|
94
96
|
//#endregion
|
|
95
97
|
//#region src/utility/envFile/upsertDotenvFile.ts
|
|
96
98
|
async function upsertDotenvFile(contents, envFilePath) {
|
|
@@ -101,14 +103,14 @@ async function upsertDotenvFile(contents, envFilePath) {
|
|
|
101
103
|
async function addVariable(program, envFileContents, file) {
|
|
102
104
|
const newVariableName = await input({ message: "Please enter the name of the environment variable you would like to add." });
|
|
103
105
|
if (newVariableName in envFileContents) program.error(`
|
|
104
|
-
${
|
|
106
|
+
${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
|
|
105
107
|
Variable name already exists. If you wish to edit this variable, please select it from the initial menu instead.
|
|
106
108
|
`, {
|
|
107
109
|
exitCode: 2,
|
|
108
110
|
code: "DUPLICATE_ENVIRONMENT_VARIABLE_NAME"
|
|
109
111
|
});
|
|
110
112
|
if (/[ \t\r\n]/.test(newVariableName)) program.error(normaliseIndents`
|
|
111
|
-
${
|
|
113
|
+
${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
|
|
112
114
|
Environment variables are not allowed to have whitespace.
|
|
113
115
|
`, {
|
|
114
116
|
exitCode: 2,
|
|
@@ -243,7 +245,7 @@ function checkLockfileVersionDiscrepancy(program) {
|
|
|
243
245
|
const { version: packageVersion } = JSON.parse(await readFile(path.resolve(process.cwd(), "package.json"), "utf-8"));
|
|
244
246
|
const { version: packageLockVersion } = JSON.parse(await readFile(path.resolve(process.cwd(), "package-lock.json"), "utf-8"));
|
|
245
247
|
if (packageVersion !== packageLockVersion) {
|
|
246
|
-
console.error(`${
|
|
248
|
+
console.error(`${ERROR_PREFIX} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
|
|
247
249
|
process.exitCode = 1;
|
|
248
250
|
return;
|
|
249
251
|
}
|
|
@@ -256,7 +258,7 @@ function gitPostMergeCleanup(program) {
|
|
|
256
258
|
program.command("git-post-merge-cleanup").alias("git-cleanup").description("Run after merging into a given branch to quickly clean up").argument("[branch]", "The branch you want to merge into", "main").option("--rebase", "Enable if your repository mainly rebases into main", true).action(async (branch, { rebase }) => {
|
|
257
259
|
console.info(`Running git-post-merge-cleanup in ${rebase ? "rebase" : "merge"} mode...`);
|
|
258
260
|
const { stdout: currentBranch } = await execa`git branch --show-current`;
|
|
259
|
-
if (currentBranch === branch) program.error(`${
|
|
261
|
+
if (currentBranch === branch) program.error(`${ERROR_PREFIX} Cannot run cleanup on ${branch} branch!`, {
|
|
260
262
|
exitCode: 1,
|
|
261
263
|
code: "INVALID_BRANCH"
|
|
262
264
|
});
|
|
@@ -272,7 +274,7 @@ function gitPostMergeCleanup(program) {
|
|
|
272
274
|
const { stdout: changes } = await execa`git diff ${branch}..${currentBranch}`;
|
|
273
275
|
if (changes) {
|
|
274
276
|
await execa`git checkout ${currentBranch}`;
|
|
275
|
-
program.error(`${
|
|
277
|
+
program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
|
|
276
278
|
exitCode: 1,
|
|
277
279
|
code: "CHANGES_NOT_MERGED"
|
|
278
280
|
});
|
|
@@ -282,7 +284,7 @@ function gitPostMergeCleanup(program) {
|
|
|
282
284
|
const { stdout: branchDeletedMessage, exitCode } = await execa({ reject: false })`git branch --delete ${currentBranch}`;
|
|
283
285
|
if (exitCode !== 0) {
|
|
284
286
|
await execa`git checkout ${currentBranch}`;
|
|
285
|
-
program.error(`${
|
|
287
|
+
program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
|
|
286
288
|
exitCode: 1,
|
|
287
289
|
code: "CHANGES_NOT_MERGED"
|
|
288
290
|
});
|
|
@@ -292,8 +294,8 @@ function gitPostMergeCleanup(program) {
|
|
|
292
294
|
});
|
|
293
295
|
}
|
|
294
296
|
//#endregion
|
|
295
|
-
//#region src/utility/constants/
|
|
296
|
-
const
|
|
297
|
+
//#region src/utility/constants/WARNING_PREFIX.ts
|
|
298
|
+
const WARNING_PREFIX = "WARNING:";
|
|
297
299
|
//#endregion
|
|
298
300
|
//#region src/utility/fileSystem/readdirSafe.ts
|
|
299
301
|
async function readdirSafe(path) {
|
|
@@ -330,7 +332,7 @@ function internalMediaGenerate(program) {
|
|
|
330
332
|
if (resolution) return await runManimCommand`manim -qh -r ${resolution} ${file}`;
|
|
331
333
|
return await runManimCommand`manim -qh ${file}`;
|
|
332
334
|
} catch (error) {
|
|
333
|
-
if (error instanceof ExecaError) program.error(`${
|
|
335
|
+
if (error instanceof ExecaError) program.error(`${ERROR_PREFIX} An error has occurred with Manim while rendering ${relativePath}.`, {
|
|
334
336
|
exitCode: error.exitCode ?? 1,
|
|
335
337
|
code: "MANIM_ERROR"
|
|
336
338
|
});
|
|
@@ -349,7 +351,7 @@ function internalMediaGenerate(program) {
|
|
|
349
351
|
const statResult = await stat(target);
|
|
350
352
|
if (statResult.isFile()) await renderFile(target);
|
|
351
353
|
else if (statResult.isDirectory()) await readDirectory(target);
|
|
352
|
-
else console.warn(`${
|
|
354
|
+
else console.warn(`${WARNING_PREFIX} Not a file or directory.`);
|
|
353
355
|
});
|
|
354
356
|
}
|
|
355
357
|
//#endregion
|
|
@@ -373,7 +375,7 @@ async function findPackageRoot(startDirectory, packageName) {
|
|
|
373
375
|
throw new DataError({ packageName }, "PACKAGE_ROOT_NOT_FOUND", `Could not find package root for ${packageName}`);
|
|
374
376
|
}
|
|
375
377
|
//#endregion
|
|
376
|
-
//#region src/utility/constants/
|
|
378
|
+
//#region src/utility/constants/ALEX_C_LINE_PACKAGE_ROOT.ts
|
|
377
379
|
const __filename$2 = fileURLToPath(import.meta.url);
|
|
378
380
|
const ALEX_C_LINE_PACKAGE_ROOT = findPackageRoot(path.dirname(__filename$2), "alex-c-line");
|
|
379
381
|
//#endregion
|
|
@@ -624,7 +626,7 @@ function localPackageUse(program) {
|
|
|
624
626
|
stdio: "inherit",
|
|
625
627
|
reject: false
|
|
626
628
|
});
|
|
627
|
-
if (exitCode !== 0) program.error(`${
|
|
629
|
+
if (exitCode !== 0) program.error(`${ERROR_PREFIX} An error occurred during the local \`alex-c-line\` run.`, {
|
|
628
630
|
exitCode,
|
|
629
631
|
code: "LOCAL_ALEX_C_LINE_ERROR"
|
|
630
632
|
});
|
|
@@ -687,6 +689,43 @@ function localPackage(program) {
|
|
|
687
689
|
loadCommands(program.command("local-package").description("Manage the use of local packages in your JavaScript project."), { localPackageUse });
|
|
688
690
|
}
|
|
689
691
|
//#endregion
|
|
692
|
+
//#region src/utility/constants/SUCCESS_PREFIX.ts
|
|
693
|
+
const SUCCESS_PREFIX = chalk.green("✓");
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/cli/commands/pyproject/check/preferExactDependencyVersions.ts
|
|
696
|
+
const pyprojectSchema = z.object({
|
|
697
|
+
project: z.object({ dependencies: z.array(z.string()).optional() }),
|
|
698
|
+
"dependency-groups": z.object({ dev: z.array(z.string()).optional() })
|
|
699
|
+
}).partial();
|
|
700
|
+
async function preferExactDependencyVersions(program) {
|
|
701
|
+
const data = parseZodSchema(pyprojectSchema, parse$1(await readFile("pyproject.toml", "utf-8")));
|
|
702
|
+
const sections = [data.project?.dependencies ?? [], data["dependency-groups"]?.dev ?? []];
|
|
703
|
+
const violations = [];
|
|
704
|
+
for (const dependencies of sections) for (const dependency of dependencies) if (!dependency.includes("==")) violations.push(dependency);
|
|
705
|
+
if (violations.length !== 0) program.error(`${ERROR_PREFIX} Non-exact dependencies found:\n\n${violations.join("\n")}`, {
|
|
706
|
+
code: "NON_EXACT_DEPENDENCIES_FOUND",
|
|
707
|
+
exitCode: 2
|
|
708
|
+
});
|
|
709
|
+
console.info(`${SUCCESS_PREFIX} All dependencies are exactly pinned`);
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/cli/commands/pyproject/check/index.ts
|
|
713
|
+
const RuleName$1 = { PREFER_EXACT_DEPENDENCY_VERSIONS: "prefer-exact-dependency-versions" };
|
|
714
|
+
function pyprojectCheck(program) {
|
|
715
|
+
program.command("check").description("Run checks on your pyproject.toml file").option("--rules <rules>", "The name of the rule to check", (rawRules) => {
|
|
716
|
+
const rawRuleNamesArray = rawRules.split(",");
|
|
717
|
+
return parseZodSchema(z.array(z.enum(RuleName$1)), rawRuleNamesArray);
|
|
718
|
+
}).action(async ({ rules }) => {
|
|
719
|
+
if (rules?.includes("prefer-exact-dependency-versions")) await preferExactDependencyVersions(program);
|
|
720
|
+
console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/cli/commands/pyproject/index.ts
|
|
725
|
+
function pyproject(program) {
|
|
726
|
+
loadCommands(program.command("pyproject").description("Manage the pyproject.toml file."), { pyprojectCheck });
|
|
727
|
+
}
|
|
728
|
+
//#endregion
|
|
690
729
|
//#region src/cli/commands/root/pre-commit/createStepRunner.ts
|
|
691
730
|
const runCommandAndLogToConsole = execa({
|
|
692
731
|
stdio: "inherit",
|
|
@@ -724,7 +763,7 @@ function createStepRunner(program) {
|
|
|
724
763
|
//#endregion
|
|
725
764
|
//#region src/cli/commands/root/pre-commit/getCommandArguments.ts
|
|
726
765
|
function getCommandArguments(program, script, scripts, args) {
|
|
727
|
-
if (!(script in (scripts ?? {}))) program.error(`${
|
|
766
|
+
if (!(script in (scripts ?? {}))) program.error(`${ERROR_PREFIX} Could not find script \`${script}\` in package.json.`, {
|
|
728
767
|
exitCode: 1,
|
|
729
768
|
code: "SCRIPT_NOT_FOUND"
|
|
730
769
|
});
|
|
@@ -757,7 +796,7 @@ function preCommit(program) {
|
|
|
757
796
|
const { allowNoStagedChanges = options?.allowNoStagedChanges, updateIndex = options?.updateIndex } = preCommitConfig;
|
|
758
797
|
const { exitCode: diffExitCode } = await execa({ reject: false })`git diff --cached --quiet`;
|
|
759
798
|
switch (diffExitCode) {
|
|
760
|
-
case 128: program.error(`${
|
|
799
|
+
case 128: program.error(`${ERROR_PREFIX} Not currently in a Git repository`, {
|
|
761
800
|
exitCode: 1,
|
|
762
801
|
code: "GIT_DIFF_FAILED"
|
|
763
802
|
});
|
|
@@ -869,12 +908,9 @@ function templatePullRequest(program) {
|
|
|
869
908
|
loadCommands(program.command("pull-request").description("Manage the pull request templates."), { templatePullRequestCreate });
|
|
870
909
|
}
|
|
871
910
|
//#endregion
|
|
872
|
-
//#region src/utility/constants/successPrefix.ts
|
|
873
|
-
const successPrefix = chalk.green("✓");
|
|
874
|
-
//#endregion
|
|
875
911
|
//#region src/utility/errors/convertDataErrorToProgramError.ts
|
|
876
912
|
function convertDataErrorToProgramError(dataError, program, options) {
|
|
877
|
-
program.error(`${
|
|
913
|
+
program.error(`${ERROR_PREFIX} ${dataError.message}`, {
|
|
878
914
|
exitCode: options?.exitCode ?? 1,
|
|
879
915
|
code: dataError.code
|
|
880
916
|
});
|
|
@@ -975,7 +1011,7 @@ function templateReleaseNoteCheck(program) {
|
|
|
975
1011
|
}).join("."));
|
|
976
1012
|
try {
|
|
977
1013
|
await validateReleaseDocument(name, documentVersion, fileContents, expectedReleaseStatus);
|
|
978
|
-
console.info(`${
|
|
1014
|
+
console.info(`${SUCCESS_PREFIX} Release document is valid!`);
|
|
979
1015
|
} catch (error) {
|
|
980
1016
|
if (DataError.check(error)) convertDataErrorToProgramError(error, program, { exitCode: 2 });
|
|
981
1017
|
else throw error;
|
|
@@ -1067,7 +1103,7 @@ function templateReleaseNoteCreate(program) {
|
|
|
1067
1103
|
await mkdir(path.dirname(releaseNotePath), { recursive: true });
|
|
1068
1104
|
await writeFile(releaseNotePath, releaseNoteTemplate, { flag: "wx" });
|
|
1069
1105
|
} catch (error) {
|
|
1070
|
-
if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${
|
|
1106
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${ERROR_PREFIX} Release notes already exist.`, {
|
|
1071
1107
|
exitCode: 1,
|
|
1072
1108
|
code: "RELEASE_NOTE_EXISTS"
|
|
1073
1109
|
});
|
|
@@ -1137,7 +1173,7 @@ function template(program) {
|
|
|
1137
1173
|
//#endregion
|
|
1138
1174
|
//#region package.json
|
|
1139
1175
|
var name = "alex-c-line";
|
|
1140
|
-
var version$1 = "2.
|
|
1176
|
+
var version$1 = "2.7.1";
|
|
1141
1177
|
var description = "Command-line tool with commands to streamline the developer workflow.";
|
|
1142
1178
|
//#endregion
|
|
1143
1179
|
//#region src/utility/updates/checkUpdate.ts
|
|
@@ -1286,12 +1322,12 @@ async function noFileDependencies(program) {
|
|
|
1286
1322
|
};
|
|
1287
1323
|
if (Object.keys(allFileDependencies.dependencies ?? {}).length === 0) delete allFileDependencies.dependencies;
|
|
1288
1324
|
if (Object.keys(allFileDependencies.devDependencies ?? {}).length === 0) delete allFileDependencies.devDependencies;
|
|
1289
|
-
if (Object.keys(allFileDependencies).length !== 0) program.error(`${
|
|
1325
|
+
if (Object.keys(allFileDependencies).length !== 0) program.error(`${ERROR_PREFIX} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
|
|
1290
1326
|
`, {
|
|
1291
1327
|
exitCode: 2,
|
|
1292
1328
|
code: "FILE_DEPENDENCIES_FOUND"
|
|
1293
1329
|
});
|
|
1294
|
-
console.info(`${
|
|
1330
|
+
console.info(`${SUCCESS_PREFIX} No file dependencies found!`);
|
|
1295
1331
|
}
|
|
1296
1332
|
//#endregion
|
|
1297
1333
|
//#region src/cli/commands/package-json/check/noPreReleaseDependencies.ts
|
|
@@ -1309,7 +1345,7 @@ async function noPreReleaseDependencies(program) {
|
|
|
1309
1345
|
for (const [dependencyName, dependencyVersionRange] of Object.entries(dependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDependencies[dependencyName] = dependencyVersionRange;
|
|
1310
1346
|
for (const [dependencyName, dependencyVersionRange] of Object.entries(devDependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDevDependencies[dependencyName] = dependencyVersionRange;
|
|
1311
1347
|
if (Object.keys(preReleaseDependencies).length !== 0 || Object.keys(preReleaseDevDependencies).length !== 0) program.error(normaliseIndents`
|
|
1312
|
-
${
|
|
1348
|
+
${ERROR_PREFIX} Pre-release version pinning is not allowed. Found the following violations:
|
|
1313
1349
|
|
|
1314
1350
|
` + JSON.stringify({
|
|
1315
1351
|
dependencies: preReleaseDependencies,
|
|
@@ -1318,7 +1354,7 @@ async function noPreReleaseDependencies(program) {
|
|
|
1318
1354
|
exitCode: 2,
|
|
1319
1355
|
code: "UNEXPECTED_PRE_RELEASE_VERSION"
|
|
1320
1356
|
});
|
|
1321
|
-
console.info(`${
|
|
1357
|
+
console.info(`${SUCCESS_PREFIX} No pre-release versions found!`);
|
|
1322
1358
|
}
|
|
1323
1359
|
//#endregion
|
|
1324
1360
|
//#region src/cli/commands/package-json/check/index.ts
|
|
@@ -1333,7 +1369,7 @@ function packageJsonCheck(program) {
|
|
|
1333
1369
|
}).action(async ({ rules }) => {
|
|
1334
1370
|
if (rules?.includes("no-pre-release-dependencies")) await noPreReleaseDependencies(program);
|
|
1335
1371
|
if (rules?.includes("no-file-dependencies")) await noFileDependencies(program);
|
|
1336
|
-
console.info(`${
|
|
1372
|
+
console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
|
|
1337
1373
|
});
|
|
1338
1374
|
}
|
|
1339
1375
|
//#endregion
|
|
@@ -1351,6 +1387,7 @@ function createCommands(program) {
|
|
|
1351
1387
|
internal,
|
|
1352
1388
|
localPackage,
|
|
1353
1389
|
packageJson,
|
|
1390
|
+
pyproject,
|
|
1354
1391
|
root,
|
|
1355
1392
|
template,
|
|
1356
1393
|
update,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alex-c-line",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "Command-line tool with commands to streamline the developer workflow.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"templates"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@alextheman/utility": "5.
|
|
37
|
+
"@alextheman/utility": "5.13.0",
|
|
38
38
|
"@inquirer/prompts": "8.4.1",
|
|
39
39
|
"axios": "1.15.0",
|
|
40
40
|
"boxen": "8.0.1",
|
|
41
41
|
"chalk": "5.6.2",
|
|
42
42
|
"commander": "14.0.3",
|
|
43
|
-
"dotenv": "17.4.
|
|
43
|
+
"dotenv": "17.4.2",
|
|
44
44
|
"dotenv-stringify": "3.0.1",
|
|
45
45
|
"env-paths": "4.0.0",
|
|
46
46
|
"execa": "9.6.1",
|
|
@@ -48,10 +48,11 @@
|
|
|
48
48
|
"gray-matter": "4.0.3",
|
|
49
49
|
"semver": "7.7.4",
|
|
50
50
|
"supports-color": "10.2.2",
|
|
51
|
+
"toml": "4.1.1",
|
|
51
52
|
"zod": "4.3.6"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@alextheman/eslint-plugin": "5.
|
|
55
|
+
"@alextheman/eslint-plugin": "5.13.0",
|
|
55
56
|
"@commander-js/extra-typings": "14.0.0",
|
|
56
57
|
"@types/eslint": "9.6.1",
|
|
57
58
|
"@types/node": "25.6.0",
|
|
@@ -59,16 +60,16 @@
|
|
|
59
60
|
"@types/update-notifier": "6.0.8",
|
|
60
61
|
"cross-env": "10.1.0",
|
|
61
62
|
"dotenv-cli": "11.0.0",
|
|
62
|
-
"eslint": "10.2.
|
|
63
|
+
"eslint": "10.2.1",
|
|
63
64
|
"husky": "9.1.7",
|
|
64
65
|
"markdownlint-cli2": "0.22.0",
|
|
65
|
-
"prettier": "3.8.
|
|
66
|
+
"prettier": "3.8.3",
|
|
66
67
|
"tempy": "3.2.0",
|
|
67
68
|
"ts-node": "10.9.2",
|
|
68
|
-
"tsdown": "0.21.
|
|
69
|
-
"typescript": "6.0.
|
|
70
|
-
"typescript-eslint": "8.58.
|
|
71
|
-
"vite
|
|
69
|
+
"tsdown": "0.21.9",
|
|
70
|
+
"typescript": "6.0.3",
|
|
71
|
+
"typescript-eslint": "8.58.2",
|
|
72
|
+
"vite": "8.0.8",
|
|
72
73
|
"vitest": "4.1.4"
|
|
73
74
|
},
|
|
74
75
|
"engines": {
|