complete-cli 1.0.1-dev.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/LICENSE +9 -0
- package/README.md +7 -0
- package/dist/commands/CheckCommand.js +141 -0
- package/dist/commands/InitCommand.js +64 -0
- package/dist/commands/NukeCommand.js +13 -0
- package/dist/commands/PublishCommand.js +158 -0
- package/dist/commands/UpdateCommand.js +16 -0
- package/dist/commands/check/check.test.js +86 -0
- package/dist/commands/check/getTruncatedText.js +139 -0
- package/dist/commands/init/checkIfProjectPathExists.js +21 -0
- package/dist/commands/init/createProject.js +123 -0
- package/dist/commands/init/getAuthorName.js +17 -0
- package/dist/commands/init/getProjectPath.js +80 -0
- package/dist/commands/init/packageManager.js +35 -0
- package/dist/commands/init/vsCodeInit.js +74 -0
- package/dist/constants.js +17 -0
- package/dist/git.js +128 -0
- package/dist/interfaces/GitHubCLIHostsYAML.js +1 -0
- package/dist/main.js +26 -0
- package/dist/prompt.js +46 -0
- package/dist/validateNoteVersion.js +25 -0
- package/file-templates/dynamic/.github/workflows/setup/action.yml +13 -0
- package/file-templates/dynamic/Node.gitignore +130 -0
- package/file-templates/dynamic/README.md +3 -0
- package/file-templates/dynamic/_gitignore +9 -0
- package/file-templates/dynamic/package.json +37 -0
- package/file-templates/static/.github/workflows/ci.yml +49 -0
- package/file-templates/static/.prettierignore +12 -0
- package/file-templates/static/.vscode/extensions.json +9 -0
- package/file-templates/static/.vscode/settings.json +75 -0
- package/file-templates/static/LICENSE +674 -0
- package/file-templates/static/_cspell.config.jsonc +25 -0
- package/file-templates/static/_gitattributes +37 -0
- package/file-templates/static/eslint.config.mjs +18 -0
- package/file-templates/static/knip.config.js +20 -0
- package/file-templates/static/prettier.config.mjs +24 -0
- package/file-templates/static/scripts/build.ts +5 -0
- package/file-templates/static/scripts/lint.ts +30 -0
- package/file-templates/static/scripts/tsconfig.json +14 -0
- package/file-templates/static/src/main.ts +5 -0
- package/file-templates/static/tsconfig.json +12 -0
- package/package.json +59 -0
- package/src/commands/CheckCommand.ts +249 -0
- package/src/commands/InitCommand.ts +105 -0
- package/src/commands/NukeCommand.ts +17 -0
- package/src/commands/PublishCommand.ts +242 -0
- package/src/commands/UpdateCommand.ts +20 -0
- package/src/commands/check/check.test.ts +123 -0
- package/src/commands/check/getTruncatedText.ts +187 -0
- package/src/commands/init/checkIfProjectPathExists.ts +36 -0
- package/src/commands/init/createProject.ts +197 -0
- package/src/commands/init/getAuthorName.ts +23 -0
- package/src/commands/init/getProjectPath.ts +112 -0
- package/src/commands/init/packageManager.ts +64 -0
- package/src/commands/init/vsCodeInit.ts +115 -0
- package/src/constants.ts +39 -0
- package/src/git.ts +182 -0
- package/src/interfaces/GitHubCLIHostsYAML.ts +7 -0
- package/src/main.ts +34 -0
- package/src/prompt.ts +72 -0
- package/src/validateNoteVersion.ts +39 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
|
|
3
|
+
"version": "0.2",
|
|
4
|
+
"import": ["@cspell/cspell-bundled-dicts"],
|
|
5
|
+
"files": ["**"],
|
|
6
|
+
"enableFiletypes": ["*"],
|
|
7
|
+
"enableGlobDot": true,
|
|
8
|
+
"useGitignore": true,
|
|
9
|
+
"ignorePaths": [
|
|
10
|
+
"*.min.js",
|
|
11
|
+
"*.mp3",
|
|
12
|
+
"*.pyc",
|
|
13
|
+
"*.svg",
|
|
14
|
+
".git/**",
|
|
15
|
+
".yarn/**",
|
|
16
|
+
"bun.lock",
|
|
17
|
+
"dist/**",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"node_modules/**",
|
|
20
|
+
"package-lock.json",
|
|
21
|
+
"pnpm-lock.yaml",
|
|
22
|
+
"yarn.lock",
|
|
23
|
+
],
|
|
24
|
+
"words": ["dbaeumer", "esbenp", "lockb", "sarisia", "tseslint", "Zamiell"],
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# https://git-scm.com/docs/git-config/#Documentation/git-config.txt-coreautocrlf
|
|
2
|
+
# Default value: input
|
|
3
|
+
# Explicitly setting it to false prevents Git from changing line endings at any point, which can
|
|
4
|
+
# prevent issues when Windows users collaborate with MacOS/Linus users.
|
|
5
|
+
core.autocrlf=false
|
|
6
|
+
|
|
7
|
+
# https://git-scm.com/docs/git-config/#Documentation/git-config.txt-coreignoreCase
|
|
8
|
+
# Default value: false (on Linux machines) or true (on Windows machines)
|
|
9
|
+
# Explicitly setting it to false prevents the issue where Windows users cannot pull down
|
|
10
|
+
# casing-related file renames.
|
|
11
|
+
core.ignoreCase=false
|
|
12
|
+
|
|
13
|
+
# https://git-scm.com/docs/git-config/#Documentation/git-config.txt-pullrebase
|
|
14
|
+
# Default value: false
|
|
15
|
+
# Setting this prevents spurious merge commits:
|
|
16
|
+
# https://www.endoflineblog.com/gitflow-considered-harmful
|
|
17
|
+
pull.rebase=true
|
|
18
|
+
|
|
19
|
+
# Convert all files to use "\n" line endings.
|
|
20
|
+
* text=auto eol=lf
|
|
21
|
+
|
|
22
|
+
# Specify the file type for some binary files to prevent Git from changing the line endings upon
|
|
23
|
+
# cloning the repository.
|
|
24
|
+
*.mp3 binary
|
|
25
|
+
*.png binary
|
|
26
|
+
*.wav binary
|
|
27
|
+
|
|
28
|
+
# Specify the file type for some files that GitHub will not automatically characterize properly.
|
|
29
|
+
# https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
|
|
30
|
+
.vscode/*.json linguist-language=JSON-with-Comments
|
|
31
|
+
tsconfig*.json linguist-language=JSON-with-Comments
|
|
32
|
+
|
|
33
|
+
# Suppress displaying changes on certain files to prevent cluttering commit diffs on GitHub.
|
|
34
|
+
package-lock.json linguist-generated=true
|
|
35
|
+
yarn.lock linguist-generated=true
|
|
36
|
+
pnpm-lock.yaml linguist-generated=true
|
|
37
|
+
bun.lockb linguist-generated=true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// This is the configuration file for ESLint, the TypeScript linter:
|
|
2
|
+
// https://eslint.org/docs/latest/use/configure/
|
|
3
|
+
|
|
4
|
+
// @ts-check
|
|
5
|
+
|
|
6
|
+
import { completeConfigBase } from "eslint-config-complete"; // eslint-disable-line import-x/no-extraneous-dependencies
|
|
7
|
+
import tseslint from "typescript-eslint"; // eslint-disable-line import-x/no-extraneous-dependencies
|
|
8
|
+
|
|
9
|
+
export default tseslint.config(
|
|
10
|
+
// https://github.com/complete-ts/complete/blob/main/packages/eslint-config-complete/src/base.js
|
|
11
|
+
...completeConfigBase,
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
rules: {
|
|
15
|
+
// Insert changed or disabled rules here, if necessary.
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// This is the configuration file for Knip:
|
|
2
|
+
// https://knip.dev/overview/configuration
|
|
3
|
+
|
|
4
|
+
// @ts-check
|
|
5
|
+
|
|
6
|
+
/** @type {import("knip").KnipConfig} */
|
|
7
|
+
const config = {
|
|
8
|
+
ignore: [
|
|
9
|
+
"eslint.config.mjs", // ESLint is provided by "complete-lint".
|
|
10
|
+
"prettier.config.mjs", // Prettier is provided by "complete-lint".
|
|
11
|
+
],
|
|
12
|
+
ignoreBinaries: [
|
|
13
|
+
"tsx", // This is provided by "complete-lint".
|
|
14
|
+
],
|
|
15
|
+
ignoreDependencies: [
|
|
16
|
+
"complete-lint", // This is a linting meta-package.
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default config;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// This is the configuration file for Prettier, the auto-formatter:
|
|
2
|
+
// https://prettier.io/docs/en/configuration.html
|
|
3
|
+
|
|
4
|
+
// @ts-check
|
|
5
|
+
|
|
6
|
+
/** @type {import("prettier").Config} */
|
|
7
|
+
const config = {
|
|
8
|
+
plugins: [
|
|
9
|
+
"prettier-plugin-organize-imports", // Prettier does not format imports by default.
|
|
10
|
+
"prettier-plugin-packagejson", // Prettier does not format "package.json" by default.
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
overrides: [
|
|
14
|
+
// Allow proper formatting of JSONC files that have JSON file extensions.
|
|
15
|
+
{
|
|
16
|
+
files: ["**/.vscode/*.json", "**/tsconfig.json", "**/tsconfig.*.json"],
|
|
17
|
+
options: {
|
|
18
|
+
parser: "jsonc",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default config;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { $, lintScript } from "complete-node";
|
|
2
|
+
|
|
3
|
+
await lintScript(async () => {
|
|
4
|
+
await Promise.all([
|
|
5
|
+
// Use TypeScript to type-check the code.
|
|
6
|
+
$`tsc --noEmit`,
|
|
7
|
+
$`tsc --noEmit --project ./scripts/tsconfig.json`,
|
|
8
|
+
|
|
9
|
+
// Use ESLint to lint the TypeScript code.
|
|
10
|
+
// - "--max-warnings 0" makes warnings fail, since we set all ESLint errors to warnings.
|
|
11
|
+
$`eslint --max-warnings 0 .`,
|
|
12
|
+
|
|
13
|
+
// Use Prettier to check formatting.
|
|
14
|
+
// - "--log-level=warn" makes it only output errors.
|
|
15
|
+
$`prettier --log-level=warn --check .`,
|
|
16
|
+
|
|
17
|
+
// Use Knip to check for unused files, exports, and dependencies.
|
|
18
|
+
$`knip --no-progress`,
|
|
19
|
+
|
|
20
|
+
// Use CSpell to spell check every file.
|
|
21
|
+
// - "--no-progress" and "--no-summary" make it only output errors.
|
|
22
|
+
$`cspell --no-progress --no-summary .`,
|
|
23
|
+
|
|
24
|
+
// Check for unused words in the CSpell configuration file.
|
|
25
|
+
$`cspell-check-unused-words`,
|
|
26
|
+
|
|
27
|
+
// Check for template updates.
|
|
28
|
+
$`complete-cli check`,
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// A TypeScript configuration file, used by project scripts.
|
|
2
|
+
{
|
|
3
|
+
"$schema": "https://raw.githubusercontent.com/complete-ts/complete/main/packages/complete-tsconfig/schemas/tsconfig-strict-schema.json",
|
|
4
|
+
|
|
5
|
+
"extends": [
|
|
6
|
+
// https://github.com/complete-ts/complete/blob/main/packages/complete-tsconfig/tsconfig.base.json
|
|
7
|
+
"complete-tsconfig/tsconfig.base.json",
|
|
8
|
+
|
|
9
|
+
// https://github.com/complete-ts/complete/blob/main/packages/complete-tsconfig/tsconfig.node.json
|
|
10
|
+
"complete-tsconfig/tsconfig.node.json",
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
"include": ["*.ts"],
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The configuration file for TypeScript.
|
|
2
|
+
{
|
|
3
|
+
"$schema": "https://raw.githubusercontent.com/complete-ts/complete/main/packages/complete-tsconfig/schemas/tsconfig-strict-schema.json",
|
|
4
|
+
|
|
5
|
+
"extends": [
|
|
6
|
+
// https://github.com/complete-ts/complete/blob/main/packages/complete-tsconfig/tsconfig.base.json
|
|
7
|
+
"complete-tsconfig/tsconfig.base.json",
|
|
8
|
+
|
|
9
|
+
// https://github.com/complete-ts/complete/blob/main/packages/complete-tsconfig/tsconfig.node.json
|
|
10
|
+
"complete-tsconfig/tsconfig.node.json",
|
|
11
|
+
],
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "complete-cli",
|
|
3
|
+
"version": "1.0.1-dev.0",
|
|
4
|
+
"description": "A command line tool for bootstrapping TypeScript projects.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"typescript"
|
|
7
|
+
],
|
|
8
|
+
"homepage": "https://complete-ts.github.io/",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/complete-ts/complete/issues"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/complete-ts/complete.git"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Zamiell",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"complete-cli": "./dist/main.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"file-templates",
|
|
25
|
+
"src",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"package.json",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsx ./scripts/build.ts",
|
|
32
|
+
"lint": "tsx ./scripts/lint.ts",
|
|
33
|
+
"start": "tsx ./src/main.ts",
|
|
34
|
+
"test": "glob \"./src/**/*.test.ts\" --cmd=\"node --import tsx --test --test-reporter spec\""
|
|
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.0.1",
|
|
41
|
+
"complete-node": "^1.7.4",
|
|
42
|
+
"klaw-sync": "^6.0.0",
|
|
43
|
+
"yaml": "^2.7.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/klaw-sync": "^6.0.5",
|
|
47
|
+
"@types/node": "^22.13.4",
|
|
48
|
+
"@types/prompt": "^1.1.9",
|
|
49
|
+
"complete-node": "^1.7.4",
|
|
50
|
+
"eslint-plugin-sort-exports": "^0.9.1",
|
|
51
|
+
"glob": "^11.0.1",
|
|
52
|
+
"typescript": "^5.7.3",
|
|
53
|
+
"typescript-eslint": "^8.24.1",
|
|
54
|
+
"unbuild": "^3.3.1"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">= 20.11.0"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command, Option } from "clipanion";
|
|
3
|
+
import { ReadonlySet } from "complete-common";
|
|
4
|
+
import {
|
|
5
|
+
$s,
|
|
6
|
+
deleteFileOrDirectory,
|
|
7
|
+
fatalError,
|
|
8
|
+
isDirectory,
|
|
9
|
+
isFile,
|
|
10
|
+
readFile,
|
|
11
|
+
writeFile,
|
|
12
|
+
} from "complete-node";
|
|
13
|
+
import klawSync from "klaw-sync";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
ACTION_YML,
|
|
17
|
+
ACTION_YML_TEMPLATE_PATH,
|
|
18
|
+
CWD,
|
|
19
|
+
TEMPLATES_DYNAMIC_DIR,
|
|
20
|
+
TEMPLATES_STATIC_DIR,
|
|
21
|
+
} from "../constants.js";
|
|
22
|
+
import { getTruncatedText } from "./check/getTruncatedText.js";
|
|
23
|
+
|
|
24
|
+
const URL_PREFIX =
|
|
25
|
+
"https://raw.githubusercontent.com/complete-ts/complete/main/packages/complete-cli/file-templates";
|
|
26
|
+
|
|
27
|
+
export class CheckCommand extends Command {
|
|
28
|
+
static override paths = [["check"], ["c"]];
|
|
29
|
+
|
|
30
|
+
ignore = Option.String("-i,--ignore", {
|
|
31
|
+
description: "Comma separated list of file names to ignore.",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
verbose = Option.Boolean("-v,--verbose", false, {
|
|
35
|
+
description: "Enable verbose output.",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
static override usage = Command.Usage({
|
|
39
|
+
description:
|
|
40
|
+
"Check the template files of the current TypeScript project to see if they are up to date.",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
44
|
+
async execute(): Promise<void> {
|
|
45
|
+
let oneOrMoreErrors = false;
|
|
46
|
+
const ignore = this.ignore ?? "";
|
|
47
|
+
const ignoreFileNames = ignore.split(",");
|
|
48
|
+
const ignoreFileNamesSet = new ReadonlySet(ignoreFileNames);
|
|
49
|
+
|
|
50
|
+
// First, check the static files.
|
|
51
|
+
if (
|
|
52
|
+
checkTemplateDirectory(
|
|
53
|
+
TEMPLATES_STATIC_DIR,
|
|
54
|
+
ignoreFileNamesSet,
|
|
55
|
+
this.verbose,
|
|
56
|
+
)
|
|
57
|
+
) {
|
|
58
|
+
oneOrMoreErrors = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Second, check dynamic files that require specific logic.
|
|
62
|
+
if (checkIndividualFiles(ignoreFileNamesSet, this.verbose)) {
|
|
63
|
+
oneOrMoreErrors = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (oneOrMoreErrors) {
|
|
67
|
+
fatalError("The check command failed.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function checkTemplateDirectory(
|
|
73
|
+
templateDirectory: string,
|
|
74
|
+
ignoreFileNamesSet: ReadonlySet<string>,
|
|
75
|
+
verbose: boolean,
|
|
76
|
+
): boolean {
|
|
77
|
+
let oneOrMoreErrors = false;
|
|
78
|
+
|
|
79
|
+
for (const klawItem of klawSync(templateDirectory)) {
|
|
80
|
+
const templateFilePath = klawItem.path;
|
|
81
|
+
|
|
82
|
+
if (isDirectory(templateFilePath)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const originalFileName = path.basename(templateFilePath);
|
|
87
|
+
if (originalFileName === "main.ts") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const relativeTemplateFilePath = path.relative(
|
|
92
|
+
templateDirectory,
|
|
93
|
+
templateFilePath,
|
|
94
|
+
);
|
|
95
|
+
const templateFileName = path.basename(relativeTemplateFilePath);
|
|
96
|
+
|
|
97
|
+
let projectFilePath = path.join(CWD, relativeTemplateFilePath);
|
|
98
|
+
switch (templateFileName) {
|
|
99
|
+
case "_cspell.config.jsonc": {
|
|
100
|
+
projectFilePath = path.join(
|
|
101
|
+
projectFilePath,
|
|
102
|
+
"..",
|
|
103
|
+
"cspell.config.jsonc",
|
|
104
|
+
);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
case "_gitattributes": {
|
|
109
|
+
projectFilePath = path.join(projectFilePath, "..", ".gitattributes");
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
default: {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const projectFileName = path.basename(projectFilePath);
|
|
119
|
+
if (ignoreFileNamesSet.has(projectFileName)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!compareTextFiles(projectFilePath, templateFilePath, verbose)) {
|
|
124
|
+
oneOrMoreErrors = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return oneOrMoreErrors;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function checkIndividualFiles(
|
|
132
|
+
ignoreFileNamesSet: ReadonlySet<string>,
|
|
133
|
+
verbose: boolean,
|
|
134
|
+
) {
|
|
135
|
+
let oneOrMoreErrors = false;
|
|
136
|
+
|
|
137
|
+
if (!ignoreFileNamesSet.has(ACTION_YML)) {
|
|
138
|
+
const templateFilePath = ACTION_YML_TEMPLATE_PATH;
|
|
139
|
+
const relativeTemplateFilePath = path.relative(
|
|
140
|
+
TEMPLATES_DYNAMIC_DIR,
|
|
141
|
+
templateFilePath,
|
|
142
|
+
);
|
|
143
|
+
const projectFilePath = path.join(CWD, relativeTemplateFilePath);
|
|
144
|
+
if (!compareTextFiles(projectFilePath, templateFilePath, verbose)) {
|
|
145
|
+
oneOrMoreErrors = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return oneOrMoreErrors;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @returns Whether the project file is valid in reference to the template file. */
|
|
153
|
+
function compareTextFiles(
|
|
154
|
+
projectFilePath: string,
|
|
155
|
+
templateFilePath: string,
|
|
156
|
+
verbose: boolean,
|
|
157
|
+
): boolean {
|
|
158
|
+
if (!isFile(projectFilePath)) {
|
|
159
|
+
console.log(`Failed to find the following file: ${projectFilePath}`);
|
|
160
|
+
printTemplateLocation(templateFilePath);
|
|
161
|
+
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const projectFileObject = getTruncatedFileText(
|
|
166
|
+
projectFilePath,
|
|
167
|
+
new Set(),
|
|
168
|
+
new Set(),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const templateFileObject = getTruncatedFileText(
|
|
172
|
+
templateFilePath,
|
|
173
|
+
projectFileObject.ignoreLines,
|
|
174
|
+
projectFileObject.linesBeforeIgnore,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (projectFileObject.text === templateFileObject.text) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(
|
|
182
|
+
`The contents of the following file do not match: ${chalk.red(
|
|
183
|
+
projectFilePath,
|
|
184
|
+
)}`,
|
|
185
|
+
);
|
|
186
|
+
printTemplateLocation(templateFilePath);
|
|
187
|
+
|
|
188
|
+
if (verbose) {
|
|
189
|
+
const originalTemplateFile = readFile(templateFilePath);
|
|
190
|
+
const originalProjectFile = readFile(projectFilePath);
|
|
191
|
+
|
|
192
|
+
console.log("--- Original template file: ---\n");
|
|
193
|
+
console.log(originalTemplateFile);
|
|
194
|
+
console.log();
|
|
195
|
+
console.log("--- Original project file: ---\n");
|
|
196
|
+
console.log(originalProjectFile);
|
|
197
|
+
console.log();
|
|
198
|
+
console.log("--- Parsed template file: ---\n");
|
|
199
|
+
console.log(templateFileObject.text);
|
|
200
|
+
console.log();
|
|
201
|
+
console.log("--- Parsed project file: ---\n");
|
|
202
|
+
console.log(projectFileObject.text);
|
|
203
|
+
console.log();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const tempProjectFilePath = path.join(CWD, "tempProjectFile.txt");
|
|
207
|
+
const tempTemplateFilePath = path.join(CWD, "tempTemplateFile.txt");
|
|
208
|
+
|
|
209
|
+
writeFile(tempProjectFilePath, projectFileObject.text);
|
|
210
|
+
writeFile(tempTemplateFilePath, templateFileObject.text);
|
|
211
|
+
|
|
212
|
+
$s`diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
|
|
213
|
+
|
|
214
|
+
deleteFileOrDirectory(tempProjectFilePath);
|
|
215
|
+
deleteFileOrDirectory(tempTemplateFilePath);
|
|
216
|
+
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getTruncatedFileText(
|
|
221
|
+
filePath: string,
|
|
222
|
+
ignoreLines: ReadonlySet<string>,
|
|
223
|
+
linesBeforeIgnore: ReadonlySet<string>,
|
|
224
|
+
) {
|
|
225
|
+
const fileName = path.basename(filePath);
|
|
226
|
+
const fileContents = readFile(filePath);
|
|
227
|
+
|
|
228
|
+
return getTruncatedText(
|
|
229
|
+
fileName,
|
|
230
|
+
fileContents,
|
|
231
|
+
ignoreLines,
|
|
232
|
+
linesBeforeIgnore,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function printTemplateLocation(templateFilePath: string) {
|
|
237
|
+
const unixPath = templateFilePath.split(path.sep).join(path.posix.sep);
|
|
238
|
+
const match = unixPath.match(/.+\/file-templates\/(?<urlSuffix>.+)/);
|
|
239
|
+
if (match === null || match.groups === undefined) {
|
|
240
|
+
fatalError(`Failed to parse the template file path: ${templateFilePath}`);
|
|
241
|
+
}
|
|
242
|
+
const { urlSuffix } = match.groups;
|
|
243
|
+
|
|
244
|
+
console.log(
|
|
245
|
+
`You can find the template at: ${chalk.green(
|
|
246
|
+
`${URL_PREFIX}/${urlSuffix}`,
|
|
247
|
+
)}\n`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Command, Option } from "clipanion";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { promptGitHubRepoOrGitRemoteURL } from "../git.js";
|
|
4
|
+
import { promptEnd, promptStart } from "../prompt.js";
|
|
5
|
+
import { checkIfProjectPathExists } from "./init/checkIfProjectPathExists.js";
|
|
6
|
+
import { createProject } from "./init/createProject.js";
|
|
7
|
+
import { getAuthorName } from "./init/getAuthorName.js";
|
|
8
|
+
import { getProjectPath } from "./init/getProjectPath.js";
|
|
9
|
+
import { getPackageManagerUsedForNewProject } from "./init/packageManager.js";
|
|
10
|
+
import { vsCodeInit } from "./init/vsCodeInit.js";
|
|
11
|
+
|
|
12
|
+
export class InitCommand extends Command {
|
|
13
|
+
static override paths = [["init"], ["i"]];
|
|
14
|
+
|
|
15
|
+
// The first positional argument.
|
|
16
|
+
name = Option.String({
|
|
17
|
+
required: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
yes = Option.Boolean("-y,--yes", false, {
|
|
21
|
+
description:
|
|
22
|
+
'Answer yes to every dialog option, similar to how "npm init --yes" works.',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
useCurrentDirectory = Option.Boolean("--use-current-directory", false, {
|
|
26
|
+
description: "Use the current directory as the root for the project.",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
customDirectory = Option.String("--custom-directory", {
|
|
30
|
+
description:
|
|
31
|
+
"Initialize the project into the specified directory (instead of creating a new one based on the name or using the current working directory).",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
vsCode = Option.Boolean("--vscode", false, {
|
|
35
|
+
description: "Open the project in VSCode after initialization.",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
npm = Option.Boolean("--npm", false, {
|
|
39
|
+
description: "Use npm as the package manager.",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
yarn = Option.Boolean("--yarn", false, {
|
|
43
|
+
description: "Use yarn as the package manager.",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
pnpm = Option.Boolean("--pnpm", false, {
|
|
47
|
+
description: "Use pnpm as the package manager.",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
skipGit = Option.Boolean("--skip-git", false, {
|
|
51
|
+
description: "Do not initialize Git.",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
skipInstall = Option.Boolean("--skip-install", false, {
|
|
55
|
+
description:
|
|
56
|
+
"Do not automatically install the dependencies after initializing the project.",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
forceName = Option.Boolean("-f,--force-name", false, {
|
|
60
|
+
description: "Allow project names that are normally illegal.",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
static override usage = Command.Usage({
|
|
64
|
+
description: "Initialize a new TypeScript project.",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
async execute(): Promise<void> {
|
|
68
|
+
promptStart();
|
|
69
|
+
|
|
70
|
+
const packageManager = getPackageManagerUsedForNewProject(this);
|
|
71
|
+
|
|
72
|
+
// Prompt the end-user for some information (and validate it as we go).
|
|
73
|
+
const { projectPath, createNewDir } = await getProjectPath(
|
|
74
|
+
this.name,
|
|
75
|
+
this.useCurrentDirectory,
|
|
76
|
+
this.customDirectory,
|
|
77
|
+
this.yes,
|
|
78
|
+
this.forceName,
|
|
79
|
+
);
|
|
80
|
+
await checkIfProjectPathExists(projectPath, this.yes);
|
|
81
|
+
|
|
82
|
+
const projectName = path.basename(projectPath);
|
|
83
|
+
const authorName = await getAuthorName();
|
|
84
|
+
const gitRemoteURL = await promptGitHubRepoOrGitRemoteURL(
|
|
85
|
+
projectName,
|
|
86
|
+
this.yes,
|
|
87
|
+
this.skipGit,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Now that we have asked the user all of the questions we need, we can create the project.
|
|
91
|
+
await createProject(
|
|
92
|
+
projectName,
|
|
93
|
+
authorName,
|
|
94
|
+
projectPath,
|
|
95
|
+
createNewDir,
|
|
96
|
+
gitRemoteURL,
|
|
97
|
+
this.skipInstall,
|
|
98
|
+
packageManager,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
await vsCodeInit(projectPath, this.vsCode, this.yes);
|
|
102
|
+
|
|
103
|
+
promptEnd("Initialization completed.");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "clipanion";
|
|
2
|
+
import { nukeDependencies } from "complete-node";
|
|
3
|
+
|
|
4
|
+
export class NukeCommand extends Command {
|
|
5
|
+
static override paths = [["nuke"], ["n"]];
|
|
6
|
+
|
|
7
|
+
static override usage = Command.Usage({
|
|
8
|
+
description:
|
|
9
|
+
'Delete the "node_modules" directory and the package lock file, then reinstall the dependencies for the project.',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
13
|
+
async execute(): Promise<void> {
|
|
14
|
+
nukeDependencies();
|
|
15
|
+
console.log("Successfully reinstalled dependencies from npm.");
|
|
16
|
+
}
|
|
17
|
+
}
|