alex-c-line 2.6.2 → 2.7.0

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/index.cjs CHANGED
@@ -42,6 +42,7 @@ let _alextheman_utility_internal = require("@alextheman/utility/internal");
42
42
  let zod = require("zod");
43
43
  zod = __toESM(zod);
44
44
  let node_module = require("node:module");
45
+ let toml = require("toml");
45
46
  let gray_matter = require("gray-matter");
46
47
  gray_matter = __toESM(gray_matter);
47
48
  let _alextheman_utility_node = require("@alextheman/utility/node");
@@ -120,8 +121,8 @@ function cache(program) {
120
121
  loadCommands(program.command("cache").description("Commands related to the alex-c-line cache"), { cachePath });
121
122
  }
122
123
  //#endregion
123
- //#region src/utility/constants/errorPrefix.ts
124
- const errorPrefix = "❌ ERROR:";
124
+ //#region src/utility/constants/ERROR_PREFIX.ts
125
+ const ERROR_PREFIX = "❌ ERROR:";
125
126
  //#endregion
126
127
  //#region src/utility/envFile/upsertDotenvFile.ts
127
128
  async function upsertDotenvFile(contents, envFilePath) {
@@ -132,14 +133,14 @@ async function upsertDotenvFile(contents, envFilePath) {
132
133
  async function addVariable(program, envFileContents, file) {
133
134
  const newVariableName = await (0, _inquirer_prompts.input)({ message: "Please enter the name of the environment variable you would like to add." });
134
135
  if (newVariableName in envFileContents) program.error(`
135
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
136
+ ${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
136
137
  Variable name already exists. If you wish to edit this variable, please select it from the initial menu instead.
137
138
  `, {
138
139
  exitCode: 2,
139
140
  code: "DUPLICATE_ENVIRONMENT_VARIABLE_NAME"
140
141
  });
141
142
  if (/[ \t\r\n]/.test(newVariableName)) program.error(_alextheman_utility.normaliseIndents`
142
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
143
+ ${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
143
144
  Environment variables are not allowed to have whitespace.
144
145
  `, {
145
146
  exitCode: 2,
@@ -274,7 +275,7 @@ function checkLockfileVersionDiscrepancy(program) {
274
275
  const { version: packageVersion } = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.resolve(process.cwd(), "package.json"), "utf-8"));
275
276
  const { version: packageLockVersion } = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.resolve(process.cwd(), "package-lock.json"), "utf-8"));
276
277
  if (packageVersion !== packageLockVersion) {
277
- console.error(`${errorPrefix} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
278
+ console.error(`${ERROR_PREFIX} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
278
279
  process.exitCode = 1;
279
280
  return;
280
281
  }
@@ -287,7 +288,7 @@ function gitPostMergeCleanup(program) {
287
288
  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
289
  console.info(`Running git-post-merge-cleanup in ${rebase ? "rebase" : "merge"} mode...`);
289
290
  const { stdout: currentBranch } = await execa.execa`git branch --show-current`;
290
- if (currentBranch === branch) program.error(`${errorPrefix} Cannot run cleanup on ${branch} branch!`, {
291
+ if (currentBranch === branch) program.error(`${ERROR_PREFIX} Cannot run cleanup on ${branch} branch!`, {
291
292
  exitCode: 1,
292
293
  code: "INVALID_BRANCH"
293
294
  });
@@ -303,7 +304,7 @@ function gitPostMergeCleanup(program) {
303
304
  const { stdout: changes } = await execa.execa`git diff ${branch}..${currentBranch}`;
304
305
  if (changes) {
305
306
  await execa.execa`git checkout ${currentBranch}`;
306
- program.error(`${errorPrefix} Changes on branch not fully merged!`, {
307
+ program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
307
308
  exitCode: 1,
308
309
  code: "CHANGES_NOT_MERGED"
309
310
  });
@@ -313,7 +314,7 @@ function gitPostMergeCleanup(program) {
313
314
  const { stdout: branchDeletedMessage, exitCode } = await (0, execa.execa)({ reject: false })`git branch --delete ${currentBranch}`;
314
315
  if (exitCode !== 0) {
315
316
  await execa.execa`git checkout ${currentBranch}`;
316
- program.error(`${errorPrefix} Changes on branch not fully merged!`, {
317
+ program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
317
318
  exitCode: 1,
318
319
  code: "CHANGES_NOT_MERGED"
319
320
  });
@@ -323,8 +324,8 @@ function gitPostMergeCleanup(program) {
323
324
  });
324
325
  }
325
326
  //#endregion
326
- //#region src/utility/constants/warningPrefix.ts
327
- const warningPrefix = "WARNING:";
327
+ //#region src/utility/constants/WARNING_PREFIX.ts
328
+ const WARNING_PREFIX = "WARNING:";
328
329
  //#endregion
329
330
  //#region src/utility/fileSystem/readdirSafe.ts
330
331
  async function readdirSafe(path) {
@@ -361,7 +362,7 @@ function internalMediaGenerate(program) {
361
362
  if (resolution) return await runManimCommand`manim -qh -r ${resolution} ${file}`;
362
363
  return await runManimCommand`manim -qh ${file}`;
363
364
  } catch (error) {
364
- if (error instanceof execa.ExecaError) program.error(`${errorPrefix} An error has occurred with Manim while rendering ${relativePath}.`, {
365
+ if (error instanceof execa.ExecaError) program.error(`${ERROR_PREFIX} An error has occurred with Manim while rendering ${relativePath}.`, {
365
366
  exitCode: error.exitCode ?? 1,
366
367
  code: "MANIM_ERROR"
367
368
  });
@@ -380,7 +381,7 @@ function internalMediaGenerate(program) {
380
381
  const statResult = await (0, node_fs_promises.stat)(target);
381
382
  if (statResult.isFile()) await renderFile(target);
382
383
  else if (statResult.isDirectory()) await readDirectory(target);
383
- else console.warn(`${warningPrefix} Not a file or directory.`);
384
+ else console.warn(`${WARNING_PREFIX} Not a file or directory.`);
384
385
  });
385
386
  }
386
387
  //#endregion
@@ -404,7 +405,7 @@ async function findPackageRoot(startDirectory, packageName) {
404
405
  throw new _alextheman_utility.DataError({ packageName }, "PACKAGE_ROOT_NOT_FOUND", `Could not find package root for ${packageName}`);
405
406
  }
406
407
  //#endregion
407
- //#region src/utility/constants/alexCLinePackageRoot.ts
408
+ //#region src/utility/constants/ALEX_C_LINE_PACKAGE_ROOT.ts
408
409
  const __filename$3 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
409
410
  const ALEX_C_LINE_PACKAGE_ROOT = findPackageRoot(node_path.default.dirname(__filename$3), "alex-c-line");
410
411
  //#endregion
@@ -655,7 +656,7 @@ function localPackageUse(program) {
655
656
  stdio: "inherit",
656
657
  reject: false
657
658
  });
658
- if (exitCode !== 0) program.error(`${errorPrefix} An error occurred during the local \`alex-c-line\` run.`, {
659
+ if (exitCode !== 0) program.error(`${ERROR_PREFIX} An error occurred during the local \`alex-c-line\` run.`, {
659
660
  exitCode,
660
661
  code: "LOCAL_ALEX_C_LINE_ERROR"
661
662
  });
@@ -718,6 +719,43 @@ function localPackage(program) {
718
719
  loadCommands(program.command("local-package").description("Manage the use of local packages in your JavaScript project."), { localPackageUse });
719
720
  }
720
721
  //#endregion
722
+ //#region src/utility/constants/SUCCESS_PREFIX.ts
723
+ const SUCCESS_PREFIX = chalk.default.green("✓");
724
+ //#endregion
725
+ //#region src/cli/commands/pyproject/check/preferExactDependencyVersions.ts
726
+ const pyprojectSchema = zod.default.object({
727
+ project: zod.default.object({ dependencies: zod.default.array(zod.default.string()).optional() }),
728
+ "dependency-groups": zod.default.object({ dev: zod.default.array(zod.default.string()).optional() })
729
+ }).partial();
730
+ async function preferExactDependencyVersions(program) {
731
+ const data = (0, _alextheman_utility.parseZodSchema)(pyprojectSchema, (0, toml.parse)(await (0, node_fs_promises.readFile)("pyproject.toml", "utf-8")));
732
+ const sections = [data.project?.dependencies ?? [], data["dependency-groups"]?.dev ?? []];
733
+ const violations = [];
734
+ for (const dependencies of sections) for (const dependency of dependencies) if (!dependency.includes("==")) violations.push(dependency);
735
+ if (violations.length !== 0) program.error(`${ERROR_PREFIX} Non-exact dependencies found:\n\n${violations.join("\n")}`, {
736
+ code: "NON_EXACT_DEPENDENCIES_FOUND",
737
+ exitCode: 2
738
+ });
739
+ console.info(`${SUCCESS_PREFIX} All dependencies are exactly pinned`);
740
+ }
741
+ //#endregion
742
+ //#region src/cli/commands/pyproject/check/index.ts
743
+ const RuleName$1 = { PREFER_EXACT_DEPENDENCY_VERSIONS: "prefer-exact-dependency-versions" };
744
+ function pyprojectCheck(program) {
745
+ program.command("check").description("Run checks on your pyproject.toml file").option("--rules <rules>", "The name of the rule to check", (rawRules) => {
746
+ const rawRuleNamesArray = rawRules.split(",");
747
+ return (0, _alextheman_utility.parseZodSchema)(zod.default.array(zod.default.enum(RuleName$1)), rawRuleNamesArray);
748
+ }).action(async ({ rules }) => {
749
+ if (rules?.includes("prefer-exact-dependency-versions")) await preferExactDependencyVersions(program);
750
+ console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
751
+ });
752
+ }
753
+ //#endregion
754
+ //#region src/cli/commands/pyproject/index.ts
755
+ function pyproject(program) {
756
+ loadCommands(program.command("pyproject").description("Manage the pyproject.toml file."), { pyprojectCheck });
757
+ }
758
+ //#endregion
721
759
  //#region src/cli/commands/root/pre-commit/createStepRunner.ts
722
760
  const runCommandAndLogToConsole = (0, execa.execa)({
723
761
  stdio: "inherit",
@@ -755,7 +793,7 @@ function createStepRunner(program) {
755
793
  //#endregion
756
794
  //#region src/cli/commands/root/pre-commit/getCommandArguments.ts
757
795
  function getCommandArguments(program, script, scripts, args) {
758
- if (!(script in (scripts ?? {}))) program.error(`${errorPrefix} Could not find script \`${script}\` in package.json.`, {
796
+ if (!(script in (scripts ?? {}))) program.error(`${ERROR_PREFIX} Could not find script \`${script}\` in package.json.`, {
759
797
  exitCode: 1,
760
798
  code: "SCRIPT_NOT_FOUND"
761
799
  });
@@ -788,7 +826,7 @@ function preCommit(program) {
788
826
  const { allowNoStagedChanges = options?.allowNoStagedChanges, updateIndex = options?.updateIndex } = preCommitConfig;
789
827
  const { exitCode: diffExitCode } = await (0, execa.execa)({ reject: false })`git diff --cached --quiet`;
790
828
  switch (diffExitCode) {
791
- case 128: program.error(`${errorPrefix} Not currently in a Git repository`, {
829
+ case 128: program.error(`${ERROR_PREFIX} Not currently in a Git repository`, {
792
830
  exitCode: 1,
793
831
  code: "GIT_DIFF_FAILED"
794
832
  });
@@ -900,12 +938,9 @@ function templatePullRequest(program) {
900
938
  loadCommands(program.command("pull-request").description("Manage the pull request templates."), { templatePullRequestCreate });
901
939
  }
902
940
  //#endregion
903
- //#region src/utility/constants/successPrefix.ts
904
- const successPrefix = chalk.default.green("✓");
905
- //#endregion
906
941
  //#region src/utility/errors/convertDataErrorToProgramError.ts
907
942
  function convertDataErrorToProgramError(dataError, program, options) {
908
- program.error(`${errorPrefix} ${dataError.message}`, {
943
+ program.error(`${ERROR_PREFIX} ${dataError.message}`, {
909
944
  exitCode: options?.exitCode ?? 1,
910
945
  code: dataError.code
911
946
  });
@@ -1006,7 +1041,7 @@ function templateReleaseNoteCheck(program) {
1006
1041
  }).join("."));
1007
1042
  try {
1008
1043
  await validateReleaseDocument(name, documentVersion, fileContents, expectedReleaseStatus);
1009
- console.info(`${successPrefix} Release document is valid!`);
1044
+ console.info(`${SUCCESS_PREFIX} Release document is valid!`);
1010
1045
  } catch (error) {
1011
1046
  if (_alextheman_utility.DataError.check(error)) convertDataErrorToProgramError(error, program, { exitCode: 2 });
1012
1047
  else throw error;
@@ -1098,7 +1133,7 @@ function templateReleaseNoteCreate(program) {
1098
1133
  await (0, node_fs_promises.mkdir)(node_path.default.dirname(releaseNotePath), { recursive: true });
1099
1134
  await (0, node_fs_promises.writeFile)(releaseNotePath, releaseNoteTemplate, { flag: "wx" });
1100
1135
  } catch (error) {
1101
- if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${errorPrefix} Release notes already exist.`, {
1136
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${ERROR_PREFIX} Release notes already exist.`, {
1102
1137
  exitCode: 1,
1103
1138
  code: "RELEASE_NOTE_EXISTS"
1104
1139
  });
@@ -1168,7 +1203,7 @@ function template(program) {
1168
1203
  //#endregion
1169
1204
  //#region package.json
1170
1205
  var name = "alex-c-line";
1171
- var version$1 = "2.6.2";
1206
+ var version$1 = "2.7.0";
1172
1207
  var description = "Command-line tool with commands to streamline the developer workflow.";
1173
1208
  //#endregion
1174
1209
  //#region src/utility/updates/checkUpdate.ts
@@ -1317,12 +1352,12 @@ async function noFileDependencies(program) {
1317
1352
  };
1318
1353
  if (Object.keys(allFileDependencies.dependencies ?? {}).length === 0) delete allFileDependencies.dependencies;
1319
1354
  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)}
1355
+ if (Object.keys(allFileDependencies).length !== 0) program.error(`${ERROR_PREFIX} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
1321
1356
  `, {
1322
1357
  exitCode: 2,
1323
1358
  code: "FILE_DEPENDENCIES_FOUND"
1324
1359
  });
1325
- console.info(`${successPrefix} No file dependencies found!`);
1360
+ console.info(`${SUCCESS_PREFIX} No file dependencies found!`);
1326
1361
  }
1327
1362
  //#endregion
1328
1363
  //#region src/cli/commands/package-json/check/noPreReleaseDependencies.ts
@@ -1340,7 +1375,7 @@ async function noPreReleaseDependencies(program) {
1340
1375
  for (const [dependencyName, dependencyVersionRange] of Object.entries(dependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDependencies[dependencyName] = dependencyVersionRange;
1341
1376
  for (const [dependencyName, dependencyVersionRange] of Object.entries(devDependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDevDependencies[dependencyName] = dependencyVersionRange;
1342
1377
  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:
1378
+ ${ERROR_PREFIX} Pre-release version pinning is not allowed. Found the following violations:
1344
1379
 
1345
1380
  ` + JSON.stringify({
1346
1381
  dependencies: preReleaseDependencies,
@@ -1349,7 +1384,7 @@ async function noPreReleaseDependencies(program) {
1349
1384
  exitCode: 2,
1350
1385
  code: "UNEXPECTED_PRE_RELEASE_VERSION"
1351
1386
  });
1352
- console.info(`${successPrefix} No pre-release versions found!`);
1387
+ console.info(`${SUCCESS_PREFIX} No pre-release versions found!`);
1353
1388
  }
1354
1389
  //#endregion
1355
1390
  //#region src/cli/commands/package-json/check/index.ts
@@ -1364,7 +1399,7 @@ function packageJsonCheck(program) {
1364
1399
  }).action(async ({ rules }) => {
1365
1400
  if (rules?.includes("no-pre-release-dependencies")) await noPreReleaseDependencies(program);
1366
1401
  if (rules?.includes("no-file-dependencies")) await noFileDependencies(program);
1367
- console.info(`${successPrefix} Success! All checks passed!`);
1402
+ console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
1368
1403
  });
1369
1404
  }
1370
1405
  //#endregion
@@ -1382,6 +1417,7 @@ function createCommands(program) {
1382
1417
  internal,
1383
1418
  localPackage,
1384
1419
  packageJson,
1420
+ pyproject,
1385
1421
  root,
1386
1422
  template,
1387
1423
  update,
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import { ExecaError, execa } from "execa";
14
14
  import { fileURLToPath, pathToFileURL } from "node:url";
15
15
  import { DependencyGroup, PackageManager, getDependenciesFromGroup, getExpectedTgzName, getPackageJsonContents, packageJsonNotFoundError } from "@alextheman/utility/internal";
16
16
  import z from "zod";
17
+ import { parse as parse$1 } from "toml";
17
18
  import matter from "gray-matter";
18
19
  import { parseFilePath } from "@alextheman/utility/node";
19
20
  import axios from "axios";
@@ -89,8 +90,8 @@ function cache(program) {
89
90
  loadCommands(program.command("cache").description("Commands related to the alex-c-line cache"), { cachePath });
90
91
  }
91
92
  //#endregion
92
- //#region src/utility/constants/errorPrefix.ts
93
- const errorPrefix = "❌ ERROR:";
93
+ //#region src/utility/constants/ERROR_PREFIX.ts
94
+ const ERROR_PREFIX = "❌ ERROR:";
94
95
  //#endregion
95
96
  //#region src/utility/envFile/upsertDotenvFile.ts
96
97
  async function upsertDotenvFile(contents, envFilePath) {
@@ -101,14 +102,14 @@ async function upsertDotenvFile(contents, envFilePath) {
101
102
  async function addVariable(program, envFileContents, file) {
102
103
  const newVariableName = await input({ message: "Please enter the name of the environment variable you would like to add." });
103
104
  if (newVariableName in envFileContents) program.error(`
104
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
105
+ ${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
105
106
  Variable name already exists. If you wish to edit this variable, please select it from the initial menu instead.
106
107
  `, {
107
108
  exitCode: 2,
108
109
  code: "DUPLICATE_ENVIRONMENT_VARIABLE_NAME"
109
110
  });
110
111
  if (/[ \t\r\n]/.test(newVariableName)) program.error(normaliseIndents`
111
- ${errorPrefix} Error with chosen environment variable name ${newVariableName}.
112
+ ${ERROR_PREFIX} Error with chosen environment variable name ${newVariableName}.
112
113
  Environment variables are not allowed to have whitespace.
113
114
  `, {
114
115
  exitCode: 2,
@@ -243,7 +244,7 @@ function checkLockfileVersionDiscrepancy(program) {
243
244
  const { version: packageVersion } = JSON.parse(await readFile(path.resolve(process.cwd(), "package.json"), "utf-8"));
244
245
  const { version: packageLockVersion } = JSON.parse(await readFile(path.resolve(process.cwd(), "package-lock.json"), "utf-8"));
245
246
  if (packageVersion !== packageLockVersion) {
246
- console.error(`${errorPrefix} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
247
+ console.error(`${ERROR_PREFIX} package.json and package-lock.json out of sync. Please run \`npm install\` to fix this.`);
247
248
  process.exitCode = 1;
248
249
  return;
249
250
  }
@@ -256,7 +257,7 @@ function gitPostMergeCleanup(program) {
256
257
  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
258
  console.info(`Running git-post-merge-cleanup in ${rebase ? "rebase" : "merge"} mode...`);
258
259
  const { stdout: currentBranch } = await execa`git branch --show-current`;
259
- if (currentBranch === branch) program.error(`${errorPrefix} Cannot run cleanup on ${branch} branch!`, {
260
+ if (currentBranch === branch) program.error(`${ERROR_PREFIX} Cannot run cleanup on ${branch} branch!`, {
260
261
  exitCode: 1,
261
262
  code: "INVALID_BRANCH"
262
263
  });
@@ -272,7 +273,7 @@ function gitPostMergeCleanup(program) {
272
273
  const { stdout: changes } = await execa`git diff ${branch}..${currentBranch}`;
273
274
  if (changes) {
274
275
  await execa`git checkout ${currentBranch}`;
275
- program.error(`${errorPrefix} Changes on branch not fully merged!`, {
276
+ program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
276
277
  exitCode: 1,
277
278
  code: "CHANGES_NOT_MERGED"
278
279
  });
@@ -282,7 +283,7 @@ function gitPostMergeCleanup(program) {
282
283
  const { stdout: branchDeletedMessage, exitCode } = await execa({ reject: false })`git branch --delete ${currentBranch}`;
283
284
  if (exitCode !== 0) {
284
285
  await execa`git checkout ${currentBranch}`;
285
- program.error(`${errorPrefix} Changes on branch not fully merged!`, {
286
+ program.error(`${ERROR_PREFIX} Changes on branch not fully merged!`, {
286
287
  exitCode: 1,
287
288
  code: "CHANGES_NOT_MERGED"
288
289
  });
@@ -292,8 +293,8 @@ function gitPostMergeCleanup(program) {
292
293
  });
293
294
  }
294
295
  //#endregion
295
- //#region src/utility/constants/warningPrefix.ts
296
- const warningPrefix = "WARNING:";
296
+ //#region src/utility/constants/WARNING_PREFIX.ts
297
+ const WARNING_PREFIX = "WARNING:";
297
298
  //#endregion
298
299
  //#region src/utility/fileSystem/readdirSafe.ts
299
300
  async function readdirSafe(path) {
@@ -330,7 +331,7 @@ function internalMediaGenerate(program) {
330
331
  if (resolution) return await runManimCommand`manim -qh -r ${resolution} ${file}`;
331
332
  return await runManimCommand`manim -qh ${file}`;
332
333
  } catch (error) {
333
- if (error instanceof ExecaError) program.error(`${errorPrefix} An error has occurred with Manim while rendering ${relativePath}.`, {
334
+ if (error instanceof ExecaError) program.error(`${ERROR_PREFIX} An error has occurred with Manim while rendering ${relativePath}.`, {
334
335
  exitCode: error.exitCode ?? 1,
335
336
  code: "MANIM_ERROR"
336
337
  });
@@ -349,7 +350,7 @@ function internalMediaGenerate(program) {
349
350
  const statResult = await stat(target);
350
351
  if (statResult.isFile()) await renderFile(target);
351
352
  else if (statResult.isDirectory()) await readDirectory(target);
352
- else console.warn(`${warningPrefix} Not a file or directory.`);
353
+ else console.warn(`${WARNING_PREFIX} Not a file or directory.`);
353
354
  });
354
355
  }
355
356
  //#endregion
@@ -373,7 +374,7 @@ async function findPackageRoot(startDirectory, packageName) {
373
374
  throw new DataError({ packageName }, "PACKAGE_ROOT_NOT_FOUND", `Could not find package root for ${packageName}`);
374
375
  }
375
376
  //#endregion
376
- //#region src/utility/constants/alexCLinePackageRoot.ts
377
+ //#region src/utility/constants/ALEX_C_LINE_PACKAGE_ROOT.ts
377
378
  const __filename$2 = fileURLToPath(import.meta.url);
378
379
  const ALEX_C_LINE_PACKAGE_ROOT = findPackageRoot(path.dirname(__filename$2), "alex-c-line");
379
380
  //#endregion
@@ -624,7 +625,7 @@ function localPackageUse(program) {
624
625
  stdio: "inherit",
625
626
  reject: false
626
627
  });
627
- if (exitCode !== 0) program.error(`${errorPrefix} An error occurred during the local \`alex-c-line\` run.`, {
628
+ if (exitCode !== 0) program.error(`${ERROR_PREFIX} An error occurred during the local \`alex-c-line\` run.`, {
628
629
  exitCode,
629
630
  code: "LOCAL_ALEX_C_LINE_ERROR"
630
631
  });
@@ -687,6 +688,43 @@ function localPackage(program) {
687
688
  loadCommands(program.command("local-package").description("Manage the use of local packages in your JavaScript project."), { localPackageUse });
688
689
  }
689
690
  //#endregion
691
+ //#region src/utility/constants/SUCCESS_PREFIX.ts
692
+ const SUCCESS_PREFIX = chalk.green("✓");
693
+ //#endregion
694
+ //#region src/cli/commands/pyproject/check/preferExactDependencyVersions.ts
695
+ const pyprojectSchema = z.object({
696
+ project: z.object({ dependencies: z.array(z.string()).optional() }),
697
+ "dependency-groups": z.object({ dev: z.array(z.string()).optional() })
698
+ }).partial();
699
+ async function preferExactDependencyVersions(program) {
700
+ const data = parseZodSchema(pyprojectSchema, parse$1(await readFile("pyproject.toml", "utf-8")));
701
+ const sections = [data.project?.dependencies ?? [], data["dependency-groups"]?.dev ?? []];
702
+ const violations = [];
703
+ for (const dependencies of sections) for (const dependency of dependencies) if (!dependency.includes("==")) violations.push(dependency);
704
+ if (violations.length !== 0) program.error(`${ERROR_PREFIX} Non-exact dependencies found:\n\n${violations.join("\n")}`, {
705
+ code: "NON_EXACT_DEPENDENCIES_FOUND",
706
+ exitCode: 2
707
+ });
708
+ console.info(`${SUCCESS_PREFIX} All dependencies are exactly pinned`);
709
+ }
710
+ //#endregion
711
+ //#region src/cli/commands/pyproject/check/index.ts
712
+ const RuleName$1 = { PREFER_EXACT_DEPENDENCY_VERSIONS: "prefer-exact-dependency-versions" };
713
+ function pyprojectCheck(program) {
714
+ program.command("check").description("Run checks on your pyproject.toml file").option("--rules <rules>", "The name of the rule to check", (rawRules) => {
715
+ const rawRuleNamesArray = rawRules.split(",");
716
+ return parseZodSchema(z.array(z.enum(RuleName$1)), rawRuleNamesArray);
717
+ }).action(async ({ rules }) => {
718
+ if (rules?.includes("prefer-exact-dependency-versions")) await preferExactDependencyVersions(program);
719
+ console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
720
+ });
721
+ }
722
+ //#endregion
723
+ //#region src/cli/commands/pyproject/index.ts
724
+ function pyproject(program) {
725
+ loadCommands(program.command("pyproject").description("Manage the pyproject.toml file."), { pyprojectCheck });
726
+ }
727
+ //#endregion
690
728
  //#region src/cli/commands/root/pre-commit/createStepRunner.ts
691
729
  const runCommandAndLogToConsole = execa({
692
730
  stdio: "inherit",
@@ -724,7 +762,7 @@ function createStepRunner(program) {
724
762
  //#endregion
725
763
  //#region src/cli/commands/root/pre-commit/getCommandArguments.ts
726
764
  function getCommandArguments(program, script, scripts, args) {
727
- if (!(script in (scripts ?? {}))) program.error(`${errorPrefix} Could not find script \`${script}\` in package.json.`, {
765
+ if (!(script in (scripts ?? {}))) program.error(`${ERROR_PREFIX} Could not find script \`${script}\` in package.json.`, {
728
766
  exitCode: 1,
729
767
  code: "SCRIPT_NOT_FOUND"
730
768
  });
@@ -757,7 +795,7 @@ function preCommit(program) {
757
795
  const { allowNoStagedChanges = options?.allowNoStagedChanges, updateIndex = options?.updateIndex } = preCommitConfig;
758
796
  const { exitCode: diffExitCode } = await execa({ reject: false })`git diff --cached --quiet`;
759
797
  switch (diffExitCode) {
760
- case 128: program.error(`${errorPrefix} Not currently in a Git repository`, {
798
+ case 128: program.error(`${ERROR_PREFIX} Not currently in a Git repository`, {
761
799
  exitCode: 1,
762
800
  code: "GIT_DIFF_FAILED"
763
801
  });
@@ -869,12 +907,9 @@ function templatePullRequest(program) {
869
907
  loadCommands(program.command("pull-request").description("Manage the pull request templates."), { templatePullRequestCreate });
870
908
  }
871
909
  //#endregion
872
- //#region src/utility/constants/successPrefix.ts
873
- const successPrefix = chalk.green("✓");
874
- //#endregion
875
910
  //#region src/utility/errors/convertDataErrorToProgramError.ts
876
911
  function convertDataErrorToProgramError(dataError, program, options) {
877
- program.error(`${errorPrefix} ${dataError.message}`, {
912
+ program.error(`${ERROR_PREFIX} ${dataError.message}`, {
878
913
  exitCode: options?.exitCode ?? 1,
879
914
  code: dataError.code
880
915
  });
@@ -975,7 +1010,7 @@ function templateReleaseNoteCheck(program) {
975
1010
  }).join("."));
976
1011
  try {
977
1012
  await validateReleaseDocument(name, documentVersion, fileContents, expectedReleaseStatus);
978
- console.info(`${successPrefix} Release document is valid!`);
1013
+ console.info(`${SUCCESS_PREFIX} Release document is valid!`);
979
1014
  } catch (error) {
980
1015
  if (DataError.check(error)) convertDataErrorToProgramError(error, program, { exitCode: 2 });
981
1016
  else throw error;
@@ -1067,7 +1102,7 @@ function templateReleaseNoteCreate(program) {
1067
1102
  await mkdir(path.dirname(releaseNotePath), { recursive: true });
1068
1103
  await writeFile(releaseNotePath, releaseNoteTemplate, { flag: "wx" });
1069
1104
  } catch (error) {
1070
- if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${errorPrefix} Release notes already exist.`, {
1105
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") program.error(`${ERROR_PREFIX} Release notes already exist.`, {
1071
1106
  exitCode: 1,
1072
1107
  code: "RELEASE_NOTE_EXISTS"
1073
1108
  });
@@ -1137,7 +1172,7 @@ function template(program) {
1137
1172
  //#endregion
1138
1173
  //#region package.json
1139
1174
  var name = "alex-c-line";
1140
- var version$1 = "2.6.2";
1175
+ var version$1 = "2.7.0";
1141
1176
  var description = "Command-line tool with commands to streamline the developer workflow.";
1142
1177
  //#endregion
1143
1178
  //#region src/utility/updates/checkUpdate.ts
@@ -1286,12 +1321,12 @@ async function noFileDependencies(program) {
1286
1321
  };
1287
1322
  if (Object.keys(allFileDependencies.dependencies ?? {}).length === 0) delete allFileDependencies.dependencies;
1288
1323
  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)}
1324
+ if (Object.keys(allFileDependencies).length !== 0) program.error(`${ERROR_PREFIX} File dependencies found:\n\n${JSON.stringify(allFileDependencies, void 0, 2)}
1290
1325
  `, {
1291
1326
  exitCode: 2,
1292
1327
  code: "FILE_DEPENDENCIES_FOUND"
1293
1328
  });
1294
- console.info(`${successPrefix} No file dependencies found!`);
1329
+ console.info(`${SUCCESS_PREFIX} No file dependencies found!`);
1295
1330
  }
1296
1331
  //#endregion
1297
1332
  //#region src/cli/commands/package-json/check/noPreReleaseDependencies.ts
@@ -1309,7 +1344,7 @@ async function noPreReleaseDependencies(program) {
1309
1344
  for (const [dependencyName, dependencyVersionRange] of Object.entries(dependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDependencies[dependencyName] = dependencyVersionRange;
1310
1345
  for (const [dependencyName, dependencyVersionRange] of Object.entries(devDependencies)) if (isPreRelease(dependencyVersionRange)) preReleaseDevDependencies[dependencyName] = dependencyVersionRange;
1311
1346
  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:
1347
+ ${ERROR_PREFIX} Pre-release version pinning is not allowed. Found the following violations:
1313
1348
 
1314
1349
  ` + JSON.stringify({
1315
1350
  dependencies: preReleaseDependencies,
@@ -1318,7 +1353,7 @@ async function noPreReleaseDependencies(program) {
1318
1353
  exitCode: 2,
1319
1354
  code: "UNEXPECTED_PRE_RELEASE_VERSION"
1320
1355
  });
1321
- console.info(`${successPrefix} No pre-release versions found!`);
1356
+ console.info(`${SUCCESS_PREFIX} No pre-release versions found!`);
1322
1357
  }
1323
1358
  //#endregion
1324
1359
  //#region src/cli/commands/package-json/check/index.ts
@@ -1333,7 +1368,7 @@ function packageJsonCheck(program) {
1333
1368
  }).action(async ({ rules }) => {
1334
1369
  if (rules?.includes("no-pre-release-dependencies")) await noPreReleaseDependencies(program);
1335
1370
  if (rules?.includes("no-file-dependencies")) await noFileDependencies(program);
1336
- console.info(`${successPrefix} Success! All checks passed!`);
1371
+ console.info(`${SUCCESS_PREFIX} Success! All checks passed!`);
1337
1372
  });
1338
1373
  }
1339
1374
  //#endregion
@@ -1351,6 +1386,7 @@ function createCommands(program) {
1351
1386
  internal,
1352
1387
  localPackage,
1353
1388
  packageJson,
1389
+ pyproject,
1354
1390
  root,
1355
1391
  template,
1356
1392
  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.0",
4
4
  "description": "Command-line tool with commands to streamline the developer workflow.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,6 +48,7 @@
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": {