@teardown/cli 1.2.38 → 2.0.41
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/bin/teardown.js +11 -1
- package/package.json +77 -57
- package/src/cli/commands/init.ts +254 -0
- package/src/cli/commands/plugins.ts +93 -0
- package/src/cli/commands/prebuild.ts +168 -0
- package/src/cli/commands/run.ts +727 -0
- package/src/cli/commands/start.ts +87 -0
- package/src/cli/commands/validate.ts +62 -0
- package/src/cli/index.ts +59 -0
- package/src/config/index.ts +45 -0
- package/src/config/loader.ts +366 -0
- package/src/config/schema.ts +235 -0
- package/src/config/types.ts +322 -0
- package/src/index.ts +177 -0
- package/src/pipeline/cache.ts +179 -0
- package/src/pipeline/index.ts +10 -0
- package/src/pipeline/stages.ts +692 -0
- package/src/plugins/base.ts +370 -0
- package/src/plugins/capabilities/biometrics.ts +64 -0
- package/src/plugins/capabilities/bluetooth.ts +86 -0
- package/src/plugins/capabilities/calendar.ts +57 -0
- package/src/plugins/capabilities/camera.ts +77 -0
- package/src/plugins/capabilities/contacts.ts +57 -0
- package/src/plugins/capabilities/deep-linking.ts +124 -0
- package/src/plugins/capabilities/firebase.ts +138 -0
- package/src/plugins/capabilities/index.ts +96 -0
- package/src/plugins/capabilities/location.ts +87 -0
- package/src/plugins/capabilities/photo-library.ts +80 -0
- package/src/plugins/capabilities/push-notifications.ts +98 -0
- package/src/plugins/capabilities/sign-in-with-apple.ts +53 -0
- package/src/plugins/context.ts +220 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/resolver.ts +321 -0
- package/src/templates/generator.ts +507 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/paths.ts +25 -0
- package/src/transformers/android/gradle.ts +400 -0
- package/src/transformers/android/index.ts +19 -0
- package/src/transformers/android/manifest.ts +506 -0
- package/src/transformers/index.ts +39 -0
- package/src/transformers/ios/entitlements.ts +283 -0
- package/src/transformers/ios/index.ts +10 -0
- package/src/transformers/ios/pbxproj.ts +267 -0
- package/src/transformers/ios/plist.ts +198 -0
- package/src/utils/fs.ts +429 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/logger.ts +203 -0
- package/templates/.gitignore +63 -0
- package/templates/Gemfile +3 -0
- package/templates/android/app/build.gradle.kts +97 -0
- package/templates/android/app/proguard-rules.pro +10 -0
- package/templates/android/app/src/main/AndroidManifest.xml +26 -0
- package/templates/android/app/src/main/java/com/appname/MainActivity.kt +22 -0
- package/templates/android/app/src/main/java/com/appname/MainApplication.kt +44 -0
- package/templates/android/app/src/main/res/values/strings.xml +3 -0
- package/templates/android/app/src/main/res/values/styles.xml +7 -0
- package/templates/android/build.gradle.kts +44 -0
- package/templates/android/gradle.properties +39 -0
- package/templates/android/settings.gradle.kts +12 -0
- package/templates/babel.config.js +15 -0
- package/templates/index.js +7 -0
- package/templates/ios/.xcode.env +11 -0
- package/templates/ios/AppName/AppDelegate.swift +25 -0
- package/templates/ios/AppName/AppName-Bridging-Header.h +4 -0
- package/templates/ios/AppName/AppName.entitlements +6 -0
- package/templates/ios/AppName/Images.xcassets/AppIcon.appiconset/Contents.json +35 -0
- package/templates/ios/AppName/Images.xcassets/Contents.json +6 -0
- package/templates/ios/AppName/Info.plist +49 -0
- package/templates/ios/AppName/LaunchScreen.storyboard +38 -0
- package/templates/ios/AppName.xcodeproj/project.pbxproj +402 -0
- package/templates/ios/AppName.xcodeproj/xcshareddata/xcschemes/AppName.xcscheme +78 -0
- package/templates/ios/Podfile +35 -0
- package/templates/metro.config.js +41 -0
- package/templates/package.json +57 -0
- package/templates/react-native.config.js +8 -0
- package/templates/src/app/index.tsx +34 -0
- package/templates/src/assets/fonts/.gitkeep +1 -0
- package/templates/src/assets/images/.gitkeep +1 -0
- package/templates/src/components/ui/accordion.tsx +114 -0
- package/templates/src/components/ui/avatar.tsx +75 -0
- package/templates/src/components/ui/button.tsx +93 -0
- package/templates/src/components/ui/card.tsx +120 -0
- package/templates/src/components/ui/checkbox.tsx +133 -0
- package/templates/src/components/ui/chip.tsx +95 -0
- package/templates/src/components/ui/dialog.tsx +134 -0
- package/templates/src/components/ui/divider.tsx +67 -0
- package/templates/src/components/ui/error-view.tsx +82 -0
- package/templates/src/components/ui/form-field.tsx +101 -0
- package/templates/src/components/ui/index.ts +100 -0
- package/templates/src/components/ui/popover.tsx +92 -0
- package/templates/src/components/ui/pressable-feedback.tsx +88 -0
- package/templates/src/components/ui/radio-group.tsx +153 -0
- package/templates/src/components/ui/scroll-shadow.tsx +108 -0
- package/templates/src/components/ui/select.tsx +165 -0
- package/templates/src/components/ui/skeleton-group.tsx +97 -0
- package/templates/src/components/ui/skeleton.tsx +87 -0
- package/templates/src/components/ui/spinner.tsx +87 -0
- package/templates/src/components/ui/surface.tsx +95 -0
- package/templates/src/components/ui/switch.tsx +124 -0
- package/templates/src/components/ui/tabs.tsx +154 -0
- package/templates/src/components/ui/text-field.tsx +106 -0
- package/templates/src/components/ui/toast.tsx +129 -0
- package/templates/src/contexts/.gitkeep +2 -0
- package/templates/src/core/clients/api/api.client.ts +113 -0
- package/templates/src/core/clients/api/index.ts +1 -0
- package/templates/src/core/clients/storage/index.ts +1 -0
- package/templates/src/core/clients/storage/storage.client.ts +121 -0
- package/templates/src/core/constants/index.ts +19 -0
- package/templates/src/core/core.ts +40 -0
- package/templates/src/core/index.ts +10 -0
- package/templates/src/global.css +87 -0
- package/templates/src/hooks/index.ts +6 -0
- package/templates/src/hooks/use-debounce.ts +23 -0
- package/templates/src/hooks/use-mounted.ts +21 -0
- package/templates/src/index.ts +28 -0
- package/templates/src/lib/index.ts +5 -0
- package/templates/src/lib/utils.ts +115 -0
- package/templates/src/modules/.gitkeep +6 -0
- package/templates/src/navigation/index.ts +8 -0
- package/templates/src/navigation/navigation-provider.tsx +36 -0
- package/templates/src/navigation/router.tsx +137 -0
- package/templates/src/providers/app.provider.tsx +29 -0
- package/templates/src/providers/index.ts +5 -0
- package/templates/src/routes/(tabs)/_layout.tsx +42 -0
- package/templates/src/routes/(tabs)/explore.tsx +161 -0
- package/templates/src/routes/(tabs)/home.tsx +138 -0
- package/templates/src/routes/(tabs)/profile.tsx +151 -0
- package/templates/src/routes/_layout.tsx +18 -0
- package/templates/src/routes/settings.tsx +194 -0
- package/templates/src/screens/auth/index.ts +6 -0
- package/templates/src/screens/auth/login.tsx +165 -0
- package/templates/src/screens/auth/register.tsx +203 -0
- package/templates/src/screens/home.tsx +204 -0
- package/templates/src/screens/index.ts +17 -0
- package/templates/src/screens/profile.tsx +210 -0
- package/templates/src/screens/settings.tsx +216 -0
- package/templates/src/screens/welcome.tsx +101 -0
- package/templates/src/styles/index.ts +103 -0
- package/templates/src/types/common.ts +71 -0
- package/templates/src/types/index.ts +5 -0
- package/templates/tsconfig.json +14 -0
- package/README.md +0 -15
- package/assets/favicon.ico +0 -0
- package/dist/commands/dev/dev.js +0 -55
- package/dist/commands/init/init-teardown.js +0 -26
- package/dist/index.js +0 -20
- package/dist/modules/dev/dev-menu/keyboard-handler.js +0 -138
- package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js +0 -105
- package/dist/modules/dev/dev-server/cdp/cdp.adapter.js +0 -12
- package/dist/modules/dev/dev-server/cdp/index.js +0 -18
- package/dist/modules/dev/dev-server/cdp/types.js +0 -2
- package/dist/modules/dev/dev-server/dev-server-checker.js +0 -72
- package/dist/modules/dev/dev-server/dev-server.js +0 -269
- package/dist/modules/dev/dev-server/inspector/device.event-reporter.js +0 -165
- package/dist/modules/dev/dev-server/inspector/device.js +0 -577
- package/dist/modules/dev/dev-server/inspector/inspector.js +0 -204
- package/dist/modules/dev/dev-server/inspector/types.js +0 -2
- package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js +0 -61
- package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js +0 -64
- package/dist/modules/dev/dev-server/plugins/devtools.plugin.js +0 -50
- package/dist/modules/dev/dev-server/plugins/favicon.plugin.js +0 -19
- package/dist/modules/dev/dev-server/plugins/multipart.plugin.js +0 -62
- package/dist/modules/dev/dev-server/plugins/systrace.plugin.js +0 -28
- package/dist/modules/dev/dev-server/plugins/types.js +0 -2
- package/dist/modules/dev/dev-server/plugins/wss/index.js +0 -19
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js +0 -66
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js +0 -128
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js +0 -75
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js +0 -198
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js +0 -120
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js +0 -357
- package/dist/modules/dev/dev-server/plugins/wss/types.js +0 -2
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js +0 -57
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js +0 -26
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js +0 -46
- package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js +0 -55
- package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js +0 -36
- package/dist/modules/dev/dev-server/sybmolicate/types.js +0 -2
- package/dist/modules/dev/terminal/base.terminal.reporter.js +0 -78
- package/dist/modules/dev/terminal/terminal.reporter.js +0 -76
- package/dist/modules/dev/types.js +0 -2
- package/dist/modules/dev/utils/log.js +0 -73
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start command - starts the Metro bundler
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if a metro.config.js file exists in the project
|
|
13
|
+
*/
|
|
14
|
+
function hasMetroConfig(projectRoot: string): boolean {
|
|
15
|
+
const configPaths = ["metro.config.js", "metro.config.cjs", "metro.config.mjs"];
|
|
16
|
+
return configPaths.some((config) => existsSync(join(projectRoot, config)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if @teardown/metro-config is installed
|
|
21
|
+
*/
|
|
22
|
+
function hasTeardownMetroConfig(projectRoot: string): boolean {
|
|
23
|
+
const nodeModulesPath = join(projectRoot, "node_modules", "@teardown", "metro-config");
|
|
24
|
+
return existsSync(nodeModulesPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create the start command
|
|
29
|
+
*/
|
|
30
|
+
export function createStartCommand(): Command {
|
|
31
|
+
const start = new Command("start")
|
|
32
|
+
.description("Start the Metro bundler")
|
|
33
|
+
.option("-p, --port <port>", "Port to run Metro on", "8081")
|
|
34
|
+
.option("--reset-cache", "Reset the Metro cache")
|
|
35
|
+
.option("--platform <platform>", "Platform to target (ios, android, all)", "all")
|
|
36
|
+
.option("--verbose", "Enable verbose logging")
|
|
37
|
+
.action(async (options) => {
|
|
38
|
+
const projectRoot = process.cwd();
|
|
39
|
+
|
|
40
|
+
// Check for metro config
|
|
41
|
+
if (!hasMetroConfig(projectRoot)) {
|
|
42
|
+
console.log(chalk.yellow("No metro.config.js found. Using default React Native config.\n"));
|
|
43
|
+
console.log(chalk.gray("Tip: Add @teardown/metro-config for 36x faster builds:\n"));
|
|
44
|
+
console.log(
|
|
45
|
+
chalk.gray(` 1. bun add @teardown/metro-config\n 2. Create metro.config.js with withTeardown()\n`)
|
|
46
|
+
);
|
|
47
|
+
} else if (!hasTeardownMetroConfig(projectRoot)) {
|
|
48
|
+
console.log(chalk.gray("Tip: Add @teardown/metro-config for 36x faster builds\n"));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(chalk.blue("Starting Metro bundler...\n"));
|
|
52
|
+
|
|
53
|
+
const args = ["react-native", "start", "--port", options.port];
|
|
54
|
+
|
|
55
|
+
if (options.resetCache) {
|
|
56
|
+
args.push("--reset-cache");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.verbose) {
|
|
60
|
+
args.push("--verbose");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const proc = spawn("npx", args, {
|
|
64
|
+
stdio: "inherit",
|
|
65
|
+
shell: true,
|
|
66
|
+
cwd: projectRoot,
|
|
67
|
+
env: {
|
|
68
|
+
...process.env,
|
|
69
|
+
// Ensure proper encoding for CocoaPods compatibility
|
|
70
|
+
LANG: "en_US.UTF-8",
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
proc.on("error", (error) => {
|
|
75
|
+
console.error(chalk.red(`Failed to start Metro: ${error.message}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
proc.on("close", (code) => {
|
|
80
|
+
if (code !== 0 && code !== null) {
|
|
81
|
+
process.exit(code);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return start;
|
|
87
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { loadConfig } from "../../config";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create the validate command
|
|
8
|
+
*/
|
|
9
|
+
export function createValidateCommand(): Command {
|
|
10
|
+
const command = new Command("validate").description("Validate the Teardown configuration file").action(async () => {
|
|
11
|
+
const spinner = ora("Validating configuration...").start();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
const result = await loadConfig(projectRoot);
|
|
16
|
+
|
|
17
|
+
if (!result.success) {
|
|
18
|
+
spinner.fail("Configuration validation failed");
|
|
19
|
+
|
|
20
|
+
console.error(chalk.red("\nErrors:"));
|
|
21
|
+
for (const error of result.errors ?? []) {
|
|
22
|
+
console.error(chalk.red(` - ${error.path || "config"}: ${error.message}`));
|
|
23
|
+
if (error.suggestion) {
|
|
24
|
+
console.error(chalk.gray(` Suggestion: ${error.suggestion}`));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
spinner.succeed("Configuration is valid");
|
|
31
|
+
|
|
32
|
+
console.log(chalk.green("\nConfiguration summary:"));
|
|
33
|
+
console.log(chalk.gray(` Name: ${result.config?.name}`));
|
|
34
|
+
console.log(chalk.gray(` Slug: ${result.config?.slug}`));
|
|
35
|
+
console.log(chalk.gray(` Version: ${result.config?.version}`));
|
|
36
|
+
|
|
37
|
+
if (result.config?.ios) {
|
|
38
|
+
console.log(chalk.blue("\n iOS:"));
|
|
39
|
+
console.log(chalk.gray(` Bundle ID: ${result.config?.ios.bundleIdentifier}`));
|
|
40
|
+
console.log(chalk.gray(` Deployment Target: ${result.config?.ios.deploymentTarget}`));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (result.config?.android) {
|
|
44
|
+
console.log(chalk.green("\n Android:"));
|
|
45
|
+
console.log(chalk.gray(` Package: ${result.config?.android.packageName}`));
|
|
46
|
+
console.log(chalk.gray(` Min SDK: ${result.config?.android.minSdkVersion}`));
|
|
47
|
+
console.log(chalk.gray(` Target SDK: ${result.config?.android.targetSdkVersion}`));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pluginCount = result.config?.plugins?.length ?? 0;
|
|
51
|
+
if (pluginCount > 0) {
|
|
52
|
+
console.log(chalk.cyan(`\n Plugins: ${pluginCount}`));
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
spinner.fail("Validation failed");
|
|
56
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return command;
|
|
62
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI for Teardown Launchpad
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { createInitCommand } from "./commands/init";
|
|
7
|
+
import { createPluginsCommand } from "./commands/plugins";
|
|
8
|
+
import { createPrebuildCommand } from "./commands/prebuild";
|
|
9
|
+
import { createRunCommand } from "./commands/run";
|
|
10
|
+
import { createStartCommand } from "./commands/start";
|
|
11
|
+
import { createValidateCommand } from "./commands/validate";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Package version (injected at build time)
|
|
15
|
+
*/
|
|
16
|
+
const VERSION = "0.0.1";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create and configure the CLI program
|
|
20
|
+
*/
|
|
21
|
+
export function createProgram(): Command {
|
|
22
|
+
const program = new Command()
|
|
23
|
+
.name("teardown")
|
|
24
|
+
.description("Teardown CLI - React Native Development Toolkit")
|
|
25
|
+
.version(VERSION);
|
|
26
|
+
|
|
27
|
+
// Add commands
|
|
28
|
+
program.addCommand(createPrebuildCommand());
|
|
29
|
+
program.addCommand(createValidateCommand());
|
|
30
|
+
program.addCommand(createInitCommand());
|
|
31
|
+
program.addCommand(createPluginsCommand());
|
|
32
|
+
program.addCommand(createRunCommand());
|
|
33
|
+
program.addCommand(createStartCommand());
|
|
34
|
+
|
|
35
|
+
// Default command (prebuild)
|
|
36
|
+
program.argument("[command]").action((cmd) => {
|
|
37
|
+
if (!cmd) {
|
|
38
|
+
program.help();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return program;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run the CLI
|
|
47
|
+
*/
|
|
48
|
+
export async function run(args: string[] = process.argv): Promise<void> {
|
|
49
|
+
const program = createProgram();
|
|
50
|
+
await program.parseAsync(args);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { createInitCommand } from "./commands/init";
|
|
54
|
+
export { createPluginsCommand } from "./commands/plugins";
|
|
55
|
+
// Export commands for testing
|
|
56
|
+
export { createPrebuildCommand } from "./commands/prebuild";
|
|
57
|
+
export { createRunCommand } from "./commands/run";
|
|
58
|
+
export { createStartCommand } from "./commands/start";
|
|
59
|
+
export { createValidateCommand } from "./commands/validate";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration module for Teardown Launchpad
|
|
3
|
+
*
|
|
4
|
+
* Provides configuration loading, validation, and type definitions
|
|
5
|
+
* for the prebuild system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type { ConfigLoadResult } from "./loader";
|
|
9
|
+
|
|
10
|
+
export { findConfigFile, generateDefaultConfig, loadConfig, watchConfig } from "./loader";
|
|
11
|
+
export {
|
|
12
|
+
AndroidConfigSchema,
|
|
13
|
+
defineConfig,
|
|
14
|
+
formatValidationErrors,
|
|
15
|
+
iOSConfigSchema,
|
|
16
|
+
SplashConfigSchema,
|
|
17
|
+
TeardownConfigSchema,
|
|
18
|
+
validateConfig,
|
|
19
|
+
} from "./schema";
|
|
20
|
+
|
|
21
|
+
export type {
|
|
22
|
+
AndroidConfig,
|
|
23
|
+
AndroidIntentFilter,
|
|
24
|
+
AndroidWidgetDefinition,
|
|
25
|
+
BuildConfig,
|
|
26
|
+
DryRunResult,
|
|
27
|
+
FileChange,
|
|
28
|
+
HealthConnectDataType,
|
|
29
|
+
HealthKitDataType,
|
|
30
|
+
IntentDefinition,
|
|
31
|
+
IntentParameter,
|
|
32
|
+
iOSConfig,
|
|
33
|
+
PermissionConfig,
|
|
34
|
+
PermissionMapping,
|
|
35
|
+
PermissionStatus,
|
|
36
|
+
PluginEntry,
|
|
37
|
+
PrebuildError,
|
|
38
|
+
PrebuildOptions,
|
|
39
|
+
PrebuildWarning,
|
|
40
|
+
ProcessedAppConfig,
|
|
41
|
+
SplashConfig,
|
|
42
|
+
TeardownConfig,
|
|
43
|
+
TemplateContext,
|
|
44
|
+
WidgetDefinition,
|
|
45
|
+
} from "./types";
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { Project, SyntaxKind } from "ts-morph";
|
|
4
|
+
import { type ValidationError, validateConfig } from "./schema";
|
|
5
|
+
import type { TeardownConfig } from "./types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configuration file names to search for (in order of priority)
|
|
9
|
+
*/
|
|
10
|
+
const CONFIG_FILE_NAMES = [
|
|
11
|
+
"teardown.config.ts",
|
|
12
|
+
"teardown.config.js",
|
|
13
|
+
"teardown.config.mjs",
|
|
14
|
+
"launchpad.config.ts",
|
|
15
|
+
"launchpad.config.js",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Result of loading configuration
|
|
20
|
+
*/
|
|
21
|
+
export interface ConfigLoadResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
config?: TeardownConfig;
|
|
24
|
+
configPath?: string;
|
|
25
|
+
errors?: ValidationError[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Find the configuration file in the project root
|
|
30
|
+
*/
|
|
31
|
+
export function findConfigFile(projectRoot: string): string | null {
|
|
32
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
33
|
+
const filePath = path.join(projectRoot, fileName);
|
|
34
|
+
if (fs.existsSync(filePath)) {
|
|
35
|
+
return filePath;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Load and parse the configuration file
|
|
43
|
+
*/
|
|
44
|
+
export async function loadConfig(projectRoot: string): Promise<ConfigLoadResult> {
|
|
45
|
+
const configPath = findConfigFile(projectRoot);
|
|
46
|
+
|
|
47
|
+
if (!configPath) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
path: "",
|
|
53
|
+
message: `No configuration file found. Expected one of: ${CONFIG_FILE_NAMES.join(", ")}`,
|
|
54
|
+
suggestion: "Create a teardown.config.ts file in your project root",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// For TypeScript files, we need to transpile first
|
|
62
|
+
if (configPath.endsWith(".ts")) {
|
|
63
|
+
return await loadTypeScriptConfig(configPath);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// For JavaScript files, we can import directly
|
|
67
|
+
const module = await import(configPath);
|
|
68
|
+
const config = module.default || module;
|
|
69
|
+
|
|
70
|
+
const validation = validateConfig(config);
|
|
71
|
+
|
|
72
|
+
if (!validation.success) {
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
configPath,
|
|
76
|
+
errors: validation.errors,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
config: validation.data,
|
|
83
|
+
configPath,
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
configPath,
|
|
89
|
+
errors: [
|
|
90
|
+
{
|
|
91
|
+
path: "",
|
|
92
|
+
message: `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Load TypeScript configuration file
|
|
101
|
+
*/
|
|
102
|
+
async function loadTypeScriptConfig(configPath: string): Promise<ConfigLoadResult> {
|
|
103
|
+
try {
|
|
104
|
+
// Use Bun's native TypeScript support if available
|
|
105
|
+
if (typeof Bun !== "undefined") {
|
|
106
|
+
// Add cache-busting query param to force fresh import
|
|
107
|
+
const cacheBuster = `?t=${Date.now()}`;
|
|
108
|
+
const module = await import(configPath + cacheBuster);
|
|
109
|
+
const config = module.default || module;
|
|
110
|
+
|
|
111
|
+
const validation = validateConfig(config);
|
|
112
|
+
|
|
113
|
+
if (!validation.success) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
configPath,
|
|
117
|
+
errors: validation.errors,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
config: validation.data,
|
|
124
|
+
configPath,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Fallback: Parse with ts-morph for analysis
|
|
129
|
+
const project = new Project({
|
|
130
|
+
compilerOptions: {
|
|
131
|
+
target: 99, // ESNext
|
|
132
|
+
module: 99, // ESNext
|
|
133
|
+
moduleResolution: 100, // Bundler
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const sourceFile = project.addSourceFileAtPath(configPath);
|
|
138
|
+
|
|
139
|
+
// Find the default export
|
|
140
|
+
const defaultExport = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
141
|
+
|
|
142
|
+
if (!defaultExport) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
configPath,
|
|
146
|
+
errors: [
|
|
147
|
+
{
|
|
148
|
+
path: "",
|
|
149
|
+
message: "Configuration file must have a default export",
|
|
150
|
+
suggestion: "Add 'export default defineConfig({ ... })' to your config file",
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Get the expression being exported
|
|
157
|
+
const expression = defaultExport.getExpression();
|
|
158
|
+
|
|
159
|
+
// If it's a call expression (defineConfig(...)), get the argument
|
|
160
|
+
let configObject: unknown;
|
|
161
|
+
|
|
162
|
+
if (expression.getKind() === SyntaxKind.CallExpression) {
|
|
163
|
+
const callExpr = expression.asKind(SyntaxKind.CallExpression);
|
|
164
|
+
const args = callExpr?.getArguments();
|
|
165
|
+
|
|
166
|
+
if (args && args.length > 0) {
|
|
167
|
+
const firstArg = args[0];
|
|
168
|
+
if (firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
|
169
|
+
configObject = evaluateObjectLiteral(firstArg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else if (expression.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
|
173
|
+
configObject = evaluateObjectLiteral(expression);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!configObject) {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
configPath,
|
|
180
|
+
errors: [
|
|
181
|
+
{
|
|
182
|
+
path: "",
|
|
183
|
+
message: "Could not parse configuration object",
|
|
184
|
+
suggestion: "Ensure your config exports a plain object or uses defineConfig()",
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const validation = validateConfig(configObject);
|
|
191
|
+
|
|
192
|
+
if (!validation.success) {
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
configPath,
|
|
196
|
+
errors: validation.errors,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
config: validation.data,
|
|
203
|
+
configPath,
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
configPath,
|
|
209
|
+
errors: [
|
|
210
|
+
{
|
|
211
|
+
path: "",
|
|
212
|
+
message: `Failed to parse TypeScript config: ${error instanceof Error ? error.message : String(error)}`,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Evaluate an object literal expression to a JavaScript object
|
|
221
|
+
* This is a simplified evaluator that handles common cases
|
|
222
|
+
*/
|
|
223
|
+
function evaluateObjectLiteral(node: import("ts-morph").Node): Record<string, unknown> {
|
|
224
|
+
const obj: Record<string, unknown> = {};
|
|
225
|
+
const objLiteral = node.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
226
|
+
|
|
227
|
+
if (!objLiteral) {
|
|
228
|
+
return obj;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const prop of objLiteral.getProperties()) {
|
|
232
|
+
if (prop.getKind() === SyntaxKind.PropertyAssignment) {
|
|
233
|
+
const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
234
|
+
if (!propAssign) continue;
|
|
235
|
+
|
|
236
|
+
const name = propAssign.getName();
|
|
237
|
+
const initializer = propAssign.getInitializer();
|
|
238
|
+
|
|
239
|
+
if (!initializer) continue;
|
|
240
|
+
|
|
241
|
+
obj[name] = evaluateExpression(initializer);
|
|
242
|
+
} else if (prop.getKind() === SyntaxKind.ShorthandPropertyAssignment) {
|
|
243
|
+
const shorthand = prop.asKind(SyntaxKind.ShorthandPropertyAssignment);
|
|
244
|
+
if (!shorthand) continue;
|
|
245
|
+
|
|
246
|
+
const name = shorthand.getName();
|
|
247
|
+
// For shorthand, we can't easily resolve the value without runtime
|
|
248
|
+
obj[name] = undefined;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return obj;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Evaluate a simple expression to a JavaScript value
|
|
257
|
+
*/
|
|
258
|
+
function evaluateExpression(node: import("ts-morph").Node): unknown {
|
|
259
|
+
const kind = node.getKind();
|
|
260
|
+
|
|
261
|
+
switch (kind) {
|
|
262
|
+
case SyntaxKind.StringLiteral:
|
|
263
|
+
return node.asKind(SyntaxKind.StringLiteral)?.getLiteralValue();
|
|
264
|
+
|
|
265
|
+
case SyntaxKind.NumericLiteral:
|
|
266
|
+
return node.asKind(SyntaxKind.NumericLiteral)?.getLiteralValue();
|
|
267
|
+
|
|
268
|
+
case SyntaxKind.TrueKeyword:
|
|
269
|
+
return true;
|
|
270
|
+
|
|
271
|
+
case SyntaxKind.FalseKeyword:
|
|
272
|
+
return false;
|
|
273
|
+
|
|
274
|
+
case SyntaxKind.NullKeyword:
|
|
275
|
+
return null;
|
|
276
|
+
|
|
277
|
+
case SyntaxKind.ObjectLiteralExpression:
|
|
278
|
+
return evaluateObjectLiteral(node);
|
|
279
|
+
|
|
280
|
+
case SyntaxKind.ArrayLiteralExpression: {
|
|
281
|
+
const arrayLiteral = node.asKind(SyntaxKind.ArrayLiteralExpression);
|
|
282
|
+
if (!arrayLiteral) return [];
|
|
283
|
+
return arrayLiteral.getElements().map((el) => evaluateExpression(el));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
default:
|
|
287
|
+
// For complex expressions, return the text representation
|
|
288
|
+
return node.getText();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Watch configuration file for changes
|
|
294
|
+
*/
|
|
295
|
+
export function watchConfig(projectRoot: string, onChange: (result: ConfigLoadResult) => void): () => void {
|
|
296
|
+
const configPath = findConfigFile(projectRoot);
|
|
297
|
+
|
|
298
|
+
if (!configPath) {
|
|
299
|
+
onChange({
|
|
300
|
+
success: false,
|
|
301
|
+
errors: [
|
|
302
|
+
{
|
|
303
|
+
path: "",
|
|
304
|
+
message: "No configuration file found",
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
return () => {};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const watcher = fs.watch(configPath, async (eventType) => {
|
|
312
|
+
if (eventType === "change") {
|
|
313
|
+
const result = await loadConfig(projectRoot);
|
|
314
|
+
onChange(result);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return () => watcher.close();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Generate a default configuration file
|
|
323
|
+
*/
|
|
324
|
+
export function generateDefaultConfig(options: {
|
|
325
|
+
name: string;
|
|
326
|
+
slug: string;
|
|
327
|
+
bundleIdentifier?: string;
|
|
328
|
+
packageName?: string;
|
|
329
|
+
}): string {
|
|
330
|
+
const { name, slug } = options;
|
|
331
|
+
const bundleIdentifier = options.bundleIdentifier || `com.example.${slug.replace(/-/g, "")}`;
|
|
332
|
+
const packageName = options.packageName || bundleIdentifier;
|
|
333
|
+
|
|
334
|
+
return `import { defineConfig } from "@teardown/cli";
|
|
335
|
+
|
|
336
|
+
export default defineConfig({
|
|
337
|
+
name: '${name}',
|
|
338
|
+
slug: '${slug}',
|
|
339
|
+
version: '1.0.0',
|
|
340
|
+
|
|
341
|
+
ios: {
|
|
342
|
+
bundleIdentifier: '${bundleIdentifier}',
|
|
343
|
+
buildNumber: 1,
|
|
344
|
+
deploymentTarget: '15.0',
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
android: {
|
|
348
|
+
packageName: '${packageName}',
|
|
349
|
+
versionCode: 1,
|
|
350
|
+
minSdkVersion: 23,
|
|
351
|
+
targetSdkVersion: 34,
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
icon: './assets/icon.png',
|
|
355
|
+
|
|
356
|
+
splash: {
|
|
357
|
+
image: './assets/splash.png',
|
|
358
|
+
backgroundColor: '#FFFFFF',
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
plugins: [
|
|
362
|
+
// Add your plugins here
|
|
363
|
+
],
|
|
364
|
+
});
|
|
365
|
+
`;
|
|
366
|
+
}
|