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.
@@ -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 _alextheman_utility = require("@alextheman/utility");
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 _alextheman_utility.DataError({ subtitleColor }, "INVALID_SUBTITLE_COLOR", "Subtitle color must either be green or white.");
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/errorPrefix.ts
124
- const errorPrefix = "❌ ERROR:";
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
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
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
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
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(`${errorPrefix} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
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(`${errorPrefix} Cannot run cleanup on ${branch} branch!`, {
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(`${errorPrefix} Changes on branch not fully merged!`, {
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(`${errorPrefix} Changes on branch not fully merged!`, {
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/warningPrefix.ts
327
- const warningPrefix = "WARNING:";
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(`${errorPrefix} An error has occurred with Manim while rendering ${relativePath}.`, {
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(`${warningPrefix} Not a file or directory.`);
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 _alextheman_utility.DataError({ packageName }, "PACKAGE_ROOT_NOT_FOUND", `Could not find package root for ${packageName}`);
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/alexCLinePackageRoot.ts
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 _alextheman_utility.DataError({ tgzFiles }, "TGZ_FILE_NOT_FOUND", "Could not find any .tgz files");
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 _alextheman_utility.DataError({
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 _alextheman_utility.DataError({
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 _alextheman_utility.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?");
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 _alextheman_utility.DataError({
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 _alextheman_utility.DataError({ currentDirectory: process.cwd() }, "MISSING_CURRENT_REPOSITORY_PACKAGE_JSON", "Could not find package.json in the current location");
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 _alextheman_utility.DataError({
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 _alextheman_utility.DataError({ localPackagePath }, "MISSING_PACKAGE_REPOSITORY_PACKAGE_JSON", "Could not find package.json in the package repository.");
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 _alextheman_utility.DataError({
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(`${errorPrefix} An error occurred during the local \`alex-c-line\` run.`, {
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(`${errorPrefix} Could not find script \`${script}\` in package.json.`, {
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 _alextheman_utility.DataError({ configPath }, "ALEX_C_LINE_CONFIG_NOT_FOUND", "Could not find the path to the alex-c-line config file. Does it exist?");
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 _alextheman_utility.DataError({
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(`${errorPrefix} Not currently in a Git repository`, {
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 _alextheman_utility.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(", ")}`));
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 _alextheman_utility.DataError({ category }, "CATEGORY_NOT_FOUND", "Category folder not found in the templates folder.");
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 _alextheman_utility.DataError({ placeholder }, "INVALID_PLACEHOLDER", "The placeholder found in frontmatter can not be found in the metadata.");
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 _alextheman_utility.DataError({ projectName }, "PROJECT_NAME_NOT_FOUND", "Could not resolve project name.");
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(`${errorPrefix} ${dataError.message}`, {
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 _alextheman_utility.DataError({ data }, "INVALID_RELEASE_STATUS", "Invalid release status. The release status must be one of \"In progress\" or \"Released\"");
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 _alextheman_utility.DataError({ releaseStatus }, "RELEASE_STATUS_NOT_FOUND", "Could not find release status in document.");
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 _alextheman_utility.DataError({ heading: content.split("\n").slice(0, 3).join("\n") }, "INVALID_HEADING", _alextheman_utility.normaliseIndents`
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 _alextheman_utility.DataError({ releaseStatus }, "INVALID_RELEASE_STATUS", _alextheman_utility.normaliseIndents`
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 _alextheman_utility.DataError({ templateContent }, "SUMMARY_NOT_FOUND", "Expected to find a release summary but it was not found.");
983
- if (!summary) throw new _alextheman_utility.DataError({ content }, "SUMMARY_NOT_FOUND", _alextheman_utility.normaliseIndents`
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 _alextheman_utility.DataError({ summary }, "INVALID_SUMMARY", _alextheman_utility.normaliseIndents`
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 _alextheman_utility.DataError({ content }, "DESCRIPTION_NOT_FOUND", "Expected to find a description of changes but it was not found.");
994
- if (version.type === "major" && !content.includes("## Migration Notes")) throw new _alextheman_utility.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.");
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(`${successPrefix} Release document is valid!`);
1045
+ console.info(`${SUCCESS_PREFIX} Release document is valid!`);
1010
1046
  } catch (error) {
1011
- if (_alextheman_utility.DataError.check(error)) convertDataErrorToProgramError(error, program, { exitCode: 2 });
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 _alextheman_utility.DataError({ placeholder }, "INVALID_PLACEHOLDER", "The placeholder found in frontmatter can not be found in the metadata.");
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 (_alextheman_utility.DataError.check(error) && error.code === "INVALID_VERSION") return (0, _alextheman_utility.parseVersionType)(rawValue);
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(`${errorPrefix} Release notes already exist.`, {
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 _alextheman_utility.DataError({ name: packageInfo?.name }, "INVALID_PACKAGE_JSON", "Invalid package.json - expected package.json to contain a `name` property."));
1130
- if (!documentPath.endsWith("md")) throw new _alextheman_utility.DataError({ documentPath }, "INVALID_FILE_PATH", "Invalid file path. Path must lead to a .md file.");
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 _alextheman_utility.DataError({
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.6.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 (_alextheman_utility.DataError.check(error)) convertDataErrorToProgramError(error, program);
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(`${errorPrefix} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
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(`${successPrefix} No file dependencies found!`);
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
- ${errorPrefix} Pre-release version pinning is not allowed. Found the following violations:
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(`${successPrefix} No pre-release versions found!`);
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(`${successPrefix} Success! All checks passed!`);
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 _alextheman_utility.DataError({
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, ONE_DAY_IN_MILLISECONDS, VersionNumber, fillArray, getStringsAndInterpolations, interpolate, isTemplateStringsArray, kebabToCamel, normaliseIndents, omitProperties, parseBoolean, parseVersionType, parseZodSchema, parseZodSchemaAsync, removeDuplicates, removeUndefinedFromObject, stringifyDotenv } from "@alextheman/utility";
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/errorPrefix.ts
93
- const errorPrefix = "❌ ERROR:";
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
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
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
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
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(`${errorPrefix} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
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(`${errorPrefix} Cannot run cleanup on ${branch} branch!`, {
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(`${errorPrefix} Changes on branch not fully merged!`, {
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(`${errorPrefix} Changes on branch not fully merged!`, {
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/warningPrefix.ts
296
- const warningPrefix = "WARNING:";
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(`${errorPrefix} An error has occurred with Manim while rendering ${relativePath}.`, {
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(`${warningPrefix} Not a file or directory.`);
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/alexCLinePackageRoot.ts
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(`${errorPrefix} An error occurred during the local \`alex-c-line\` run.`, {
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(`${errorPrefix} Could not find script \`${script}\` in package.json.`, {
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(`${errorPrefix} Not currently in a Git repository`, {
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(`${errorPrefix} ${dataError.message}`, {
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(`${successPrefix} Release document is valid!`);
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(`${errorPrefix} Release notes already exist.`, {
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.6.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(`${errorPrefix} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
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(`${successPrefix} No file dependencies found!`);
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
- ${errorPrefix} Pre-release version pinning is not allowed. Found the following violations:
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(`${successPrefix} No pre-release versions found!`);
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(`${successPrefix} Success! All checks passed!`);
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.6.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.11.0",
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.1",
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.12.0",
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.0",
63
+ "eslint": "10.2.1",
63
64
  "husky": "9.1.7",
64
65
  "markdownlint-cli2": "0.22.0",
65
- "prettier": "3.8.2",
66
+ "prettier": "3.8.3",
66
67
  "tempy": "3.2.0",
67
68
  "ts-node": "10.9.2",
68
- "tsdown": "0.21.7",
69
- "typescript": "6.0.2",
70
- "typescript-eslint": "8.58.1",
71
- "vite-tsconfig-paths": "6.1.1",
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": {