complete-cli 1.3.2 → 1.3.4

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 (55) hide show
  1. package/dist/commands/CheckCommand.js +16 -11
  2. package/dist/commands/CheckCommand.js.map +1 -0
  3. package/dist/commands/InitCommand.js +1 -0
  4. package/dist/commands/InitCommand.js.map +1 -0
  5. package/dist/commands/MetadataCommand.js +5 -4
  6. package/dist/commands/MetadataCommand.js.map +1 -0
  7. package/dist/commands/NukeCommand.js +1 -0
  8. package/dist/commands/NukeCommand.js.map +1 -0
  9. package/dist/commands/PublishCommand.js +6 -5
  10. package/dist/commands/PublishCommand.js.map +1 -0
  11. package/dist/commands/UpdateCommand.js +1 -0
  12. package/dist/commands/UpdateCommand.js.map +1 -0
  13. package/dist/commands/check/check.test.js +1 -0
  14. package/dist/commands/check/check.test.js.map +1 -0
  15. package/dist/commands/check/getTruncatedText.js +1 -0
  16. package/dist/commands/check/getTruncatedText.js.map +1 -0
  17. package/dist/commands/init/checkIfProjectPathExists.js +12 -5
  18. package/dist/commands/init/checkIfProjectPathExists.js.map +1 -0
  19. package/dist/commands/init/createProject.js +53 -32
  20. package/dist/commands/init/createProject.js.map +1 -0
  21. package/dist/commands/init/getAuthorName.js +1 -0
  22. package/dist/commands/init/getAuthorName.js.map +1 -0
  23. package/dist/commands/init/getProjectPath.js +1 -0
  24. package/dist/commands/init/getProjectPath.js.map +1 -0
  25. package/dist/commands/init/lockedDependencies.js +1 -0
  26. package/dist/commands/init/lockedDependencies.js.map +1 -0
  27. package/dist/commands/init/packageManager.js +16 -3
  28. package/dist/commands/init/packageManager.js.map +1 -0
  29. package/dist/commands/init/vsCodeInit.js +7 -1
  30. package/dist/commands/init/vsCodeInit.js.map +1 -0
  31. package/dist/constants.js +1 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/git.js +4 -3
  34. package/dist/git.js.map +1 -0
  35. package/dist/interfaces/GitHubCLIHostsYAML.js +1 -0
  36. package/dist/interfaces/GitHubCLIHostsYAML.js.map +1 -0
  37. package/dist/main.js +1 -0
  38. package/dist/main.js.map +1 -0
  39. package/dist/prompt.js +2 -1
  40. package/dist/prompt.js.map +1 -0
  41. package/file-templates/dynamic/.github/workflows/setup/action.yml +1 -1
  42. package/file-templates/dynamic/Node.gitignore +3 -1
  43. package/file-templates/dynamic/package.json +2 -6
  44. package/file-templates/static/eslint.config.mjs +2 -2
  45. package/file-templates/static/scripts/lint.ts +1 -1
  46. package/package.json +9 -12
  47. package/src/commands/CheckCommand.ts +18 -14
  48. package/src/commands/MetadataCommand.ts +4 -9
  49. package/src/commands/PublishCommand.ts +6 -6
  50. package/src/commands/init/checkIfProjectPathExists.ts +13 -9
  51. package/src/commands/init/createProject.ts +69 -36
  52. package/src/commands/init/packageManager.ts +22 -3
  53. package/src/commands/init/vsCodeInit.ts +9 -1
  54. package/src/git.ts +3 -3
  55. package/src/prompt.ts +1 -1
@@ -12,10 +12,9 @@ import {
12
12
  makeDirectory,
13
13
  PackageManager,
14
14
  readFile,
15
- renameFile,
15
+ renameFileOrDirectory,
16
16
  updatePackageJSONDependencies,
17
17
  writeFile,
18
- writeFileAsync,
19
18
  } from "complete-node";
20
19
  import path from "node:path";
21
20
  import {
@@ -38,12 +37,12 @@ export async function createProject(
38
37
  packageManager: PackageManager,
39
38
  ): Promise<void> {
40
39
  if (createNewDir) {
41
- makeDirectory(projectPath);
40
+ await makeDirectory(projectPath);
42
41
  }
43
42
 
44
- copyStaticFiles(projectPath);
45
- copyDynamicFiles(projectName, authorName, projectPath, packageManager);
46
- copyPackageManagerSpecificFiles(projectPath, packageManager);
43
+ await copyStaticFiles(projectPath);
44
+ await copyDynamicFiles(projectName, authorName, projectPath, packageManager);
45
+ await copyPackageManagerSpecificFiles(projectPath, packageManager);
47
46
 
48
47
  // There is no package manager lock files yet, so we have to pass "false" to this function.
49
48
  const updated = await updatePackageJSONDependencies(projectPath, false, true);
@@ -69,38 +68,44 @@ export async function createProject(
69
68
  }
70
69
 
71
70
  /** Copy static files, like "eslint.config.mjs", "tsconfig.json", etc. */
72
- function copyStaticFiles(projectPath: string) {
73
- copyTemplateDirectoryWithoutOverwriting(TEMPLATES_STATIC_DIR, projectPath);
71
+ async function copyStaticFiles(projectPath: string) {
72
+ await copyTemplateDirectoryWithoutOverwriting(
73
+ TEMPLATES_STATIC_DIR,
74
+ projectPath,
75
+ );
74
76
 
75
77
  // Rename "_gitattributes" to ".gitattributes". (If it is kept as ".gitattributes", then it won't
76
78
  // be committed to git.)
77
79
  const gitAttributesPath = path.join(projectPath, "_gitattributes");
78
80
  const correctGitAttributesPath = path.join(projectPath, ".gitattributes");
79
- renameFile(gitAttributesPath, correctGitAttributesPath);
81
+ await renameFileOrDirectory(gitAttributesPath, correctGitAttributesPath);
80
82
 
81
83
  // Rename "_cspell.config.jsonc" to "cspell.config.jsonc". (If it is kept as
82
84
  // "cspell.config.jsonc", then local spell checking will fail.)
83
85
  const cSpellConfigPath = path.join(projectPath, "_cspell.config.jsonc");
84
86
  const correctCSpellConfigPath = path.join(projectPath, "cspell.config.jsonc");
85
- renameFile(cSpellConfigPath, correctCSpellConfigPath);
87
+ await renameFileOrDirectory(cSpellConfigPath, correctCSpellConfigPath);
86
88
  }
87
89
 
88
- function copyTemplateDirectoryWithoutOverwriting(
90
+ async function copyTemplateDirectoryWithoutOverwriting(
89
91
  templateDirPath: string,
90
92
  projectPath: string,
91
93
  ) {
92
- const fileNames = getFileNamesInDirectory(templateDirPath);
93
- for (const fileName of fileNames) {
94
- const templateFilePath = path.join(templateDirPath, fileName);
95
- const destinationFilePath = path.join(projectPath, fileName);
96
- if (!isFile(destinationFilePath)) {
97
- copyFileOrDirectory(templateFilePath, destinationFilePath);
98
- }
99
- }
94
+ const fileNames = await getFileNamesInDirectory(templateDirPath);
95
+ await Promise.all(
96
+ fileNames.map(async (fileName) => {
97
+ const templateFilePath = path.join(templateDirPath, fileName);
98
+ const destinationFilePath = path.join(projectPath, fileName);
99
+ const file = await isFile(destinationFilePath);
100
+ if (!file) {
101
+ await copyFileOrDirectory(templateFilePath, destinationFilePath);
102
+ }
103
+ }),
104
+ );
100
105
  }
101
106
 
102
107
  /** Copy files that need to have text replaced inside of them. */
103
- function copyDynamicFiles(
108
+ async function copyDynamicFiles(
104
109
  projectName: string,
105
110
  authorName: string | undefined,
106
111
  projectPath: string,
@@ -110,7 +115,7 @@ function copyDynamicFiles(
110
115
  {
111
116
  const fileName = ACTION_YML;
112
117
  const templatePath = ACTION_YML_TEMPLATE_PATH;
113
- const template = readFile(templatePath);
118
+ const template = await readFile(templatePath);
114
119
 
115
120
  const installCommand = getPackageManagerInstallCICommand(packageManager);
116
121
  const actionYML = template
@@ -118,9 +123,9 @@ function copyDynamicFiles(
118
123
  .replaceAll("PACKAGE_MANAGER_INSTALL_COMMAND", installCommand);
119
124
 
120
125
  const setupPath = path.join(projectPath, ".github", "workflows", "setup");
121
- makeDirectory(setupPath);
126
+ await makeDirectory(setupPath);
122
127
  const destinationPath = path.join(setupPath, fileName);
123
- writeFile(destinationPath, actionYML);
128
+ await writeFile(destinationPath, actionYML);
124
129
  }
125
130
 
126
131
  // `.gitignore`
@@ -129,7 +134,7 @@ function copyDynamicFiles(
129
134
  TEMPLATES_DYNAMIC_DIR,
130
135
  "_gitignore", // Not named ".gitignore" to prevent npm from deleting it.
131
136
  );
132
- const template = readFile(templatePath);
137
+ const template = await readFile(templatePath);
133
138
 
134
139
  // Prepend a header with the project name.
135
140
  let separatorLine = "# ";
@@ -142,33 +147,33 @@ function copyDynamicFiles(
142
147
  TEMPLATES_DYNAMIC_DIR,
143
148
  "Node.gitignore",
144
149
  );
145
- const nodeGitIgnore = readFile(nodeGitIgnorePath);
150
+ const nodeGitIgnore = await readFile(nodeGitIgnorePath);
146
151
 
147
152
  // eslint-disable-next-line prefer-template
148
153
  const gitignore = gitIgnoreHeader + template + "\n" + nodeGitIgnore;
149
154
 
150
155
  // We need to replace the underscore with a period.
151
156
  const destinationPath = path.join(projectPath, ".gitignore");
152
- writeFile(destinationPath, gitignore);
157
+ await writeFile(destinationPath, gitignore);
153
158
  }
154
159
 
155
160
  // `package.json`
156
161
  {
157
162
  const templatePath = path.join(TEMPLATES_DYNAMIC_DIR, "package.json");
158
- const template = readFile(templatePath);
163
+ const template = await readFile(templatePath);
159
164
 
160
165
  const packageJSON = template
161
166
  .replaceAll("project-name", projectName)
162
167
  .replaceAll("author-name", authorName ?? "unknown");
163
168
 
164
169
  const destinationPath = path.join(projectPath, "package.json");
165
- writeFile(destinationPath, packageJSON);
170
+ await writeFile(destinationPath, packageJSON);
166
171
  }
167
172
 
168
173
  // `README.md`
169
174
  {
170
175
  const templatePath = path.join(TEMPLATES_DYNAMIC_DIR, "README.md");
171
- const template = readFile(templatePath);
176
+ const template = await readFile(templatePath);
172
177
 
173
178
  // "PROJECT-NAME" must be hyphenated, as using an underscore will break Prettier for some
174
179
  // reason.
@@ -177,11 +182,11 @@ function copyDynamicFiles(
177
182
  .replaceAll("PROJECT-NAME", projectName)
178
183
  .replaceAll("PACKAGE-MANAGER-INSTALL-COMMAND", command);
179
184
  const destinationPath = path.join(projectPath, "README.md");
180
- writeFile(destinationPath, readmeMD);
185
+ await writeFile(destinationPath, readmeMD);
181
186
  }
182
187
  }
183
188
 
184
- function copyPackageManagerSpecificFiles(
189
+ async function copyPackageManagerSpecificFiles(
185
190
  projectPath: string,
186
191
  packageManager: PackageManager,
187
192
  ) {
@@ -189,7 +194,7 @@ function copyPackageManagerSpecificFiles(
189
194
  case PackageManager.npm: {
190
195
  const npmrc = "save-exact=true\n";
191
196
  const npmrcPath = path.join(projectPath, ".npmrc");
192
- writeFile(npmrcPath, npmrc);
197
+ await writeFile(npmrcPath, npmrc);
193
198
  break;
194
199
  }
195
200
 
@@ -198,7 +203,7 @@ function copyPackageManagerSpecificFiles(
198
203
  case PackageManager.pnpm: {
199
204
  const npmrc = "save-exact=true\nshamefully-hoist=true\n";
200
205
  const npmrcPath = path.join(projectPath, ".npmrc");
201
- writeFile(npmrcPath, npmrc);
206
+ await writeFile(npmrcPath, npmrc);
202
207
  break;
203
208
  }
204
209
 
@@ -209,7 +214,35 @@ function copyPackageManagerSpecificFiles(
209
214
  case PackageManager.bun: {
210
215
  const bunfig = "[install]\nexact = true\n";
211
216
  const bunfigPath = path.join(projectPath, "bunfig.toml");
212
- writeFile(bunfigPath, bunfig);
217
+ await writeFile(bunfigPath, bunfig);
218
+
219
+ // Additionally, we assume that if they are using the Bun package manager, they also want to
220
+ // use the Bun runtime. First, replace "complete-tsconfig/tsconfig.node.json" with
221
+ // "complete-tsconfig/tsconfig.bun.json".
222
+ const tsConfigJSONPath = path.join(projectPath, "tsconfig.json");
223
+ const tsConfigJSONScriptsPath = path.join(
224
+ projectPath,
225
+ "scripts",
226
+ "tsconfig.json",
227
+ );
228
+ const filePathsToReplaceNodeWithBun = [
229
+ tsConfigJSONPath,
230
+ tsConfigJSONScriptsPath,
231
+ ];
232
+ await Promise.all(
233
+ filePathsToReplaceNodeWithBun.map(async (filePath) => {
234
+ const fileContents = await readFile(filePath);
235
+ const newFileContents = fileContents.replaceAll("node", "bun");
236
+ await writeFile(filePath, newFileContents);
237
+ }),
238
+ );
239
+
240
+ // Second, replace "tsx" with "bun run".
241
+ const packageJSONPath = path.join(projectPath, "package.json");
242
+ const fileContents = await readFile(packageJSONPath);
243
+ const newFileContents = fileContents.replaceAll("tsx", "bun run");
244
+ await writeFile(packageJSONPath, newFileContents);
245
+
213
246
  break;
214
247
  }
215
248
  }
@@ -228,7 +261,7 @@ async function revertVersionsInPackageJSON(projectPath: string) {
228
261
  }
229
262
  const packageJSONText = JSON.stringify(packageJSON);
230
263
  await formatWithPrettier(packageJSONText, "json", projectPath);
231
- await writeFileAsync(packageJSONPath, packageJSONText);
264
+ await writeFile(packageJSONPath, packageJSONText);
232
265
  }
233
266
 
234
267
  async function createPackageMetadataJSON(projectPath: string) {
@@ -244,7 +277,7 @@ async function createPackageMetadataJSON(projectPath: string) {
244
277
  const packageMetadataText = JSON.stringify(packageMetadata);
245
278
  await formatWithPrettier(packageMetadataText, "json", projectPath);
246
279
  const packageMetadataPath = path.join(projectPath, "package-metadata.json");
247
- await writeFileAsync(packageMetadataPath, packageMetadataText);
280
+ await writeFile(packageMetadataPath, packageMetadataText);
248
281
  }
249
282
 
250
283
  async function installNodeModules(
@@ -1,7 +1,12 @@
1
1
  import chalk from "chalk";
2
2
  import type { ReadonlyRecord } from "complete-common";
3
- import { getEnumValues } from "complete-common";
4
- import { commandExists, PackageManager } from "complete-node";
3
+ import { assertDefined, getEnumValues } from "complete-common";
4
+ import {
5
+ commandExists,
6
+ getJavaScriptRuntime,
7
+ JavaScriptRuntime,
8
+ PackageManager,
9
+ } from "complete-node";
5
10
  import { DEFAULT_PACKAGE_MANAGER } from "../../constants.js";
6
11
  import { promptError } from "../../prompt.js";
7
12
 
@@ -10,8 +15,21 @@ const PACKAGE_MANAGERS = getEnumValues(PackageManager);
10
15
  export async function getPackageManagerUsedForNewProject(
11
16
  options: ReadonlyRecord<PackageManager, boolean>,
12
17
  ): Promise<PackageManager> {
18
+ // If the package manager was explicitly specified in the options, use that.
13
19
  const packageManagerFromOptions = await getPackageManagerFromOptions(options);
14
- return packageManagerFromOptions ?? DEFAULT_PACKAGE_MANAGER;
20
+ if (packageManagerFromOptions !== undefined) {
21
+ return packageManagerFromOptions;
22
+ }
23
+
24
+ // If `bun` or `bunx` was used to launch this program, assume that they also want to use the Bun
25
+ // package manager.
26
+ const javaScriptRuntime = getJavaScriptRuntime();
27
+ assertDefined(javaScriptRuntime, "Failed to get the JavaScript runtime.");
28
+ if (javaScriptRuntime === JavaScriptRuntime.bun) {
29
+ return PackageManager.bun;
30
+ }
31
+
32
+ return DEFAULT_PACKAGE_MANAGER;
15
33
  }
16
34
 
17
35
  async function getPackageManagerFromOptions(
@@ -19,6 +37,7 @@ async function getPackageManagerFromOptions(
19
37
  ) {
20
38
  for (const packageManager of PACKAGE_MANAGERS) {
21
39
  if (options[packageManager]) {
40
+ // Only one package manager flag will be specified at a time.
22
41
  // eslint-disable-next-line no-await-in-loop
23
42
  const exists = await commandExists(packageManager);
24
43
  if (!exists) {
@@ -1,3 +1,4 @@
1
+ import { assertObject } from "complete-common";
1
2
  import { $, $q, commandExists, getJSONC, isFile } from "complete-node";
2
3
  import path from "node:path";
3
4
  import { getInputYesNo, promptError, promptLog } from "../../prompt.js";
@@ -28,6 +29,8 @@ export async function vsCodeInit(
28
29
 
29
30
  async function getVSCodeCommand(): Promise<string | undefined> {
30
31
  for (const command of VS_CODE_COMMANDS) {
32
+ // We want to only check for one command at a time, since it is unlikely that the special VSCode
33
+ // commands will exist.
31
34
  // eslint-disable-next-line no-await-in-loop
32
35
  const exists = await commandExists(command);
33
36
  if (exists) {
@@ -67,11 +70,16 @@ async function getExtensionsFromJSON(
67
70
  "extensions.json",
68
71
  );
69
72
 
70
- if (!isFile(extensionsJSONPath)) {
73
+ const extensionsJSONExists = await isFile(extensionsJSONPath);
74
+ if (!extensionsJSONExists) {
71
75
  return [];
72
76
  }
73
77
 
74
78
  const extensionsJSON = await getJSONC(extensionsJSONPath);
79
+ assertObject(
80
+ extensionsJSON,
81
+ `The "${extensionsJSONPath}" file is not an object.`,
82
+ );
75
83
 
76
84
  const { recommendations } = extensionsJSON;
77
85
  if (!Array.isArray(recommendations)) {
package/src/git.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { $q, commandExists, isFileAsync, readFileAsync } from "complete-node";
2
+ import { $q, commandExists, isFile, readFile } from "complete-node";
3
3
  import path from "node:path";
4
4
  import yaml from "yaml";
5
5
  import { HOME_DIR, PROJECT_NAME, PROJECT_VERSION } from "./constants.js";
@@ -21,12 +21,12 @@ export async function getGitHubUsername(): Promise<string | undefined> {
21
21
  return undefined;
22
22
  }
23
23
 
24
- const hostsPathExists = await isFileAsync(githubCLIHostsPath);
24
+ const hostsPathExists = await isFile(githubCLIHostsPath);
25
25
  if (!hostsPathExists) {
26
26
  return undefined;
27
27
  }
28
28
 
29
- const configYAMLRaw = await readFileAsync(githubCLIHostsPath);
29
+ const configYAMLRaw = await readFile(githubCLIHostsPath);
30
30
  const configYAML = yaml.parse(configYAMLRaw) as GitHubCLIHostsYAML;
31
31
 
32
32
  const githubCom = configYAML["github.com"];
package/src/prompt.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  outro,
11
11
  spinner,
12
12
  text,
13
- } from "@zamiell/clack-prompts";
13
+ } from "@clack/prompts";
14
14
  import chalk from "chalk";
15
15
  import { PROJECT_NAME } from "./constants.js";
16
16