@teardown/cli 2.0.56 → 2.0.58
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teardown/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.58",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@biomejs/biome": "2.3.11",
|
|
74
|
-
"@teardown/tsconfig": "2.0.
|
|
74
|
+
"@teardown/tsconfig": "2.0.58",
|
|
75
75
|
"@types/bun": "1.3.5",
|
|
76
76
|
"@types/ejs": "^3.1.5",
|
|
77
77
|
"typescript": "5.9.3"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { findConfigFile } from "../../config";
|
|
9
|
+
import { TemplateGenerator } from "../../templates";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Teardown packages that should be upgraded
|
|
13
|
+
*/
|
|
14
|
+
const TEARDOWN_PACKAGES = [
|
|
15
|
+
"@teardown/cli",
|
|
16
|
+
"@teardown/dev-client",
|
|
17
|
+
"@teardown/navigation",
|
|
18
|
+
"@teardown/metro-config",
|
|
19
|
+
"@teardown/navigation-metro",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Config files that can be upgraded from templates
|
|
24
|
+
*/
|
|
25
|
+
const UPGRADABLE_CONFIG_FILES = ["metro.config.js", "babel.config.js", "react-native.config.js", "Gemfile"];
|
|
26
|
+
|
|
27
|
+
interface PackageJson {
|
|
28
|
+
name?: string;
|
|
29
|
+
version?: string;
|
|
30
|
+
dependencies?: Record<string, string>;
|
|
31
|
+
devDependencies?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetch the latest version of a package from npm
|
|
36
|
+
*/
|
|
37
|
+
async function fetchLatestVersion(packageName: string): Promise<string | null> {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const child = spawn("npm", ["view", packageName, "version"], {
|
|
40
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
let stdout = "";
|
|
44
|
+
child.stdout?.on("data", (data) => {
|
|
45
|
+
stdout += data.toString();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
child.on("close", (code) => {
|
|
49
|
+
if (code === 0) {
|
|
50
|
+
resolve(stdout.trim());
|
|
51
|
+
} else {
|
|
52
|
+
resolve(null);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.on("error", () => {
|
|
57
|
+
resolve(null);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the CLI package version
|
|
64
|
+
*/
|
|
65
|
+
function getCliVersion(): string {
|
|
66
|
+
try {
|
|
67
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
68
|
+
const __dirname = path.dirname(__filename);
|
|
69
|
+
const packageJsonPath = path.resolve(__dirname, "../../../package.json");
|
|
70
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
71
|
+
return packageJson.version;
|
|
72
|
+
} catch {
|
|
73
|
+
return "0.0.1";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create the upgrade command
|
|
79
|
+
*/
|
|
80
|
+
export function createUpgradeCommand(): Command {
|
|
81
|
+
const command = new Command("upgrade")
|
|
82
|
+
.description("Upgrade Teardown packages and config files to the latest versions")
|
|
83
|
+
.option("-f, --force", "Force upgrade config files even if they exist", false)
|
|
84
|
+
.option("--skip-install", "Skip running bun install after upgrade", false)
|
|
85
|
+
.option("--skip-config", "Skip upgrading config files", false)
|
|
86
|
+
.option("--dry-run", "Show what would be upgraded without making changes", false)
|
|
87
|
+
.action(async (options) => {
|
|
88
|
+
const projectRoot = process.cwd();
|
|
89
|
+
|
|
90
|
+
// Check if this is a Teardown project
|
|
91
|
+
const configPath = findConfigFile(projectRoot);
|
|
92
|
+
if (!configPath) {
|
|
93
|
+
console.error(chalk.red("Error: This does not appear to be a Teardown project."));
|
|
94
|
+
console.log(chalk.gray("\nNo teardown.config.ts found."));
|
|
95
|
+
console.log(chalk.gray("Run 'teardown init <name>' to initialize a new project."));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(chalk.cyan("\nTeardown Upgrade\n"));
|
|
100
|
+
|
|
101
|
+
// Check for package.json
|
|
102
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
103
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
104
|
+
console.error(chalk.red("Error: No package.json found in project root."));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
109
|
+
const updates: { name: string; from: string; to: string }[] = [];
|
|
110
|
+
|
|
111
|
+
// Fetch latest versions and check for updates
|
|
112
|
+
const versionSpinner = ora("Checking for package updates...").start();
|
|
113
|
+
|
|
114
|
+
for (const pkg of TEARDOWN_PACKAGES) {
|
|
115
|
+
// Check if package is in dependencies or devDependencies
|
|
116
|
+
const currentVersion = packageJson.dependencies?.[pkg] || packageJson.devDependencies?.[pkg];
|
|
117
|
+
|
|
118
|
+
if (!currentVersion) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const latestVersion = await fetchLatestVersion(pkg);
|
|
123
|
+
if (latestVersion) {
|
|
124
|
+
const cleanCurrentVersion = currentVersion.replace(/^\^|~/, "");
|
|
125
|
+
if (cleanCurrentVersion !== latestVersion) {
|
|
126
|
+
updates.push({
|
|
127
|
+
name: pkg,
|
|
128
|
+
from: currentVersion,
|
|
129
|
+
to: `^${latestVersion}`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
versionSpinner.succeed("Checked package versions");
|
|
136
|
+
|
|
137
|
+
// Display updates
|
|
138
|
+
if (updates.length > 0) {
|
|
139
|
+
console.log(chalk.yellow("\nPackages to update:"));
|
|
140
|
+
for (const update of updates) {
|
|
141
|
+
console.log(
|
|
142
|
+
chalk.gray(` ${update.name}: `) + chalk.red(update.from) + chalk.gray(" -> ") + chalk.green(update.to)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
console.log(chalk.green("\nAll Teardown packages are up to date!"));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Apply updates if not dry run
|
|
150
|
+
if (!options.dryRun && updates.length > 0) {
|
|
151
|
+
const updateSpinner = ora("Updating package.json...").start();
|
|
152
|
+
|
|
153
|
+
for (const update of updates) {
|
|
154
|
+
if (packageJson.dependencies?.[update.name]) {
|
|
155
|
+
packageJson.dependencies[update.name] = update.to;
|
|
156
|
+
}
|
|
157
|
+
if (packageJson.devDependencies?.[update.name]) {
|
|
158
|
+
packageJson.devDependencies[update.name] = update.to;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, "\t") + "\n", "utf-8");
|
|
163
|
+
updateSpinner.succeed("Updated package.json");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Upgrade config files
|
|
167
|
+
if (!options.skipConfig) {
|
|
168
|
+
console.log(chalk.yellow("\nConfig files:"));
|
|
169
|
+
|
|
170
|
+
// Read current config to get variables
|
|
171
|
+
const config = await import(configPath);
|
|
172
|
+
const configData = config.default || config;
|
|
173
|
+
|
|
174
|
+
const slug = configData.slug || configData.name?.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "app";
|
|
175
|
+
const appName = configData.name || "App";
|
|
176
|
+
const bundleIdentifier = configData.ios?.bundleIdentifier || `com.example.${slug.replace(/-/g, "")}`;
|
|
177
|
+
const packageName = configData.android?.packageName || bundleIdentifier;
|
|
178
|
+
|
|
179
|
+
const generator = new TemplateGenerator({
|
|
180
|
+
projectRoot,
|
|
181
|
+
variables: {
|
|
182
|
+
slug,
|
|
183
|
+
appName,
|
|
184
|
+
bundleIdentifier,
|
|
185
|
+
version: configData.version || "1.0.0",
|
|
186
|
+
buildNumber: configData.ios?.buildNumber || 1,
|
|
187
|
+
packageName,
|
|
188
|
+
versionCode: configData.android?.versionCode || 1,
|
|
189
|
+
versionName: configData.version || "1.0.0",
|
|
190
|
+
cliVersion: `^${getCliVersion()}`,
|
|
191
|
+
},
|
|
192
|
+
force: options.force,
|
|
193
|
+
dryRun: options.dryRun,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const configFilesUpdated: string[] = [];
|
|
197
|
+
const configFilesSkipped: string[] = [];
|
|
198
|
+
|
|
199
|
+
for (const file of UPGRADABLE_CONFIG_FILES) {
|
|
200
|
+
const filePath = path.join(projectRoot, file);
|
|
201
|
+
const exists = fs.existsSync(filePath);
|
|
202
|
+
|
|
203
|
+
if (exists && !options.force) {
|
|
204
|
+
configFilesSkipped.push(file);
|
|
205
|
+
console.log(chalk.gray(` ${file}: skipped (use --force to overwrite)`));
|
|
206
|
+
} else {
|
|
207
|
+
if (!options.dryRun) {
|
|
208
|
+
const result = await generator.generateRootConfigFiles();
|
|
209
|
+
if (result.filesCreated.some((f) => f.endsWith(file))) {
|
|
210
|
+
configFilesUpdated.push(file);
|
|
211
|
+
console.log(chalk.green(` ${file}: updated`));
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
console.log(chalk.cyan(` ${file}: would be updated`));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Run bun install
|
|
221
|
+
if (!options.skipInstall && !options.dryRun && updates.length > 0) {
|
|
222
|
+
const installSpinner = ora("Installing updated dependencies...").start();
|
|
223
|
+
await new Promise<void>((resolve, reject) => {
|
|
224
|
+
const child = spawn("bun", ["install"], {
|
|
225
|
+
cwd: projectRoot,
|
|
226
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
let stderr = "";
|
|
230
|
+
child.stderr?.on("data", (data) => {
|
|
231
|
+
stderr += data.toString();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
child.on("close", (code) => {
|
|
235
|
+
if (code === 0) {
|
|
236
|
+
installSpinner.succeed("Dependencies installed");
|
|
237
|
+
resolve();
|
|
238
|
+
} else {
|
|
239
|
+
installSpinner.fail("Failed to install dependencies");
|
|
240
|
+
console.error(chalk.red(stderr));
|
|
241
|
+
reject(new Error(`bun install exited with code ${code}`));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
child.on("error", (err) => {
|
|
246
|
+
installSpinner.fail("Failed to run bun install");
|
|
247
|
+
reject(err);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Summary
|
|
253
|
+
if (options.dryRun) {
|
|
254
|
+
console.log(chalk.cyan("\n[Dry run] No changes were made."));
|
|
255
|
+
} else if (updates.length > 0) {
|
|
256
|
+
console.log(chalk.green("\nUpgrade complete!"));
|
|
257
|
+
console.log(chalk.gray("\nNext steps:"));
|
|
258
|
+
console.log(chalk.cyan(" 1. teardown prebuild --clean") + chalk.gray(" - Regenerate native projects"));
|
|
259
|
+
console.log(chalk.cyan(" 2. teardown run ios") + chalk.gray(" or ") + chalk.cyan("teardown run android"));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return command;
|
|
264
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { createInitCommand } from "./commands/init";
|
|
|
8
8
|
import { createPluginsCommand } from "./commands/plugins";
|
|
9
9
|
import { createPrebuildCommand } from "./commands/prebuild";
|
|
10
10
|
import { createRunCommand } from "./commands/run";
|
|
11
|
+
import { createUpgradeCommand } from "./commands/upgrade";
|
|
11
12
|
import { createValidateCommand } from "./commands/validate";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -28,6 +29,7 @@ export function createProgram(): Command {
|
|
|
28
29
|
program.addCommand(createPrebuildCommand());
|
|
29
30
|
program.addCommand(createValidateCommand());
|
|
30
31
|
program.addCommand(createInitCommand());
|
|
32
|
+
program.addCommand(createUpgradeCommand());
|
|
31
33
|
program.addCommand(createPluginsCommand());
|
|
32
34
|
program.addCommand(createRunCommand());
|
|
33
35
|
program.addCommand(createDevCommand());
|
|
@@ -56,4 +58,5 @@ export { createPluginsCommand } from "./commands/plugins";
|
|
|
56
58
|
// Export commands for testing
|
|
57
59
|
export { createPrebuildCommand } from "./commands/prebuild";
|
|
58
60
|
export { createRunCommand } from "./commands/run";
|
|
61
|
+
export { createUpgradeCommand } from "./commands/upgrade";
|
|
59
62
|
export { createValidateCommand } from "./commands/validate";
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* The app is wrapped with DevClientProvider for development tools.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { AppRegistry } from "react-native";
|
|
9
8
|
import { DevClientProvider } from "@teardown/dev-client";
|
|
10
|
-
import {
|
|
9
|
+
import { AppRegistry } from "react-native";
|
|
10
|
+
import { devClientConfig } from "../dev-client.config";
|
|
11
11
|
import { App } from "./app";
|
|
12
12
|
|
|
13
13
|
/**
|