complete-cli 1.0.8 → 1.0.10
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,8 +1,9 @@
|
|
|
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, readFileAsync, writeFileAsync, } from "complete-node";
|
|
5
5
|
import klawSync from "klaw-sync";
|
|
6
|
+
import os from "node:os";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import { ACTION_YML, ACTION_YML_TEMPLATE_PATH, CWD, TEMPLATES_DYNAMIC_DIR, TEMPLATES_STATIC_DIR, } from "../constants.js";
|
|
8
9
|
import { getTruncatedText } from "./check/getTruncatedText.js";
|
|
@@ -18,18 +19,19 @@ export class CheckCommand extends Command {
|
|
|
18
19
|
static usage = Command.Usage({
|
|
19
20
|
description: "Check the template files of the current TypeScript project to see if they are up to date.",
|
|
20
21
|
});
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
22
22
|
async execute() {
|
|
23
23
|
let oneOrMoreErrors = false;
|
|
24
24
|
const ignore = this.ignore ?? "";
|
|
25
25
|
const ignoreFileNames = ignore.split(",");
|
|
26
26
|
const ignoreFileNamesSet = new ReadonlySet(ignoreFileNames);
|
|
27
27
|
// First, check the static files.
|
|
28
|
-
|
|
28
|
+
const staticTemplatesValid = await checkTemplateDirectory(TEMPLATES_STATIC_DIR, ignoreFileNamesSet, this.verbose);
|
|
29
|
+
if (!staticTemplatesValid) {
|
|
29
30
|
oneOrMoreErrors = true;
|
|
30
31
|
}
|
|
31
32
|
// Second, check dynamic files that require specific logic.
|
|
32
|
-
|
|
33
|
+
const dynamicFilesValid = await checkDynamicFiles(ignoreFileNamesSet, this.verbose);
|
|
34
|
+
if (!dynamicFilesValid) {
|
|
33
35
|
oneOrMoreErrors = true;
|
|
34
36
|
}
|
|
35
37
|
if (oneOrMoreErrors) {
|
|
@@ -37,7 +39,8 @@ export class CheckCommand extends Command {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
/** @returns Whether the directory was valid. */
|
|
43
|
+
async function checkTemplateDirectory(templateDirectory, ignoreFileNamesSet, verbose) {
|
|
41
44
|
let oneOrMoreErrors = false;
|
|
42
45
|
for (const klawItem of klawSync(templateDirectory)) {
|
|
43
46
|
const templateFilePath = klawItem.path;
|
|
@@ -68,41 +71,46 @@ function checkTemplateDirectory(templateDirectory, ignoreFileNamesSet, verbose)
|
|
|
68
71
|
if (ignoreFileNamesSet.has(projectFileName)) {
|
|
69
72
|
continue;
|
|
70
73
|
}
|
|
71
|
-
|
|
74
|
+
// eslint-disable-next-line no-await-in-loop
|
|
75
|
+
const fileValid = await compareTextFiles(projectFilePath, templateFilePath, verbose);
|
|
76
|
+
if (!fileValid) {
|
|
72
77
|
oneOrMoreErrors = true;
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
|
-
return oneOrMoreErrors;
|
|
80
|
+
return !oneOrMoreErrors;
|
|
76
81
|
}
|
|
77
|
-
|
|
82
|
+
/** @returns Whether the dynamic files were valid. */
|
|
83
|
+
async function checkDynamicFiles(ignoreFileNamesSet, verbose) {
|
|
78
84
|
let oneOrMoreErrors = false;
|
|
79
85
|
if (!ignoreFileNamesSet.has(ACTION_YML)) {
|
|
80
86
|
const templateFilePath = ACTION_YML_TEMPLATE_PATH;
|
|
81
87
|
const relativeTemplateFilePath = path.relative(TEMPLATES_DYNAMIC_DIR, templateFilePath);
|
|
82
88
|
const projectFilePath = path.join(CWD, relativeTemplateFilePath);
|
|
83
|
-
|
|
89
|
+
const fileValid = await compareTextFiles(projectFilePath, templateFilePath, verbose);
|
|
90
|
+
if (!fileValid) {
|
|
84
91
|
oneOrMoreErrors = true;
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
|
-
return oneOrMoreErrors;
|
|
94
|
+
return !oneOrMoreErrors;
|
|
88
95
|
}
|
|
89
96
|
/** @returns Whether the project file is valid in reference to the template file. */
|
|
90
|
-
function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
91
|
-
|
|
97
|
+
async function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
98
|
+
const fileExists = await isFileAsync(projectFilePath);
|
|
99
|
+
if (!fileExists) {
|
|
92
100
|
console.log(`Failed to find the following file: ${projectFilePath}`);
|
|
93
101
|
printTemplateLocation(templateFilePath);
|
|
94
102
|
return false;
|
|
95
103
|
}
|
|
96
|
-
const projectFileObject = getTruncatedFileText(projectFilePath, new Set(), new Set());
|
|
97
|
-
const templateFileObject = getTruncatedFileText(templateFilePath, projectFileObject.ignoreLines, projectFileObject.linesBeforeIgnore);
|
|
104
|
+
const projectFileObject = await getTruncatedFileText(projectFilePath, new Set(), new Set());
|
|
105
|
+
const templateFileObject = await getTruncatedFileText(templateFilePath, projectFileObject.ignoreLines, projectFileObject.linesBeforeIgnore);
|
|
98
106
|
if (projectFileObject.text === templateFileObject.text) {
|
|
99
107
|
return true;
|
|
100
108
|
}
|
|
101
109
|
console.log(`The contents of the following file do not match: ${chalk.red(projectFilePath)}`);
|
|
102
110
|
printTemplateLocation(templateFilePath);
|
|
103
111
|
if (verbose) {
|
|
104
|
-
const originalTemplateFile =
|
|
105
|
-
const originalProjectFile =
|
|
112
|
+
const originalTemplateFile = await readFileAsync(templateFilePath);
|
|
113
|
+
const originalProjectFile = await readFileAsync(projectFilePath);
|
|
106
114
|
console.log("--- Original template file: ---\n");
|
|
107
115
|
console.log(originalTemplateFile);
|
|
108
116
|
console.log();
|
|
@@ -116,18 +124,19 @@ function compareTextFiles(projectFilePath, templateFilePath, verbose) {
|
|
|
116
124
|
console.log(projectFileObject.text);
|
|
117
125
|
console.log();
|
|
118
126
|
}
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
const tempDir = os.tmpdir();
|
|
128
|
+
const tempProjectFilePath = path.join(tempDir, "tempProjectFile.txt");
|
|
129
|
+
const tempTemplateFilePath = path.join(tempDir, "tempTemplateFile.txt");
|
|
130
|
+
await writeFileAsync(tempProjectFilePath, projectFileObject.text);
|
|
131
|
+
await writeFileAsync(tempTemplateFilePath, templateFileObject.text);
|
|
132
|
+
await $ `diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
|
|
133
|
+
await deleteFileOrDirectoryAsync(tempProjectFilePath);
|
|
134
|
+
await deleteFileOrDirectoryAsync(tempTemplateFilePath);
|
|
126
135
|
return false;
|
|
127
136
|
}
|
|
128
|
-
function getTruncatedFileText(filePath, ignoreLines, linesBeforeIgnore) {
|
|
137
|
+
async function getTruncatedFileText(filePath, ignoreLines, linesBeforeIgnore) {
|
|
129
138
|
const fileName = path.basename(filePath);
|
|
130
|
-
const fileContents =
|
|
139
|
+
const fileContents = await readFileAsync(filePath);
|
|
131
140
|
return getTruncatedText(fileName, fileContents, ignoreLines, linesBeforeIgnore);
|
|
132
141
|
}
|
|
133
142
|
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.10",
|
|
4
4
|
"description": "A command line tool for bootstrapping TypeScript projects.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript"
|
|
@@ -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,14 +3,15 @@ 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
isFileAsync,
|
|
10
|
+
readFileAsync,
|
|
11
|
+
writeFileAsync,
|
|
12
12
|
} from "complete-node";
|
|
13
13
|
import klawSync from "klaw-sync";
|
|
14
|
+
import os from "node:os";
|
|
14
15
|
import path from "node:path";
|
|
15
16
|
import {
|
|
16
17
|
ACTION_YML,
|
|
@@ -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,
|
|
@@ -186,8 +203,8 @@ function compareTextFiles(
|
|
|
186
203
|
printTemplateLocation(templateFilePath);
|
|
187
204
|
|
|
188
205
|
if (verbose) {
|
|
189
|
-
const originalTemplateFile =
|
|
190
|
-
const originalProjectFile =
|
|
206
|
+
const originalTemplateFile = await readFileAsync(templateFilePath);
|
|
207
|
+
const originalProjectFile = await readFileAsync(projectFilePath);
|
|
191
208
|
|
|
192
209
|
console.log("--- Original template file: ---\n");
|
|
193
210
|
console.log(originalTemplateFile);
|
|
@@ -203,27 +220,28 @@ function compareTextFiles(
|
|
|
203
220
|
console.log();
|
|
204
221
|
}
|
|
205
222
|
|
|
206
|
-
const
|
|
207
|
-
const
|
|
223
|
+
const tempDir = os.tmpdir();
|
|
224
|
+
const tempProjectFilePath = path.join(tempDir, "tempProjectFile.txt");
|
|
225
|
+
const tempTemplateFilePath = path.join(tempDir, "tempTemplateFile.txt");
|
|
208
226
|
|
|
209
|
-
|
|
210
|
-
|
|
227
|
+
await writeFileAsync(tempProjectFilePath, projectFileObject.text);
|
|
228
|
+
await writeFileAsync(tempTemplateFilePath, templateFileObject.text);
|
|
211
229
|
|
|
212
|
-
|
|
230
|
+
await $`diff ${tempProjectFilePath} ${tempTemplateFilePath} --ignore-blank-lines`;
|
|
213
231
|
|
|
214
|
-
|
|
215
|
-
|
|
232
|
+
await deleteFileOrDirectoryAsync(tempProjectFilePath);
|
|
233
|
+
await deleteFileOrDirectoryAsync(tempTemplateFilePath);
|
|
216
234
|
|
|
217
235
|
return false;
|
|
218
236
|
}
|
|
219
237
|
|
|
220
|
-
function getTruncatedFileText(
|
|
238
|
+
async function getTruncatedFileText(
|
|
221
239
|
filePath: string,
|
|
222
240
|
ignoreLines: ReadonlySet<string>,
|
|
223
241
|
linesBeforeIgnore: ReadonlySet<string>,
|
|
224
242
|
) {
|
|
225
243
|
const fileName = path.basename(filePath);
|
|
226
|
-
const fileContents =
|
|
244
|
+
const fileContents = await readFileAsync(filePath);
|
|
227
245
|
|
|
228
246
|
return getTruncatedText(
|
|
229
247
|
fileName,
|