complete-cli 1.0.7 → 1.0.9
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { Command, Option } from "clipanion";
|
|
3
3
|
import { ReadonlySet } from "complete-common";
|
|
4
|
-
import { $,
|
|
4
|
+
import { $, deleteFileOrDirectoryAsync, fatalError, isDirectory, isFileAsync, readFile, readFileAsync, writeFileAsync, } from "complete-node";
|
|
5
5
|
import klawSync from "klaw-sync";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { ACTION_YML, ACTION_YML_TEMPLATE_PATH, CWD, TEMPLATES_DYNAMIC_DIR, TEMPLATES_STATIC_DIR, } from "../constants.js";
|
|
@@ -18,18 +18,19 @@ export class CheckCommand extends Command {
|
|
|
18
18
|
static usage = Command.Usage({
|
|
19
19
|
description: "Check the template files of the current TypeScript project to see if they are up to date.",
|
|
20
20
|
});
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
22
21
|
async execute() {
|
|
23
22
|
let oneOrMoreErrors = false;
|
|
24
23
|
const ignore = this.ignore ?? "";
|
|
25
24
|
const ignoreFileNames = ignore.split(",");
|
|
26
25
|
const ignoreFileNamesSet = new ReadonlySet(ignoreFileNames);
|
|
27
26
|
// First, check the static files.
|
|
28
|
-
|
|
27
|
+
const staticTemplatesValid = await checkTemplateDirectory(TEMPLATES_STATIC_DIR, ignoreFileNamesSet, this.verbose);
|
|
28
|
+
if (!staticTemplatesValid) {
|
|
29
29
|
oneOrMoreErrors = true;
|
|
30
30
|
}
|
|
31
31
|
// Second, check dynamic files that require specific logic.
|
|
32
|
-
|
|
32
|
+
const dynamicFilesValid = await checkDynamicFiles(ignoreFileNamesSet, this.verbose);
|
|
33
|
+
if (!dynamicFilesValid) {
|
|
33
34
|
oneOrMoreErrors = true;
|
|
34
35
|
}
|
|
35
36
|
if (oneOrMoreErrors) {
|
|
@@ -37,7 +38,8 @@ export class CheckCommand extends Command {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
+
/** @returns Whether the directory was valid. */
|
|
42
|
+
async function checkTemplateDirectory(templateDirectory, ignoreFileNamesSet, verbose) {
|
|
41
43
|
let oneOrMoreErrors = false;
|
|
42
44
|
for (const klawItem of klawSync(templateDirectory)) {
|
|
43
45
|
const templateFilePath = klawItem.path;
|
|
@@ -68,33 +70,38 @@ function checkTemplateDirectory(templateDirectory, ignoreFileNamesSet, verbose)
|
|
|
68
70
|
if (ignoreFileNamesSet.has(projectFileName)) {
|
|
69
71
|
continue;
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
// eslint-disable-next-line no-await-in-loop
|
|
74
|
+
const fileValid = await compareTextFiles(projectFilePath, templateFilePath, verbose);
|
|
75
|
+
if (!fileValid) {
|
|
72
76
|
oneOrMoreErrors = true;
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
|
-
return oneOrMoreErrors;
|
|
79
|
+
return !oneOrMoreErrors;
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
/** @returns Whether the dynamic files were valid. */
|
|
82
|
+
async function checkDynamicFiles(ignoreFileNamesSet, verbose) {
|
|
78
83
|
let oneOrMoreErrors = false;
|
|
79
84
|
if (!ignoreFileNamesSet.has(ACTION_YML)) {
|
|
80
85
|
const templateFilePath = ACTION_YML_TEMPLATE_PATH;
|
|
81
86
|
const relativeTemplateFilePath = path.relative(TEMPLATES_DYNAMIC_DIR, templateFilePath);
|
|
82
87
|
const projectFilePath = path.join(CWD, relativeTemplateFilePath);
|
|
83
|
-
|
|
88
|
+
const fileValid = await compareTextFiles(projectFilePath, templateFilePath, verbose);
|
|
89
|
+
if (!fileValid) {
|
|
84
90
|
oneOrMoreErrors = true;
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
|
-
return oneOrMoreErrors;
|
|
93
|
+
return !oneOrMoreErrors;
|
|
88
94
|
}
|
|
89
95
|
/** @returns Whether the project file is valid in reference to the template file. */
|
|
90
|
-
function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
91
|
-
|
|
96
|
+
async function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
97
|
+
const fileExists = await isFileAsync(projectFilePath);
|
|
98
|
+
if (!fileExists) {
|
|
92
99
|
console.log(`Failed to find the following file: ${projectFilePath}`);
|
|
93
100
|
printTemplateLocation(templateFilePath);
|
|
94
101
|
return false;
|
|
95
102
|
}
|
|
96
|
-
const projectFileObject = getTruncatedFileText(projectFilePath, new Set(), new Set());
|
|
97
|
-
const templateFileObject = getTruncatedFileText(templateFilePath, projectFileObject.ignoreLines, projectFileObject.linesBeforeIgnore);
|
|
103
|
+
const projectFileObject = await getTruncatedFileText(projectFilePath, new Set(), new Set());
|
|
104
|
+
const templateFileObject = await getTruncatedFileText(templateFilePath, projectFileObject.ignoreLines, projectFileObject.linesBeforeIgnore);
|
|
98
105
|
if (projectFileObject.text === templateFileObject.text) {
|
|
99
106
|
return true;
|
|
100
107
|
}
|
|
@@ -118,16 +125,16 @@ function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
|
118
125
|
}
|
|
119
126
|
const tempProjectFilePath = path.join(CWD, "tempProjectFile.txt");
|
|
120
127
|
const tempTemplateFilePath = path.join(CWD, "tempTemplateFile.txt");
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
await writeFileAsync(tempProjectFilePath, projectFileObject.text);
|
|
129
|
+
await writeFileAsync(tempTemplateFilePath, templateFileObject.text);
|
|
130
|
+
await $ `diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
|
|
131
|
+
await deleteFileOrDirectoryAsync(tempProjectFilePath);
|
|
132
|
+
await deleteFileOrDirectoryAsync(tempTemplateFilePath);
|
|
126
133
|
return false;
|
|
127
134
|
}
|
|
128
|
-
function getTruncatedFileText(filePath, ignoreLines, linesBeforeIgnore) {
|
|
135
|
+
async function getTruncatedFileText(filePath, ignoreLines, linesBeforeIgnore) {
|
|
129
136
|
const fileName = path.basename(filePath);
|
|
130
|
-
const fileContents =
|
|
137
|
+
const fileContents = await readFileAsync(filePath);
|
|
131
138
|
return getTruncatedText(fileName, fileContents, ignoreLines, linesBeforeIgnore);
|
|
132
139
|
}
|
|
133
140
|
function printTemplateLocation(templateFilePath) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "complete-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "A command line tool for bootstrapping TypeScript projects.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"author": "Zamiell",
|
|
18
18
|
"type": "module",
|
|
19
19
|
"bin": {
|
|
20
|
-
"complete-cli": "./dist/main.
|
|
20
|
+
"complete-cli": "./dist/main.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"chalk": "5.4.1",
|
|
39
39
|
"clipanion": "4.0.0-rc.4",
|
|
40
40
|
"complete-common": "^1.1.1",
|
|
41
|
-
"complete-node": "^3.0
|
|
41
|
+
"complete-node": "^3.1.0",
|
|
42
42
|
"klaw-sync": "6.0.0",
|
|
43
43
|
"yaml": "2.7.0"
|
|
44
44
|
},
|
|
@@ -3,12 +3,13 @@ import { Command, Option } from "clipanion";
|
|
|
3
3
|
import { ReadonlySet } from "complete-common";
|
|
4
4
|
import {
|
|
5
5
|
$,
|
|
6
|
-
|
|
6
|
+
deleteFileOrDirectoryAsync,
|
|
7
7
|
fatalError,
|
|
8
8
|
isDirectory,
|
|
9
|
-
|
|
9
|
+
isFileAsync,
|
|
10
10
|
readFile,
|
|
11
|
-
|
|
11
|
+
readFileAsync,
|
|
12
|
+
writeFileAsync,
|
|
12
13
|
} from "complete-node";
|
|
13
14
|
import klawSync from "klaw-sync";
|
|
14
15
|
import path from "node:path";
|
|
@@ -40,7 +41,6 @@ export class CheckCommand extends Command {
|
|
|
40
41
|
"Check the template files of the current TypeScript project to see if they are up to date.",
|
|
41
42
|
});
|
|
42
43
|
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
44
44
|
async execute(): Promise<void> {
|
|
45
45
|
let oneOrMoreErrors = false;
|
|
46
46
|
const ignore = this.ignore ?? "";
|
|
@@ -48,18 +48,21 @@ export class CheckCommand extends Command {
|
|
|
48
48
|
const ignoreFileNamesSet = new ReadonlySet(ignoreFileNames);
|
|
49
49
|
|
|
50
50
|
// First, check the static files.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
) {
|
|
51
|
+
const staticTemplatesValid = await checkTemplateDirectory(
|
|
52
|
+
TEMPLATES_STATIC_DIR,
|
|
53
|
+
ignoreFileNamesSet,
|
|
54
|
+
this.verbose,
|
|
55
|
+
);
|
|
56
|
+
if (!staticTemplatesValid) {
|
|
58
57
|
oneOrMoreErrors = true;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
// Second, check dynamic files that require specific logic.
|
|
62
|
-
|
|
61
|
+
const dynamicFilesValid = await checkDynamicFiles(
|
|
62
|
+
ignoreFileNamesSet,
|
|
63
|
+
this.verbose,
|
|
64
|
+
);
|
|
65
|
+
if (!dynamicFilesValid) {
|
|
63
66
|
oneOrMoreErrors = true;
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -69,11 +72,12 @@ export class CheckCommand extends Command {
|
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
|
|
75
|
+
/** @returns Whether the directory was valid. */
|
|
76
|
+
async function checkTemplateDirectory(
|
|
73
77
|
templateDirectory: string,
|
|
74
78
|
ignoreFileNamesSet: ReadonlySet<string>,
|
|
75
79
|
verbose: boolean,
|
|
76
|
-
): boolean {
|
|
80
|
+
): Promise<boolean> {
|
|
77
81
|
let oneOrMoreErrors = false;
|
|
78
82
|
|
|
79
83
|
for (const klawItem of klawSync(templateDirectory)) {
|
|
@@ -120,15 +124,22 @@ function checkTemplateDirectory(
|
|
|
120
124
|
continue;
|
|
121
125
|
}
|
|
122
126
|
|
|
123
|
-
|
|
127
|
+
// eslint-disable-next-line no-await-in-loop
|
|
128
|
+
const fileValid = await compareTextFiles(
|
|
129
|
+
projectFilePath,
|
|
130
|
+
templateFilePath,
|
|
131
|
+
verbose,
|
|
132
|
+
);
|
|
133
|
+
if (!fileValid) {
|
|
124
134
|
oneOrMoreErrors = true;
|
|
125
135
|
}
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
return oneOrMoreErrors;
|
|
138
|
+
return !oneOrMoreErrors;
|
|
129
139
|
}
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
/** @returns Whether the dynamic files were valid. */
|
|
142
|
+
async function checkDynamicFiles(
|
|
132
143
|
ignoreFileNamesSet: ReadonlySet<string>,
|
|
133
144
|
verbose: boolean,
|
|
134
145
|
) {
|
|
@@ -141,34 +152,40 @@ function checkIndividualFiles(
|
|
|
141
152
|
templateFilePath,
|
|
142
153
|
);
|
|
143
154
|
const projectFilePath = path.join(CWD, relativeTemplateFilePath);
|
|
144
|
-
|
|
155
|
+
const fileValid = await compareTextFiles(
|
|
156
|
+
projectFilePath,
|
|
157
|
+
templateFilePath,
|
|
158
|
+
verbose,
|
|
159
|
+
);
|
|
160
|
+
if (!fileValid) {
|
|
145
161
|
oneOrMoreErrors = true;
|
|
146
162
|
}
|
|
147
163
|
}
|
|
148
164
|
|
|
149
|
-
return oneOrMoreErrors;
|
|
165
|
+
return !oneOrMoreErrors;
|
|
150
166
|
}
|
|
151
167
|
|
|
152
168
|
/** @returns Whether the project file is valid in reference to the template file. */
|
|
153
|
-
function compareTextFiles(
|
|
169
|
+
async function compareTextFiles(
|
|
154
170
|
projectFilePath: string,
|
|
155
171
|
templateFilePath: string,
|
|
156
172
|
verbose: boolean,
|
|
157
|
-
): boolean {
|
|
158
|
-
|
|
173
|
+
): Promise<boolean> {
|
|
174
|
+
const fileExists = await isFileAsync(projectFilePath);
|
|
175
|
+
if (!fileExists) {
|
|
159
176
|
console.log(`Failed to find the following file: ${projectFilePath}`);
|
|
160
177
|
printTemplateLocation(templateFilePath);
|
|
161
178
|
|
|
162
179
|
return false;
|
|
163
180
|
}
|
|
164
181
|
|
|
165
|
-
const projectFileObject = getTruncatedFileText(
|
|
182
|
+
const projectFileObject = await getTruncatedFileText(
|
|
166
183
|
projectFilePath,
|
|
167
184
|
new Set(),
|
|
168
185
|
new Set(),
|
|
169
186
|
);
|
|
170
187
|
|
|
171
|
-
const templateFileObject = getTruncatedFileText(
|
|
188
|
+
const templateFileObject = await getTruncatedFileText(
|
|
172
189
|
templateFilePath,
|
|
173
190
|
projectFileObject.ignoreLines,
|
|
174
191
|
projectFileObject.linesBeforeIgnore,
|
|
@@ -206,24 +223,24 @@ function compareTextFiles(
|
|
|
206
223
|
const tempProjectFilePath = path.join(CWD, "tempProjectFile.txt");
|
|
207
224
|
const tempTemplateFilePath = path.join(CWD, "tempTemplateFile.txt");
|
|
208
225
|
|
|
209
|
-
|
|
210
|
-
|
|
226
|
+
await writeFileAsync(tempProjectFilePath, projectFileObject.text);
|
|
227
|
+
await writeFileAsync(tempTemplateFilePath, templateFileObject.text);
|
|
211
228
|
|
|
212
|
-
|
|
229
|
+
await $`diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
|
|
213
230
|
|
|
214
|
-
|
|
215
|
-
|
|
231
|
+
await deleteFileOrDirectoryAsync(tempProjectFilePath);
|
|
232
|
+
await deleteFileOrDirectoryAsync(tempTemplateFilePath);
|
|
216
233
|
|
|
217
234
|
return false;
|
|
218
235
|
}
|
|
219
236
|
|
|
220
|
-
function getTruncatedFileText(
|
|
237
|
+
async function getTruncatedFileText(
|
|
221
238
|
filePath: string,
|
|
222
239
|
ignoreLines: ReadonlySet<string>,
|
|
223
240
|
linesBeforeIgnore: ReadonlySet<string>,
|
|
224
241
|
) {
|
|
225
242
|
const fileName = path.basename(filePath);
|
|
226
|
-
const fileContents =
|
|
243
|
+
const fileContents = await readFileAsync(filePath);
|
|
227
244
|
|
|
228
245
|
return getTruncatedText(
|
|
229
246
|
fileName,
|