@cedarjs/cli 3.0.0-canary.13258 → 3.0.0-canary.13260
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,9 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createHandler,
|
|
3
|
+
createBuilder,
|
|
4
|
+
getYargsDefaults
|
|
5
|
+
} from "../yargsCommandHelpers.js";
|
|
2
6
|
const command = "package <name>";
|
|
3
7
|
const description = "Generate a workspace Package";
|
|
4
8
|
const builder = createBuilder({
|
|
5
9
|
componentName: "package",
|
|
6
|
-
addStories: false
|
|
10
|
+
addStories: false,
|
|
11
|
+
optionsObj: () => ({
|
|
12
|
+
...getYargsDefaults(),
|
|
13
|
+
workspace: {
|
|
14
|
+
alias: "w",
|
|
15
|
+
description: "Which workspace(s) should use this package? One of: 'none', 'api', 'web', 'both'. If provided, the generator will skip the interactive prompt and apply the chosen workspace.",
|
|
16
|
+
type: "string",
|
|
17
|
+
choices: ["none", "api", "web", "both"]
|
|
18
|
+
}
|
|
19
|
+
})
|
|
7
20
|
});
|
|
8
21
|
const handler = createHandler("package");
|
|
9
22
|
export {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
|
|
3
4
|
import { paramCase, camelCase } from "change-case";
|
|
4
5
|
import execa from "execa";
|
|
6
|
+
import { modify, applyEdits } from "jsonc-parser";
|
|
5
7
|
import { Listr } from "listr2";
|
|
6
8
|
import { terminalLink } from "termi-link";
|
|
9
|
+
import ts from "typescript";
|
|
7
10
|
import { recordTelemetryAttributes } from "@cedarjs/cli-helpers";
|
|
8
11
|
import { getConfig } from "@cedarjs/project-config";
|
|
9
12
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
@@ -25,6 +28,8 @@ async function updateTsconfig(task) {
|
|
|
25
28
|
name: "api",
|
|
26
29
|
path: path.join(getPaths().api.base, "tsconfig.json"),
|
|
27
30
|
expectedModule: "Node20",
|
|
31
|
+
// While Cedar doesn't officially endorse NodeNext, it will still work
|
|
32
|
+
// here, so we'll keep it
|
|
28
33
|
acceptable: ["node20", "nodenext"]
|
|
29
34
|
},
|
|
30
35
|
{
|
|
@@ -40,38 +45,57 @@ async function updateTsconfig(task) {
|
|
|
40
45
|
acceptable: ["node20", "nodenext"]
|
|
41
46
|
}
|
|
42
47
|
];
|
|
43
|
-
let
|
|
48
|
+
let didUpdate = false;
|
|
44
49
|
for (const target of targets) {
|
|
45
50
|
if (!fs.existsSync(target.path)) {
|
|
46
51
|
continue;
|
|
47
52
|
}
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
const tsconfigText = await fs.promises.readFile(target.path, "utf8");
|
|
54
|
+
const { config: tsconfig, error } = ts.parseConfigFileTextToJson(
|
|
55
|
+
target.path,
|
|
56
|
+
tsconfigText
|
|
52
57
|
);
|
|
53
|
-
if (
|
|
58
|
+
if (error) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"Failed to parse tsconfig: " + JSON.stringify(error, null, 2)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (!tsconfig?.compilerOptions || typeof tsconfig.compilerOptions.module === "undefined") {
|
|
54
64
|
continue;
|
|
55
65
|
}
|
|
56
|
-
const
|
|
57
|
-
const lower = moduleLine.toLowerCase();
|
|
66
|
+
const currentModule = typeof tsconfig.compilerOptions.module === "string" ? tsconfig.compilerOptions.module.toLowerCase() : String(tsconfig.compilerOptions.module).toLowerCase();
|
|
58
67
|
const alreadySet = target.acceptable.some((acc) => {
|
|
59
|
-
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
return false;
|
|
68
|
+
return currentModule.includes(acc);
|
|
63
69
|
});
|
|
64
70
|
if (alreadySet) {
|
|
65
71
|
continue;
|
|
66
72
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
const edits = modify(
|
|
74
|
+
tsconfigText,
|
|
75
|
+
["compilerOptions", "module"],
|
|
76
|
+
target.expectedModule,
|
|
77
|
+
{
|
|
78
|
+
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
79
|
+
}
|
|
70
80
|
);
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
if (edits.length === 0) {
|
|
82
|
+
const newConfig = { ...tsconfig };
|
|
83
|
+
if (!newConfig.compilerOptions) {
|
|
84
|
+
newConfig.compilerOptions = {};
|
|
85
|
+
}
|
|
86
|
+
newConfig.compilerOptions.module = target.expectedModule;
|
|
87
|
+
await fs.promises.writeFile(
|
|
88
|
+
target.path,
|
|
89
|
+
JSON.stringify(newConfig, null, 2),
|
|
90
|
+
"utf8"
|
|
91
|
+
);
|
|
92
|
+
} else {
|
|
93
|
+
const newText = applyEdits(tsconfigText, edits);
|
|
94
|
+
await fs.promises.writeFile(target.path, newText, "utf8");
|
|
95
|
+
}
|
|
96
|
+
didUpdate = true;
|
|
73
97
|
}
|
|
74
|
-
if (!
|
|
98
|
+
if (!didUpdate) {
|
|
75
99
|
task.skip("tsconfig already up to date");
|
|
76
100
|
return;
|
|
77
101
|
}
|
|
@@ -94,6 +118,130 @@ async function updateGitignore(task) {
|
|
|
94
118
|
}
|
|
95
119
|
await fs.promises.writeFile(gitignorePath, gitignoreLines.join("\n"));
|
|
96
120
|
}
|
|
121
|
+
async function addDependencyToPackageJson(task, packageJsonPath, packageName) {
|
|
122
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
123
|
+
task.skip("package.json not found");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const packageJson = JSON.parse(
|
|
127
|
+
await fs.promises.readFile(packageJsonPath, "utf8")
|
|
128
|
+
);
|
|
129
|
+
if (!packageJson.dependencies) {
|
|
130
|
+
packageJson.dependencies = {};
|
|
131
|
+
}
|
|
132
|
+
if (packageJson.dependencies[packageName]) {
|
|
133
|
+
task.skip("Dependency already exists");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
packageJson.dependencies[packageName] = "workspace:*";
|
|
137
|
+
await fs.promises.writeFile(
|
|
138
|
+
packageJsonPath,
|
|
139
|
+
JSON.stringify(packageJson, null, 2)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
function parseWorkspaceFlag(workspace) {
|
|
143
|
+
if (workspace === void 0 || workspace === null) {
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
const ws = String(workspace).trim().toLowerCase();
|
|
147
|
+
const allowed = ["none", "api", "web", "both"];
|
|
148
|
+
if (!allowed.includes(ws)) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Invalid workspace value "${workspace}". Valid options: ${allowed.join(", ")}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return ws;
|
|
154
|
+
}
|
|
155
|
+
function updateWorkspaceTsconfigReferences(task, folderName, targetWorkspaces) {
|
|
156
|
+
if (!targetWorkspaces || targetWorkspaces === "none") {
|
|
157
|
+
task.skip("No workspace selected");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const workspaces = [];
|
|
161
|
+
const packageDir = path.join(getPaths().base, "packages", folderName);
|
|
162
|
+
if (targetWorkspaces === "api" || targetWorkspaces === "both") {
|
|
163
|
+
const tsconfigPath = path.join(getPaths().api.base, "tsconfig.json");
|
|
164
|
+
workspaces.push({ name: "api", tsconfigPath, packageDir });
|
|
165
|
+
}
|
|
166
|
+
if (targetWorkspaces === "web" || targetWorkspaces === "both") {
|
|
167
|
+
const tsconfigPath = path.join(getPaths().web.base, "tsconfig.json");
|
|
168
|
+
workspaces.push({ name: "web", tsconfigPath, packageDir });
|
|
169
|
+
}
|
|
170
|
+
if (targetWorkspaces !== "none") {
|
|
171
|
+
const tsconfigPath = path.join(getPaths().scripts, "tsconfig.json");
|
|
172
|
+
workspaces.push({ name: "scripts", tsconfigPath, packageDir });
|
|
173
|
+
}
|
|
174
|
+
if (workspaces.length === 0) {
|
|
175
|
+
task.skip("No workspace selected");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const subtasks = workspaces.map((ws) => {
|
|
179
|
+
return {
|
|
180
|
+
title: `Updating ${ws.name} tsconfig references...`,
|
|
181
|
+
task: async (_ctx, subtask) => {
|
|
182
|
+
if (!fs.existsSync(ws.tsconfigPath)) {
|
|
183
|
+
subtask.skip("tsconfig.json not found");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const tsconfigText = await fs.promises.readFile(ws.tsconfigPath, "utf8");
|
|
187
|
+
const { config: tsconfig, error } = ts.parseConfigFileTextToJson(
|
|
188
|
+
ws.tsconfigPath,
|
|
189
|
+
tsconfigText
|
|
190
|
+
);
|
|
191
|
+
if (error) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
"Failed to parse tsconfig: " + JSON.stringify(error, null, 2)
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const configParseResult = ts.parseJsonConfigFileContent(
|
|
197
|
+
tsconfig,
|
|
198
|
+
{
|
|
199
|
+
...ts.sys,
|
|
200
|
+
readFile: (p) => {
|
|
201
|
+
try {
|
|
202
|
+
return fs.readFileSync(p, "utf8");
|
|
203
|
+
} catch (e) {
|
|
204
|
+
return ts.sys.readFile(p);
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
fileExists: (p) => {
|
|
208
|
+
if (typeof fs.existsSync === "function") {
|
|
209
|
+
return fs.existsSync(p);
|
|
210
|
+
}
|
|
211
|
+
return ts.sys.fileExists(p);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
path.dirname(ws.tsconfigPath)
|
|
215
|
+
);
|
|
216
|
+
if (configParseResult.errors && configParseResult.errors.length) {
|
|
217
|
+
console.error("Parse errors:", configParseResult.errors);
|
|
218
|
+
}
|
|
219
|
+
if (!Array.isArray(tsconfig.references)) {
|
|
220
|
+
tsconfig.references = [];
|
|
221
|
+
}
|
|
222
|
+
const packageDir2 = ws.packageDir || path.join(getPaths().base, "packages", folderName);
|
|
223
|
+
const referencePath = path.relative(path.dirname(ws.tsconfigPath), packageDir2).split(path.sep).join("/");
|
|
224
|
+
const references = tsconfig.references;
|
|
225
|
+
if (references.some((ref) => ref && ref.path === referencePath)) {
|
|
226
|
+
subtask.skip("tsconfig already up to date");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const newReferences = references.concat([{ path: referencePath }]);
|
|
230
|
+
let text = await fs.promises.readFile(ws.tsconfigPath, "utf8");
|
|
231
|
+
const edits = modify(text, ["references"], newReferences, {
|
|
232
|
+
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
233
|
+
});
|
|
234
|
+
const newText = applyEdits(text, edits);
|
|
235
|
+
const formattedText = newText.replace(
|
|
236
|
+
/"references": \[\s*\{\s*"path":\s*"([^"]+)"\s*\}\s*\]/,
|
|
237
|
+
'"references": [{ "path": "' + referencePath + '" }]'
|
|
238
|
+
);
|
|
239
|
+
await fs.promises.writeFile(ws.tsconfigPath, formattedText, "utf8");
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
return new Listr(subtasks);
|
|
244
|
+
}
|
|
97
245
|
async function installAndBuild(folderName) {
|
|
98
246
|
const packagePath = path.join("packages", folderName);
|
|
99
247
|
await execa("yarn", ["install"], { stdio: "inherit", cwd: getPaths().base });
|
|
@@ -165,6 +313,35 @@ const handler = async ({ name, force, ...rest }) => {
|
|
|
165
313
|
}
|
|
166
314
|
}
|
|
167
315
|
},
|
|
316
|
+
{
|
|
317
|
+
title: "Choose package workspace(s)...",
|
|
318
|
+
task: async (ctx, task) => {
|
|
319
|
+
try {
|
|
320
|
+
const flagValue = parseWorkspaceFlag(rest.workspace);
|
|
321
|
+
if (flagValue !== void 0) {
|
|
322
|
+
ctx.targetWorkspaces = flagValue;
|
|
323
|
+
task.skip(
|
|
324
|
+
`Using workspace provided via --workspace: ${flagValue}`
|
|
325
|
+
);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
} catch (e) {
|
|
329
|
+
throw new Error(e.message);
|
|
330
|
+
}
|
|
331
|
+
const prompt = task.prompt(ListrEnquirerPromptAdapter);
|
|
332
|
+
const response = await prompt.run({
|
|
333
|
+
type: "select",
|
|
334
|
+
message: "Which workspace(s) should use this package?",
|
|
335
|
+
choices: [
|
|
336
|
+
{ message: "none (I will add it manually later)", value: "none" },
|
|
337
|
+
{ message: "api", value: "api" },
|
|
338
|
+
{ message: "web", value: "web" },
|
|
339
|
+
{ message: "both", value: "both" }
|
|
340
|
+
]
|
|
341
|
+
});
|
|
342
|
+
ctx.targetWorkspaces = response;
|
|
343
|
+
}
|
|
344
|
+
},
|
|
168
345
|
{
|
|
169
346
|
title: "Updating tsconfig files...",
|
|
170
347
|
task: (_ctx, task) => updateTsconfig(task)
|
|
@@ -180,6 +357,67 @@ const handler = async ({ name, force, ...rest }) => {
|
|
|
180
357
|
return writeFilesTask(packageFiles, { overwriteExisting: force });
|
|
181
358
|
}
|
|
182
359
|
},
|
|
360
|
+
{
|
|
361
|
+
title: "Adding package to workspace dependencies...",
|
|
362
|
+
task: async (ctx, task) => {
|
|
363
|
+
if (!ctx.targetWorkspaces || ctx.targetWorkspaces === "none") {
|
|
364
|
+
task.skip("No workspace selected");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const subtasks = [];
|
|
368
|
+
if (ctx.targetWorkspaces === "api" || ctx.targetWorkspaces === "both") {
|
|
369
|
+
subtasks.push({
|
|
370
|
+
title: "Adding to api package.json...",
|
|
371
|
+
task: async (_subCtx, subtask) => {
|
|
372
|
+
const apiPackageJsonPath = path.join(
|
|
373
|
+
getPaths().api.base,
|
|
374
|
+
"package.json"
|
|
375
|
+
);
|
|
376
|
+
return addDependencyToPackageJson(
|
|
377
|
+
subtask,
|
|
378
|
+
apiPackageJsonPath,
|
|
379
|
+
ctx.nameVariants.packageName
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (ctx.targetWorkspaces === "web" || ctx.targetWorkspaces === "both") {
|
|
385
|
+
subtasks.push({
|
|
386
|
+
title: "Adding to web package.json...",
|
|
387
|
+
task: async (_subCtx, subtask) => {
|
|
388
|
+
const webPackageJsonPath = path.join(
|
|
389
|
+
getPaths().web.base,
|
|
390
|
+
"package.json"
|
|
391
|
+
);
|
|
392
|
+
return addDependencyToPackageJson(
|
|
393
|
+
subtask,
|
|
394
|
+
webPackageJsonPath,
|
|
395
|
+
ctx.nameVariants.packageName
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
if (subtasks.length === 0) {
|
|
401
|
+
task.skip("No workspace selected");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
return new Listr(subtasks);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
title: "Updating tsconfig references...",
|
|
409
|
+
task: (ctx, task) => {
|
|
410
|
+
if (!ctx.targetWorkspaces || ctx.targetWorkspaces === "none") {
|
|
411
|
+
task.skip("No workspace selected");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
return updateWorkspaceTsconfigReferences(
|
|
415
|
+
task,
|
|
416
|
+
ctx.nameVariants.folderName,
|
|
417
|
+
ctx.targetWorkspaces
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
},
|
|
183
421
|
{
|
|
184
422
|
title: "Installing and building...",
|
|
185
423
|
task: (ctx) => installAndBuild(ctx.nameVariants.folderName)
|
|
@@ -211,8 +449,11 @@ const handler = async ({ name, force, ...rest }) => {
|
|
|
211
449
|
}
|
|
212
450
|
};
|
|
213
451
|
export {
|
|
452
|
+
addDependencyToPackageJson,
|
|
214
453
|
handler,
|
|
215
454
|
nameVariants,
|
|
455
|
+
parseWorkspaceFlag,
|
|
216
456
|
updateGitignore,
|
|
217
|
-
updateTsconfig
|
|
457
|
+
updateTsconfig,
|
|
458
|
+
updateWorkspaceTsconfigReferences
|
|
218
459
|
};
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { terminalLink } from "termi-link";
|
|
2
2
|
import { isTypeScriptProject } from "@cedarjs/cli-helpers";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
3
|
+
function getYargsDefaults() {
|
|
4
|
+
return {
|
|
5
|
+
force: {
|
|
6
|
+
alias: "f",
|
|
7
|
+
default: false,
|
|
8
|
+
description: "Overwrite existing files",
|
|
9
|
+
type: "boolean"
|
|
10
|
+
},
|
|
11
|
+
typescript: {
|
|
12
|
+
alias: "ts",
|
|
13
|
+
default: isTypeScriptProject(),
|
|
14
|
+
description: "Generate TypeScript files",
|
|
15
|
+
type: "boolean"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
17
19
|
const appendPositionalsToCmd = (commandString, positionalsObj) => {
|
|
18
20
|
if (Object.keys(positionalsObj).length > 0) {
|
|
19
21
|
const positionalNames = Object.keys(positionalsObj).map((positionalName) => `[${positionalName}]`).join(" ");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/cli",
|
|
3
|
-
"version": "3.0.0-canary.
|
|
3
|
+
"version": "3.0.0-canary.13260+fc8c011ea",
|
|
4
4
|
"description": "The CedarJS Command Line",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@babel/preset-typescript": "7.28.5",
|
|
35
35
|
"@babel/runtime-corejs3": "7.28.4",
|
|
36
|
-
"@cedarjs/api-server": "3.0.0-canary.
|
|
37
|
-
"@cedarjs/cli-helpers": "3.0.0-canary.
|
|
38
|
-
"@cedarjs/fastify-web": "3.0.0-canary.
|
|
39
|
-
"@cedarjs/internal": "3.0.0-canary.
|
|
40
|
-
"@cedarjs/prerender": "3.0.0-canary.
|
|
41
|
-
"@cedarjs/project-config": "3.0.0-canary.
|
|
42
|
-
"@cedarjs/structure": "3.0.0-canary.
|
|
43
|
-
"@cedarjs/telemetry": "3.0.0-canary.
|
|
44
|
-
"@cedarjs/web-server": "3.0.0-canary.
|
|
36
|
+
"@cedarjs/api-server": "3.0.0-canary.13260",
|
|
37
|
+
"@cedarjs/cli-helpers": "3.0.0-canary.13260",
|
|
38
|
+
"@cedarjs/fastify-web": "3.0.0-canary.13260",
|
|
39
|
+
"@cedarjs/internal": "3.0.0-canary.13260",
|
|
40
|
+
"@cedarjs/prerender": "3.0.0-canary.13260",
|
|
41
|
+
"@cedarjs/project-config": "3.0.0-canary.13260",
|
|
42
|
+
"@cedarjs/structure": "3.0.0-canary.13260",
|
|
43
|
+
"@cedarjs/telemetry": "3.0.0-canary.13260",
|
|
44
|
+
"@cedarjs/web-server": "3.0.0-canary.13260",
|
|
45
45
|
"@listr2/prompt-adapter-enquirer": "2.0.16",
|
|
46
46
|
"@opentelemetry/api": "1.8.0",
|
|
47
47
|
"@opentelemetry/core": "1.22.0",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"fast-glob": "3.3.3",
|
|
69
69
|
"humanize-string": "2.1.0",
|
|
70
70
|
"jscodeshift": "17.0.0",
|
|
71
|
+
"jsonc-parser": "3.3.1",
|
|
71
72
|
"latest-version": "9.0.0",
|
|
72
73
|
"listr2": "7.0.2",
|
|
73
74
|
"lodash": "4.17.21",
|
|
@@ -103,5 +104,5 @@
|
|
|
103
104
|
"publishConfig": {
|
|
104
105
|
"access": "public"
|
|
105
106
|
},
|
|
106
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "fc8c011eae3a8664a56932f184759fa74b57e265"
|
|
107
108
|
}
|