complete-cli 1.0.1 → 1.0.3

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.
Files changed (48) hide show
  1. package/dist/107.js +1 -0
  2. package/dist/140.js +1 -0
  3. package/dist/242.js +1 -0
  4. package/dist/269.js +1 -0
  5. package/dist/309.js +1 -0
  6. package/dist/367.js +1 -0
  7. package/dist/541.js +1 -0
  8. package/dist/55.js +1 -0
  9. package/dist/717.js +1 -0
  10. package/dist/742.js +1 -0
  11. package/dist/769.js +1 -0
  12. package/dist/770.js +1 -0
  13. package/dist/914.js +1 -0
  14. package/dist/951.js +1 -0
  15. package/dist/main.js +3 -25
  16. package/dist/main.js.LICENSE.txt +78 -0
  17. package/file-templates/dynamic/package.json +9 -10
  18. package/file-templates/static/scripts/build.ts +1 -2
  19. package/package.json +18 -20
  20. package/src/commands/CheckCommand.ts +1 -1
  21. package/src/commands/InitCommand.ts +1 -1
  22. package/src/commands/NukeCommand.ts +1 -2
  23. package/src/commands/PublishCommand.ts +4 -4
  24. package/src/commands/init/createProject.ts +3 -3
  25. package/src/commands/init/getAuthorName.ts +1 -1
  26. package/src/commands/init/packageManager.ts +8 -8
  27. package/src/commands/init/vsCodeInit.ts +27 -8
  28. package/src/git.ts +12 -9
  29. package/src/main.ts +0 -3
  30. package/dist/commands/CheckCommand.js +0 -142
  31. package/dist/commands/InitCommand.js +0 -64
  32. package/dist/commands/NukeCommand.js +0 -13
  33. package/dist/commands/PublishCommand.js +0 -163
  34. package/dist/commands/UpdateCommand.js +0 -15
  35. package/dist/commands/check/check.test.js +0 -86
  36. package/dist/commands/check/getTruncatedText.js +0 -139
  37. package/dist/commands/init/checkIfProjectPathExists.js +0 -21
  38. package/dist/commands/init/createProject.js +0 -131
  39. package/dist/commands/init/getAuthorName.js +0 -17
  40. package/dist/commands/init/getProjectPath.js +0 -80
  41. package/dist/commands/init/packageManager.js +0 -35
  42. package/dist/commands/init/vsCodeInit.js +0 -75
  43. package/dist/constants.js +0 -17
  44. package/dist/git.js +0 -129
  45. package/dist/interfaces/GitHubCLIHostsYAML.js +0 -1
  46. package/dist/prompt.js +0 -53
  47. package/dist/validateNoteVersion.js +0 -25
  48. package/src/validateNoteVersion.ts +0 -39
@@ -0,0 +1,78 @@
1
+ /*!
2
+ * fill-range <https://github.com/jonschlinkert/fill-range>
3
+ *
4
+ * Copyright (c) 2014-present, Jon Schlinkert.
5
+ * Licensed under the MIT License.
6
+ */
7
+
8
+ /*!
9
+ * is-extglob <https://github.com/jonschlinkert/is-extglob>
10
+ *
11
+ * Copyright (c) 2014-2016, Jon Schlinkert.
12
+ * Licensed under the MIT License.
13
+ */
14
+
15
+ /*!
16
+ * is-number <https://github.com/jonschlinkert/is-number>
17
+ *
18
+ * Copyright (c) 2014-present, Jon Schlinkert.
19
+ * Released under the MIT License.
20
+ */
21
+
22
+ /*!
23
+ * negotiator
24
+ * Copyright(c) 2012 Federico Romero
25
+ * Copyright(c) 2012-2014 Isaac Z. Schlueter
26
+ * Copyright(c) 2015 Douglas Christopher Wilson
27
+ * MIT Licensed
28
+ */
29
+
30
+ /*!
31
+ * node-progress
32
+ * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
33
+ * MIT Licensed
34
+ */
35
+
36
+ /*!
37
+ * parse-github-url <https://github.com/jonschlinkert/parse-github-url>
38
+ *
39
+ * Copyright (c) 2015-2017, Jon Schlinkert.
40
+ * Released under the MIT License.
41
+ */
42
+
43
+ /*!
44
+ * to-regex-range <https://github.com/micromatch/to-regex-range>
45
+ *
46
+ * Copyright (c) 2015-present, Jon Schlinkert.
47
+ * Released under the MIT License.
48
+ */
49
+
50
+ /*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
51
+
52
+ /*! run-parallel. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
53
+
54
+ /**
55
+ * @license
56
+ * Copyright (c) 2010-2012 Mikeal Rogers
57
+ * Licensed under the Apache License, Version 2.0 (the "License");
58
+ * you may not use this file except in compliance with the License.
59
+ * You may obtain a copy of the License at
60
+ * http://www.apache.org/licenses/LICENSE-2.0
61
+ * Unless required by applicable law or agreed to in writing,
62
+ * software distributed under the License is distributed on an "AS
63
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
64
+ * express or implied. See the License for the specific language
65
+ * governing permissions and limitations under the License.
66
+ */
67
+
68
+ /**
69
+ * @preserve
70
+ * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013)
71
+ *
72
+ * @author <a href="mailto:jensyt@gmail.com">Jens Taylor</a>
73
+ * @see http://github.com/homebrewing/brauhaus-diff
74
+ * @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
75
+ * @see http://github.com/garycourt/murmurhash-js
76
+ * @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
77
+ * @see http://sites.google.com/site/murmurhash/
78
+ */
@@ -1,18 +1,18 @@
1
1
  {
2
- "name": "PROJECT_NAME",
2
+ "name": "project-name",
3
3
  "version": "0.0.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
- "homepage": "https://github.com/AUTHOR_NAME/PROJECT_NAME",
6
+ "homepage": "https://github.com/author-name/project-name",
7
7
  "bugs": {
8
- "url": "https://github.com/AUTHOR_NAME/PROJECT_NAME/issues"
8
+ "url": "https://github.com/author-name/project-name/issues"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "git+https://github.com/AUTHOR_NAME/PROJECT_NAME.git"
12
+ "url": "git+https://github.com/author-name/project-name.git"
13
13
  },
14
14
  "license": "GPL-3.0",
15
- "author": "AUTHOR_NAME",
15
+ "author": "author-name",
16
16
  "type": "module",
17
17
  "files": [
18
18
  "dist",
@@ -29,10 +29,9 @@
29
29
  "update": "complete-cli update"
30
30
  },
31
31
  "devDependencies": {
32
- "complete-cli": "^0.0.1",
33
- "complete-lint": "^0.0.1",
34
- "complete-node": "^0.0.1",
35
- "execa": "^0.0.1",
36
- "typescript": "^0.0.1"
32
+ "complete-cli": "0.0.1",
33
+ "complete-lint": "0.0.1",
34
+ "complete-node": "0.0.1",
35
+ "typescript": "0.0.1"
37
36
  }
38
37
  }
@@ -1,5 +1,4 @@
1
- import { buildScript } from "complete-node";
2
- import { $ } from "execa";
1
+ import { $, buildScript } from "complete-node";
3
2
 
4
3
  await buildScript(async () => {
5
4
  await $`tsc`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "complete-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A command line tool for bootstrapping TypeScript projects.",
5
5
  "keywords": [
6
6
  "typescript"
@@ -33,26 +33,24 @@
33
33
  "start": "tsx ./src/main.ts",
34
34
  "test": "glob \"./src/**/*.test.ts\" --cmd=\"node --import tsx --test --test-reporter spec\""
35
35
  },
36
- "dependencies": {
37
- "@clack/prompts": "^0.10.0",
38
- "chalk": "^5.4.1",
39
- "clipanion": "^4.0.0-rc.4",
40
- "complete-common": "^1.1.0",
41
- "complete-node": "^2.1.0",
42
- "execa": "^9.5.2",
43
- "klaw-sync": "^6.0.0",
44
- "yaml": "^2.7.0"
45
- },
46
36
  "devDependencies": {
47
- "@types/klaw-sync": "^6.0.5",
48
- "@types/node": "^22.13.4",
49
- "@types/prompt": "^1.1.9",
50
- "complete-node": "^2.1.0",
51
- "eslint-plugin-sort-exports": "^0.9.1",
52
- "glob": "^11.0.1",
53
- "typescript": "^5.7.3",
54
- "typescript-eslint": "^8.24.1",
55
- "unbuild": "^3.3.1"
37
+ "@clack/prompts": "0.10.0",
38
+ "@types/klaw-sync": "6.0.5",
39
+ "@types/node": "22.13.5",
40
+ "chalk": "5.4.1",
41
+ "clipanion": "4.0.0-rc.4",
42
+ "complete-common": "^1.1.1",
43
+ "complete-node": "^3.0.1",
44
+ "glob": "11.0.1",
45
+ "klaw-sync": "6.0.0",
46
+ "ts-loader": "9.5.2",
47
+ "tsconfig-paths-webpack-plugin": "^4.2.0",
48
+ "typescript": "5.7.3",
49
+ "typescript-eslint": "8.24.1",
50
+ "webpack": "5.98.0",
51
+ "webpack-cli": "6.0.1",
52
+ "webpack-shebang-plugin": "^1.1.8",
53
+ "yaml": "2.7.0"
56
54
  },
57
55
  "engines": {
58
56
  "node": ">= 20.11.0"
@@ -2,6 +2,7 @@ import chalk from "chalk";
2
2
  import { Command, Option } from "clipanion";
3
3
  import { ReadonlySet } from "complete-common";
4
4
  import {
5
+ $,
5
6
  deleteFileOrDirectory,
6
7
  fatalError,
7
8
  isDirectory,
@@ -9,7 +10,6 @@ import {
9
10
  readFile,
10
11
  writeFile,
11
12
  } from "complete-node";
12
- import { $ } from "execa";
13
13
  import klawSync from "klaw-sync";
14
14
  import path from "node:path";
15
15
  import {
@@ -67,7 +67,7 @@ export class InitCommand extends Command {
67
67
  async execute(): Promise<void> {
68
68
  promptStart();
69
69
 
70
- const packageManager = getPackageManagerUsedForNewProject(this);
70
+ const packageManager = await getPackageManagerUsedForNewProject(this);
71
71
 
72
72
  // Prompt the end-user for some information (and validate it as we go).
73
73
  const { projectPath, createNewDir } = await getProjectPath(
@@ -9,9 +9,8 @@ export class NukeCommand extends Command {
9
9
  'Delete the "node_modules" directory and the package lock file, then reinstall the dependencies for the project.',
10
10
  });
11
11
 
12
- // eslint-disable-next-line @typescript-eslint/require-await
13
12
  async execute(): Promise<void> {
14
- nukeDependencies();
13
+ await nukeDependencies();
15
14
  console.log("Successfully reinstalled dependencies from npm.");
16
15
  }
17
16
  }
@@ -2,6 +2,7 @@ import { Command, Option } from "clipanion";
2
2
  import { isSemanticVersion } from "complete-common";
3
3
  import type { PackageManager } from "complete-node";
4
4
  import {
5
+ $,
5
6
  fatalError,
6
7
  getPackageJSONField,
7
8
  getPackageJSONVersion,
@@ -16,7 +17,6 @@ import {
16
17
  updatePackageJSONDependencies,
17
18
  writeFileAsync,
18
19
  } from "complete-node";
19
- import { $ } from "execa";
20
20
  import path from "node:path";
21
21
  import { CWD, DEFAULT_PACKAGE_MANAGER } from "../constants.js";
22
22
 
@@ -96,7 +96,7 @@ async function prePublish(
96
96
  skipLint: boolean,
97
97
  skipUpdate: boolean,
98
98
  ) {
99
- const packageManager = getPackageManagerUsedForExistingProject();
99
+ const packageManager = await getPackageManagerUsedForExistingProject();
100
100
 
101
101
  await $`git pull --rebase`;
102
102
  await $`git push`;
@@ -110,8 +110,8 @@ async function prePublish(
110
110
  }
111
111
  }
112
112
 
113
- function getPackageManagerUsedForExistingProject(): PackageManager {
114
- const packageManagers = getPackageManagersForProject(CWD);
113
+ async function getPackageManagerUsedForExistingProject(): Promise<PackageManager> {
114
+ const packageManagers = await getPackageManagersForProject(CWD);
115
115
  if (packageManagers.length > 1) {
116
116
  const packageManagerLockFileNames = packageManagers
117
117
  .map((packageManager) => getPackageManagerLockFileName(packageManager))
@@ -2,6 +2,7 @@ import chalk from "chalk";
2
2
  import { repeat } from "complete-common";
3
3
  import type { PackageManager } from "complete-node";
4
4
  import {
5
+ $,
5
6
  copyFileOrDirectory,
6
7
  getFileNamesInDirectory,
7
8
  getPackageManagerInstallCICommand,
@@ -13,7 +14,6 @@ import {
13
14
  updatePackageJSONDependencies,
14
15
  writeFile,
15
16
  } from "complete-node";
16
- import { $ } from "execa";
17
17
  import path from "node:path";
18
18
  import {
19
19
  ACTION_YML,
@@ -147,8 +147,8 @@ function copyDynamicFiles(
147
147
  const template = readFile(templatePath);
148
148
 
149
149
  const packageJSON = template
150
- .replaceAll("PROJECT_NAME", projectName)
151
- .replaceAll("AUTHOR_NAME", authorName ?? "unknown");
150
+ .replaceAll("project-name", projectName)
151
+ .replaceAll("author-name", authorName ?? "unknown");
152
152
 
153
153
  const destinationPath = path.join(projectPath, "package.json");
154
154
  writeFile(destinationPath, packageJSON);
@@ -2,7 +2,7 @@ import { getGitHubUsername } from "../../git.js";
2
2
  import { getInputString, promptError, promptLog } from "../../prompt.js";
3
3
 
4
4
  export async function getAuthorName(): Promise<string | undefined> {
5
- const gitHubUsername = getGitHubUsername();
5
+ const gitHubUsername = await getGitHubUsername();
6
6
  if (gitHubUsername !== undefined) {
7
7
  return gitHubUsername;
8
8
  }
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { PackageManager, commandExists } from "complete-node";
2
+ import { commandExists, PackageManager } from "complete-node";
3
3
  import { DEFAULT_PACKAGE_MANAGER } from "../../constants.js";
4
4
  import { promptError } from "../../prompt.js";
5
5
 
@@ -9,10 +9,10 @@ interface PackageManagerOptions {
9
9
  pnpm: boolean;
10
10
  }
11
11
 
12
- export function getPackageManagerUsedForNewProject(
12
+ export async function getPackageManagerUsedForNewProject(
13
13
  options: PackageManagerOptions,
14
- ): PackageManager {
15
- const packageManagerFromOptions = getPackageManagerFromOptions(options);
14
+ ): Promise<PackageManager> {
15
+ const packageManagerFromOptions = await getPackageManagerFromOptions(options);
16
16
  if (packageManagerFromOptions !== undefined) {
17
17
  return packageManagerFromOptions;
18
18
  }
@@ -20,9 +20,9 @@ export function getPackageManagerUsedForNewProject(
20
20
  return DEFAULT_PACKAGE_MANAGER;
21
21
  }
22
22
 
23
- function getPackageManagerFromOptions(options: PackageManagerOptions) {
23
+ async function getPackageManagerFromOptions(options: PackageManagerOptions) {
24
24
  if (options.npm) {
25
- const npmExists = commandExists("npm");
25
+ const npmExists = await commandExists("npm");
26
26
  if (!npmExists) {
27
27
  promptError(
28
28
  `You specified the "--npm" option, but "${chalk.green(
@@ -35,7 +35,7 @@ function getPackageManagerFromOptions(options: PackageManagerOptions) {
35
35
  }
36
36
 
37
37
  if (options.yarn) {
38
- const yarnExists = commandExists("yarn");
38
+ const yarnExists = await commandExists("yarn");
39
39
  if (!yarnExists) {
40
40
  promptError(
41
41
  `You specified the "--yarn" option, but "${chalk.green(
@@ -48,7 +48,7 @@ function getPackageManagerFromOptions(options: PackageManagerOptions) {
48
48
  }
49
49
 
50
50
  if (options.pnpm) {
51
- const pnpmExists = commandExists("pnpm");
51
+ const pnpmExists = await commandExists("pnpm");
52
52
  if (!pnpmExists) {
53
53
  promptError(
54
54
  `You specified the "--pnpm" option, but "${chalk.green(
@@ -1,5 +1,4 @@
1
- import { commandExists, getJSONC, isFile } from "complete-node";
2
- import { $ } from "execa";
1
+ import { $, commandExists, getJSONC, isFile } from "complete-node";
3
2
  import path from "node:path";
4
3
  import { getInputYesNo, promptError, promptLog } from "../../prompt.js";
5
4
 
@@ -15,7 +14,7 @@ export async function vsCodeInit(
15
14
  vscode: boolean,
16
15
  yes: boolean,
17
16
  ): Promise<void> {
18
- const VSCodeCommand = getVSCodeCommand();
17
+ const VSCodeCommand = await getVSCodeCommand();
19
18
  if (VSCodeCommand === undefined) {
20
19
  promptLog(
21
20
  'VSCode does not seem to be installed. (The "code" command is not in the path.) Skipping VSCode-related things.',
@@ -27,8 +26,26 @@ export async function vsCodeInit(
27
26
  await promptVSCode(projectPath, VSCodeCommand, vscode, yes);
28
27
  }
29
28
 
30
- function getVSCodeCommand(): string | undefined {
31
- return VS_CODE_COMMANDS.find((command) => commandExists(command));
29
+ async function getVSCodeCommand(): Promise<
30
+ (typeof VS_CODE_COMMANDS)[number] | undefined
31
+ > {
32
+ const commandCheckPromises = VS_CODE_COMMANDS.map((command) => ({
33
+ command,
34
+ existsPromise: commandExists(command),
35
+ }));
36
+
37
+ const commandChecks = await Promise.all(
38
+ commandCheckPromises.map(async (check) => ({
39
+ command: check.command,
40
+ exists: await check.existsPromise,
41
+ })),
42
+ );
43
+
44
+ const existingCommands = commandChecks
45
+ .filter((check) => check.exists)
46
+ .map((check) => check.command);
47
+
48
+ return existingCommands[0];
32
49
  }
33
50
 
34
51
  async function installVSCodeExtensions(
@@ -42,14 +59,16 @@ async function installVSCodeExtensions(
42
59
  return;
43
60
  }
44
61
 
45
- const extensions = getExtensionsFromJSON(projectPath);
62
+ const extensions = await getExtensionsFromJSON(projectPath);
46
63
  for (const extensionName of extensions) {
47
64
  // eslint-disable-next-line no-await-in-loop
48
65
  await $`${vsCodeCommand} --install-extension ${extensionName}`;
49
66
  }
50
67
  }
51
68
 
52
- function getExtensionsFromJSON(projectPath: string): readonly string[] {
69
+ async function getExtensionsFromJSON(
70
+ projectPath: string,
71
+ ): Promise<readonly string[]> {
53
72
  const extensionsJSONPath = path.join(
54
73
  projectPath,
55
74
  ".vscode",
@@ -60,7 +79,7 @@ function getExtensionsFromJSON(projectPath: string): readonly string[] {
60
79
  return [];
61
80
  }
62
81
 
63
- const extensionsJSON = getJSONC(extensionsJSONPath);
82
+ const extensionsJSON = await getJSONC(extensionsJSONPath);
64
83
 
65
84
  const { recommendations } = extensionsJSON;
66
85
  if (!Array.isArray(recommendations)) {
package/src/git.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { commandExists, isFile, readFile } from "complete-node";
3
- import { $ } from "execa";
2
+ import { $, commandExists, isFileAsync, readFileAsync } from "complete-node";
4
3
  import path from "node:path";
5
4
  import yaml from "yaml";
6
5
  import { HOME_DIR, PROJECT_NAME, PROJECT_VERSION } from "./constants.js";
@@ -11,8 +10,9 @@ import { getInputString, getInputYesNo, promptLog } from "./prompt.js";
11
10
  * If the GitHub CLI is installed, we can derive the user's GitHub username from their YAML
12
11
  * configuration.
13
12
  */
14
- export function getGitHubUsername(): string | undefined {
15
- if (!commandExists("gh")) {
13
+ export async function getGitHubUsername(): Promise<string | undefined> {
14
+ const ghExists = await commandExists("gh");
15
+ if (!ghExists) {
16
16
  return undefined;
17
17
  }
18
18
 
@@ -21,11 +21,12 @@ export function getGitHubUsername(): string | undefined {
21
21
  return undefined;
22
22
  }
23
23
 
24
- if (!isFile(githubCLIHostsPath)) {
24
+ const hostsPathExists = await isFileAsync(githubCLIHostsPath);
25
+ if (!hostsPathExists) {
25
26
  return undefined;
26
27
  }
27
28
 
28
- const configYAMLRaw = readFile(githubCLIHostsPath);
29
+ const configYAMLRaw = await readFileAsync(githubCLIHostsPath);
29
30
  const configYAML = yaml.parse(configYAMLRaw) as GitHubCLIHostsYAML;
30
31
 
31
32
  const githubCom = configYAML["github.com"];
@@ -71,14 +72,15 @@ export async function promptGitHubRepoOrGitRemoteURL(
71
72
  }
72
73
 
73
74
  // We do not need to prompt the user if they do not have Git installed.
74
- if (!commandExists("git")) {
75
+ const gitExists = await commandExists("git");
76
+ if (!gitExists) {
75
77
  promptLog(
76
78
  'Git does not seem to be installed. (The "git" command is not in the path.) Skipping Git-related things.',
77
79
  );
78
80
  return undefined;
79
81
  }
80
82
 
81
- const gitHubUsername = getGitHubUsername();
83
+ const gitHubUsername = await getGitHubUsername();
82
84
  if (gitHubUsername !== undefined) {
83
85
  const { exitCode } = await $`gh repo view ${projectName}`;
84
86
  const gitHubRepoExists = exitCode === 0;
@@ -153,7 +155,8 @@ export async function initGitRepository(
153
155
  projectPath: string,
154
156
  gitRemoteURL: string | undefined,
155
157
  ): Promise<void> {
156
- if (!commandExists("git")) {
158
+ const gitExists = await commandExists("git");
159
+ if (!gitExists) {
157
160
  return;
158
161
  }
159
162
 
package/src/main.ts CHANGED
@@ -7,13 +7,10 @@ import { NukeCommand } from "./commands/NukeCommand.js";
7
7
  import { PublishCommand } from "./commands/PublishCommand.js";
8
8
  import { UpdateCommand } from "./commands/UpdateCommand.js";
9
9
  import { PROJECT_NAME, PROJECT_VERSION } from "./constants.js";
10
- import { validateNodeVersion } from "./validateNoteVersion.js";
11
10
 
12
11
  await main();
13
12
 
14
13
  async function main(): Promise<void> {
15
- validateNodeVersion();
16
-
17
14
  const [_node, _app, ...args] = process.argv;
18
15
 
19
16
  const cli = new Cli({
@@ -1,142 +0,0 @@
1
- import chalk from "chalk";
2
- import { Command, Option } from "clipanion";
3
- import { ReadonlySet } from "complete-common";
4
- import { deleteFileOrDirectory, fatalError, isDirectory, isFile, readFile, writeFile, } from "complete-node";
5
- import { $ } from "execa";
6
- import klawSync from "klaw-sync";
7
- import path from "node:path";
8
- import { ACTION_YML, ACTION_YML_TEMPLATE_PATH, CWD, TEMPLATES_DYNAMIC_DIR, TEMPLATES_STATIC_DIR, } from "../constants.js";
9
- import { getTruncatedText } from "./check/getTruncatedText.js";
10
- const URL_PREFIX = "https://raw.githubusercontent.com/complete-ts/complete/main/packages/complete-cli/file-templates";
11
- export class CheckCommand extends Command {
12
- static paths = [["check"], ["c"]];
13
- ignore = Option.String("-i,--ignore", {
14
- description: "Comma separated list of file names to ignore.",
15
- });
16
- verbose = Option.Boolean("-v,--verbose", false, {
17
- description: "Enable verbose output.",
18
- });
19
- static usage = Command.Usage({
20
- description: "Check the template files of the current TypeScript project to see if they are up to date.",
21
- });
22
- // eslint-disable-next-line @typescript-eslint/require-await
23
- async execute() {
24
- let oneOrMoreErrors = false;
25
- const ignore = this.ignore ?? "";
26
- const ignoreFileNames = ignore.split(",");
27
- const ignoreFileNamesSet = new ReadonlySet(ignoreFileNames);
28
- // First, check the static files.
29
- if (checkTemplateDirectory(TEMPLATES_STATIC_DIR, ignoreFileNamesSet, this.verbose)) {
30
- oneOrMoreErrors = true;
31
- }
32
- // Second, check dynamic files that require specific logic.
33
- if (checkIndividualFiles(ignoreFileNamesSet, this.verbose)) {
34
- oneOrMoreErrors = true;
35
- }
36
- if (oneOrMoreErrors) {
37
- fatalError("The check command failed.");
38
- }
39
- }
40
- }
41
- function checkTemplateDirectory(templateDirectory, ignoreFileNamesSet, verbose) {
42
- let oneOrMoreErrors = false;
43
- for (const klawItem of klawSync(templateDirectory)) {
44
- const templateFilePath = klawItem.path;
45
- if (isDirectory(templateFilePath)) {
46
- continue;
47
- }
48
- const originalFileName = path.basename(templateFilePath);
49
- if (originalFileName === "main.ts") {
50
- continue;
51
- }
52
- const relativeTemplateFilePath = path.relative(templateDirectory, templateFilePath);
53
- const templateFileName = path.basename(relativeTemplateFilePath);
54
- let projectFilePath = path.join(CWD, relativeTemplateFilePath);
55
- switch (templateFileName) {
56
- case "_cspell.config.jsonc": {
57
- projectFilePath = path.join(projectFilePath, "..", "cspell.config.jsonc");
58
- break;
59
- }
60
- case "_gitattributes": {
61
- projectFilePath = path.join(projectFilePath, "..", ".gitattributes");
62
- break;
63
- }
64
- default: {
65
- break;
66
- }
67
- }
68
- const projectFileName = path.basename(projectFilePath);
69
- if (ignoreFileNamesSet.has(projectFileName)) {
70
- continue;
71
- }
72
- if (!compareTextFiles(projectFilePath, templateFilePath, verbose)) {
73
- oneOrMoreErrors = true;
74
- }
75
- }
76
- return oneOrMoreErrors;
77
- }
78
- function checkIndividualFiles(ignoreFileNamesSet, verbose) {
79
- let oneOrMoreErrors = false;
80
- if (!ignoreFileNamesSet.has(ACTION_YML)) {
81
- const templateFilePath = ACTION_YML_TEMPLATE_PATH;
82
- const relativeTemplateFilePath = path.relative(TEMPLATES_DYNAMIC_DIR, templateFilePath);
83
- const projectFilePath = path.join(CWD, relativeTemplateFilePath);
84
- if (!compareTextFiles(projectFilePath, templateFilePath, verbose)) {
85
- oneOrMoreErrors = true;
86
- }
87
- }
88
- return oneOrMoreErrors;
89
- }
90
- /** @returns Whether the project file is valid in reference to the template file. */
91
- function compareTextFiles(projectFilePath, templateFilePath, verbose) {
92
- if (!isFile(projectFilePath)) {
93
- console.log(`Failed to find the following file: ${projectFilePath}`);
94
- printTemplateLocation(templateFilePath);
95
- return false;
96
- }
97
- const projectFileObject = getTruncatedFileText(projectFilePath, new Set(), new Set());
98
- const templateFileObject = getTruncatedFileText(templateFilePath, projectFileObject.ignoreLines, projectFileObject.linesBeforeIgnore);
99
- if (projectFileObject.text === templateFileObject.text) {
100
- return true;
101
- }
102
- console.log(`The contents of the following file do not match: ${chalk.red(projectFilePath)}`);
103
- printTemplateLocation(templateFilePath);
104
- if (verbose) {
105
- const originalTemplateFile = readFile(templateFilePath);
106
- const originalProjectFile = readFile(projectFilePath);
107
- console.log("--- Original template file: ---\n");
108
- console.log(originalTemplateFile);
109
- console.log();
110
- console.log("--- Original project file: ---\n");
111
- console.log(originalProjectFile);
112
- console.log();
113
- console.log("--- Parsed template file: ---\n");
114
- console.log(templateFileObject.text);
115
- console.log();
116
- console.log("--- Parsed project file: ---\n");
117
- console.log(projectFileObject.text);
118
- console.log();
119
- }
120
- const tempProjectFilePath = path.join(CWD, "tempProjectFile.txt");
121
- const tempTemplateFilePath = path.join(CWD, "tempTemplateFile.txt");
122
- writeFile(tempProjectFilePath, projectFileObject.text);
123
- writeFile(tempTemplateFilePath, templateFileObject.text);
124
- $.sync `diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
125
- deleteFileOrDirectory(tempProjectFilePath);
126
- deleteFileOrDirectory(tempTemplateFilePath);
127
- return false;
128
- }
129
- function getTruncatedFileText(filePath, ignoreLines, linesBeforeIgnore) {
130
- const fileName = path.basename(filePath);
131
- const fileContents = readFile(filePath);
132
- return getTruncatedText(fileName, fileContents, ignoreLines, linesBeforeIgnore);
133
- }
134
- function printTemplateLocation(templateFilePath) {
135
- const unixPath = templateFilePath.split(path.sep).join(path.posix.sep);
136
- const match = unixPath.match(/.+\/file-templates\/(?<urlSuffix>.+)/);
137
- if (match === null || match.groups === undefined) {
138
- fatalError(`Failed to parse the template file path: ${templateFilePath}`);
139
- }
140
- const { urlSuffix } = match.groups;
141
- console.log(`You can find the template at: ${chalk.green(`${URL_PREFIX}/${urlSuffix}`)}\n`);
142
- }