@cedarjs/cli 2.2.2-next.0 → 2.2.2-next.31

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.
@@ -0,0 +1,235 @@
1
+ import fs from "node:fs";
2
+ import { builtinModules } from "node:module";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import execa from "execa";
6
+ import semver from "semver";
7
+ function isExecaError(e) {
8
+ return e instanceof Error && ("stdout" in e || "stderr" in e || "exitCode" in e);
9
+ }
10
+ async function runPreUpgradeScripts(ctx, task, { verbose, force }) {
11
+ if (!ctx.versionToUpgradeTo) {
12
+ return;
13
+ }
14
+ const version = typeof ctx.versionToUpgradeTo === "string" ? ctx.versionToUpgradeTo : void 0;
15
+ const parsed = semver.parse(version);
16
+ const baseUrl = "https://raw.githubusercontent.com/cedarjs/cedar/main/upgrade-scripts/";
17
+ const manifestUrl = `${baseUrl}manifest.json`;
18
+ let manifest = [];
19
+ try {
20
+ const res = await fetch(manifestUrl);
21
+ if (res.status === 200) {
22
+ manifest = await res.json();
23
+ } else {
24
+ if (verbose) {
25
+ console.log("No upgrade script manifest found.");
26
+ }
27
+ }
28
+ } catch (e) {
29
+ if (verbose) {
30
+ console.log("Failed to fetch upgrade script manifest", e);
31
+ }
32
+ }
33
+ if (!Array.isArray(manifest) || manifest.length === 0) {
34
+ return;
35
+ }
36
+ const checkLevels = [];
37
+ if (parsed && !parsed.prerelease.length) {
38
+ checkLevels.push({
39
+ id: "exact",
40
+ candidates: [`${version}.ts`, `${version}/index.ts`]
41
+ });
42
+ checkLevels.push({
43
+ id: "patch",
44
+ candidates: [
45
+ `${parsed.major}.${parsed.minor}.x.ts`,
46
+ `${parsed.major}.${parsed.minor}.x/index.ts`
47
+ ]
48
+ });
49
+ checkLevels.push({
50
+ id: "minor",
51
+ candidates: [`${parsed.major}.x.ts`, `${parsed.major}.x/index.ts`]
52
+ });
53
+ } else if (parsed && parsed.prerelease.length > 0) {
54
+ checkLevels.push({
55
+ id: "tag",
56
+ candidates: [
57
+ `${parsed.prerelease[0]}.ts`,
58
+ `${parsed.prerelease[0]}/index.ts`
59
+ ]
60
+ });
61
+ }
62
+ const scriptsToRun = [];
63
+ for (const level of checkLevels) {
64
+ for (const candidate of level.candidates) {
65
+ if (manifest.includes(candidate)) {
66
+ scriptsToRun.push(candidate);
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ if (scriptsToRun.length === 0) {
72
+ if (verbose) {
73
+ console.log(`No upgrade scripts found for ${version}`);
74
+ }
75
+ return;
76
+ }
77
+ ctx.preUpgradeMessage = "";
78
+ ctx.preUpgradeError = "";
79
+ for (const scriptName of scriptsToRun) {
80
+ task.output = `Found upgrade check script: ${scriptName}. Downloading...`;
81
+ const tempDir = await fs.promises.mkdtemp(
82
+ // realpath: https://github.com/e18e/ecosystem-issues/issues/168
83
+ path.join(fs.realpathSync(os.tmpdir()), "cedar-upgrade-")
84
+ );
85
+ const scriptPath = path.join(tempDir, "script.mts");
86
+ const isDirectoryScript = scriptName.includes("/");
87
+ if (isDirectoryScript) {
88
+ const dirName = scriptName.split("/")[0];
89
+ const githubApiUrl = `https://api.github.com/repos/cedarjs/cedar/contents/upgrade-scripts/${dirName}`;
90
+ try {
91
+ const dirRes = await fetch(githubApiUrl, {
92
+ headers: {
93
+ Accept: "application/vnd.github.v3+json"
94
+ }
95
+ });
96
+ if (dirRes.status !== 200) {
97
+ throw new Error(
98
+ `Failed to fetch directory contents: ${dirRes.statusText}`
99
+ );
100
+ }
101
+ const files = await dirRes.json();
102
+ for (const file of files) {
103
+ if (file.type === "file") {
104
+ task.output = `Downloading ${file.name}...`;
105
+ const fileRes = await fetch(file.download_url);
106
+ if (fileRes.status !== 200) {
107
+ throw new Error(`Failed to download ${file.name}`);
108
+ }
109
+ const fileContent = await fileRes.text();
110
+ const filePath = path.join(tempDir, file.name);
111
+ await fs.promises.writeFile(filePath, fileContent);
112
+ if (file.name === "index.ts") {
113
+ await fs.promises.rename(filePath, scriptPath);
114
+ }
115
+ }
116
+ }
117
+ } catch (e) {
118
+ if (verbose) {
119
+ console.error(e);
120
+ }
121
+ throw new Error(
122
+ `Failed to download upgrade script directory from ${githubApiUrl}`
123
+ );
124
+ }
125
+ } else {
126
+ const scriptUrl = `${baseUrl}${scriptName}`;
127
+ try {
128
+ const res = await fetch(scriptUrl);
129
+ if (res.status !== 200) {
130
+ throw new Error(`Failed to download script: ${res.statusText}`);
131
+ }
132
+ const scriptContent2 = await res.text();
133
+ await fs.promises.writeFile(scriptPath, scriptContent2);
134
+ } catch (e) {
135
+ if (verbose) {
136
+ console.error(e);
137
+ }
138
+ throw new Error(`Failed to download upgrade script from ${scriptUrl}`);
139
+ }
140
+ }
141
+ const scriptContent = await fs.promises.readFile(scriptPath, "utf8");
142
+ const deps = extractDependencies(scriptContent);
143
+ if (deps.length > 0) {
144
+ const depList = deps.join(", ");
145
+ task.output = `Installing dependencies for ${scriptName}: ${depList}...`;
146
+ await fs.promises.writeFile(
147
+ path.join(tempDir, "package.json"),
148
+ JSON.stringify({
149
+ name: "pre-upgrade-script",
150
+ version: "0.0.0",
151
+ dependencies: {}
152
+ })
153
+ );
154
+ await execa("npm", ["install", ...deps], { cwd: tempDir });
155
+ }
156
+ task.output = `Running pre-upgrade script: ${scriptName}...`;
157
+ let shouldCleanup = true;
158
+ try {
159
+ const { stdout } = await execa(
160
+ "node",
161
+ ["script.mts", "--verbose", String(verbose), "--force", String(force)],
162
+ { cwd: tempDir }
163
+ );
164
+ if (stdout) {
165
+ if (ctx.preUpgradeMessage) {
166
+ ctx.preUpgradeMessage += "\n";
167
+ }
168
+ ctx.preUpgradeMessage += `
169
+ ${stdout}`;
170
+ }
171
+ } catch (e) {
172
+ let errorOutput = String(e);
173
+ let exitCode;
174
+ let stderr;
175
+ if (isExecaError(e)) {
176
+ errorOutput = e.stdout || e.message;
177
+ stderr = e.stderr;
178
+ exitCode = e.exitCode;
179
+ } else if (e instanceof Error) {
180
+ errorOutput = e.message;
181
+ }
182
+ if (ctx.preUpgradeError) {
183
+ ctx.preUpgradeError += "\n";
184
+ }
185
+ if (verbose) {
186
+ ctx.preUpgradeError += `
187
+ Pre-upgrade check ${scriptName} failed with exit code ${exitCode}:
188
+ ${stderr ? stderr + "\n" : ""}`;
189
+ }
190
+ ctx.preUpgradeError += `
191
+ ${errorOutput}`;
192
+ if (!force) {
193
+ await fs.promises.rm(tempDir, { recursive: true });
194
+ shouldCleanup = false;
195
+ return;
196
+ }
197
+ } finally {
198
+ if (shouldCleanup) {
199
+ await fs.promises.rm(tempDir, { recursive: true });
200
+ }
201
+ }
202
+ }
203
+ }
204
+ const extractDependencies = (content) => {
205
+ const deps = /* @__PURE__ */ new Map();
206
+ const commentRegex = /\/\/\s*@dependency:\s*(\S+)/g;
207
+ let match;
208
+ while ((match = commentRegex.exec(content)) !== null) {
209
+ const spec = match[1];
210
+ const nameMatch = spec.match(/^(@?[^@\s]+)(?:@.+)?$/);
211
+ if (nameMatch) {
212
+ deps.set(nameMatch[1], spec);
213
+ }
214
+ }
215
+ const importRegex = /(?:import|from)\s*\(?['"]([^'"]+)['"]\)?/g;
216
+ while ((match = importRegex.exec(content)) !== null) {
217
+ let name = match[1];
218
+ if (name.startsWith(".") || name.startsWith("/") || name.startsWith("node:") || builtinModules.includes(name)) {
219
+ continue;
220
+ }
221
+ const parts = name.split("/");
222
+ if (name.startsWith("@") && parts.length >= 2) {
223
+ name = parts.slice(0, 2).join("/");
224
+ } else if (parts.length >= 1) {
225
+ name = parts[0];
226
+ }
227
+ if (!deps.has(name)) {
228
+ deps.set(name, name);
229
+ }
230
+ }
231
+ return Array.from(deps.values());
232
+ };
233
+ export {
234
+ runPreUpgradeScripts
235
+ };
@@ -0,0 +1,6 @@
1
+ function isValidCedarJSTag(tag) {
2
+ return ["rc", "canary", "latest", "next", "experimental"].includes(tag);
3
+ }
4
+ export {
5
+ isValidCedarJSTag
6
+ };
@@ -0,0 +1,75 @@
1
+ import { terminalLink } from "termi-link";
2
+ import c from "../../lib/colors.js";
3
+ import { isValidCedarJSTag } from "./tags.js";
4
+ const SEMVER_REGEX = /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(?=$|\s)/i;
5
+ const isValidSemver = (string) => {
6
+ return SEMVER_REGEX.test(string);
7
+ };
8
+ const validateTag = (tag) => {
9
+ const isTagValid = isValidSemver(tag) || isValidCedarJSTag(tag);
10
+ if (!isTagValid) {
11
+ throw new Error(
12
+ c.error(
13
+ "Invalid tag supplied. Supported values: 'rc', 'canary', 'latest', 'next', 'experimental', or a valid semver version\n"
14
+ )
15
+ );
16
+ }
17
+ return tag;
18
+ };
19
+ const command = "upgrade";
20
+ const description = "Upgrade all @cedarjs packages via interactive CLI";
21
+ const builder = (yargs) => {
22
+ yargs.example(
23
+ "cedar upgrade -t 0.20.1-canary.5",
24
+ "Specify a version. URL for Version History:\nhttps://www.npmjs.com/package/@cedarjs/core"
25
+ ).option("dry-run", {
26
+ alias: "d",
27
+ description: "Check for outdated packages without upgrading",
28
+ type: "boolean"
29
+ }).option("tag", {
30
+ alias: "t",
31
+ description: '[choices: "latest", "rc", "next", "canary", "experimental", or a specific-version (see example below)] WARNING: "canary", "rc" and "experimental" are unstable releases! And "canary" releases include breaking changes often requiring changes to your codebase when upgrading a project.',
32
+ requiresArg: true,
33
+ type: "string",
34
+ coerce: validateTag
35
+ }).option("verbose", {
36
+ alias: "v",
37
+ description: "Print verbose logs",
38
+ type: "boolean",
39
+ default: false
40
+ }).option("dedupe", {
41
+ description: "Skip dedupe check with --no-dedupe",
42
+ type: "boolean",
43
+ default: true
44
+ }).option("yes", {
45
+ alias: "y",
46
+ describe: "Skip prompts and use defaults",
47
+ default: false,
48
+ type: "boolean"
49
+ }).option("force", {
50
+ alias: "f",
51
+ describe: "Force upgrade even if pre-upgrade checks fail",
52
+ default: false,
53
+ type: "boolean"
54
+ }).epilogue(
55
+ `Also see the ${terminalLink(
56
+ "CedarJS CLI Reference for the upgrade command",
57
+ "https://cedarjs.com/docs/cli-commands#upgrade"
58
+ )}.
59
+ And the ${terminalLink(
60
+ "GitHub releases page",
61
+ "https://github.com/cedarjs/cedar/releases"
62
+ )} for more information on the current release.`
63
+ );
64
+ };
65
+ const handler = async (options) => {
66
+ const { handler: handler2 } = await import("./upgradeHandler.js");
67
+ return handler2(options);
68
+ };
69
+ export {
70
+ builder,
71
+ command,
72
+ description,
73
+ handler,
74
+ validateTag
75
+ };