@tamagui/cli 2.7.7 → 3.0.0-beta.1097.1

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.
Files changed (46) hide show
  1. package/dist/add.cjs +76 -87
  2. package/dist/build.cjs +425 -278
  3. package/dist/cli.cjs +361 -340
  4. package/dist/generate-prompt.cjs +484 -362
  5. package/dist/generate.cjs +45 -54
  6. package/dist/index.cjs +1 -1
  7. package/dist/migrate.cjs +437 -0
  8. package/dist/setup-prompt.cjs +216 -0
  9. package/dist/to-tailwind-default-config.cjs +789 -0
  10. package/dist/to-tailwind.cjs +213 -0
  11. package/dist/update-template.cjs +43 -52
  12. package/dist/update.cjs +14 -18
  13. package/dist/upgrade.cjs +417 -401
  14. package/dist/utils.cjs +84 -101
  15. package/metro.cjs +1 -0
  16. package/metro.d.ts +1 -0
  17. package/metro.mjs +1 -0
  18. package/package.json +43 -13
  19. package/src/build.ts +594 -362
  20. package/src/cli.ts +113 -12
  21. package/src/generate-prompt.ts +428 -329
  22. package/src/migrate.ts +422 -0
  23. package/src/setup-prompt.ts +192 -0
  24. package/src/to-tailwind-default-config.ts +767 -0
  25. package/src/to-tailwind.ts +313 -0
  26. package/src/upgrade.ts +30 -33
  27. package/src/utils.ts +11 -9
  28. package/types/add.d.ts +1 -1
  29. package/types/add.d.ts.map +1 -1
  30. package/types/build.d.ts +2 -1
  31. package/types/build.d.ts.map +1 -1
  32. package/types/generate-prompt.d.ts +6 -2
  33. package/types/generate-prompt.d.ts.map +1 -1
  34. package/types/migrate.d.ts +7 -0
  35. package/types/migrate.d.ts.map +1 -0
  36. package/types/setup-prompt.d.ts +5 -0
  37. package/types/setup-prompt.d.ts.map +1 -0
  38. package/types/to-tailwind-default-config.d.ts +53 -0
  39. package/types/to-tailwind-default-config.d.ts.map +1 -0
  40. package/types/to-tailwind.d.ts +16 -0
  41. package/types/to-tailwind.d.ts.map +1 -0
  42. package/types/upgrade.d.ts.map +1 -1
  43. package/types/utils.d.ts.map +1 -1
  44. package/vite.cjs +1 -0
  45. package/vite.d.ts +1 -0
  46. package/vite.mjs +1 -0
package/dist/build.cjs CHANGED
@@ -5,36 +5,29 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: true
11
- });
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
12
  };
13
13
  var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
- get: () => from[key],
17
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
- });
19
- }
20
- return to;
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: () => from[key],
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
21
  };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
25
- // compatible transform (i.e. "__esModule" has not been set), then set
26
- // "default" to the CommonJS "module.exports" for node compatibility.
27
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
- value: mod,
29
- enumerable: true
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
30
25
  }) : target, mod));
31
- var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
32
- value: true
33
- }), mod);
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
27
  var build_exports = {};
35
28
  __export(build_exports, {
36
- build: () => build,
37
- insertCssImport: () => insertCssImport
29
+ build: () => build,
30
+ insertCssImport: () => insertCssImport
38
31
  });
39
32
  module.exports = __toCommonJS(build_exports);
40
33
  var import_static = require("@tamagui/static");
@@ -45,270 +38,424 @@ var import_node_path = require("node:path");
45
38
  var import_node_os = require("node:os");
46
39
  var import_node_child_process = require("node:child_process");
47
40
  var import_node_crypto = require("node:crypto");
41
+ var import_node_module = require("node:module");
42
+ var import_typescript = require("typescript");
43
+ const import_meta = {};
44
+ const cliVersion = (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : import_meta.url)("@tamagui/cli/package.json").version;
48
45
  function insertCssImport(jsContent, cssImport) {
49
- const directiveMatch = jsContent.match(/^(['"])use (client|server)\1;?\n?/);
50
- if (directiveMatch) {
51
- const directive = directiveMatch[0];
52
- return `${directive}${cssImport}
46
+ const directiveMatch = jsContent.match(/^(['"])use (client|server)\1;?\n?/);
47
+ if (directiveMatch) {
48
+ const directive = directiveMatch[0];
49
+ return `${directive}${cssImport}
53
50
  ${jsContent.slice(directive.length)}`;
54
- }
55
- return `${cssImport}
51
+ }
52
+ return `${cssImport}
56
53
  ${jsContent}`;
57
54
  }
58
- const build = async options => {
59
- const sourceDir = options.dir ?? ".";
60
- const outputDir = options.output;
61
- const outputAround = options.outputAround || false;
62
- const promises = [];
63
- const isDryRun = options.dryRun || false;
64
- if (isDryRun) console.info("[dry-run] no files will be written\n");
65
- if (outputDir) await (0, import_fs_extra.mkdir)(outputDir, {
66
- recursive: true
67
- });
68
- const loadedOptions = (0, import_static.loadTamaguiBuildConfigSync)(options.tamaguiOptions);
69
- if (loadedOptions.disable) console.warn(`[tamagui] Note: "disable" option in tamagui.build.ts is being ignored for CLI build command`);
70
- const buildOptions = {
71
- ...loadedOptions,
72
- disable: false,
73
- disableExtraction: false
74
- };
75
- const targets = options.target === "both" || !options.target ? ["web", "native"] : [options.target];
76
- const webTamaguiOptions = {
77
- ...buildOptions,
78
- platform: "web"
79
- };
80
- await (0, import_static.loadTamagui)(webTamaguiOptions);
81
- const allFiles = [];
82
- const watchPattern = sourceDir.match(/\.(tsx|jsx)$/) ? sourceDir : `${sourceDir}/**/*.{tsx,jsx}`;
83
- await new Promise(res => {
84
- const watcher = import_chokidar.default.watch(watchPattern, {
85
- ignoreInitial: false
86
- });
87
- watcher.on("add", relativePath => {
88
- const sourcePath = (0, import_node_path.resolve)(process.cwd(), relativePath);
89
- if (options.exclude && import_micromatch.default.contains(relativePath, options.exclude)) return;
90
- if (options.include && !import_micromatch.default.contains(relativePath, options.include)) return;
91
- allFiles.push(sourcePath);
92
- }).on("ready", () => {
93
- watcher.close().then(() => res());
94
- });
95
- });
96
- const fileToTargets = /* @__PURE__ */new Map();
97
- for (const sourcePath of allFiles) {
98
- const platformMatch = sourcePath.match(/\.(web|native|ios|android)\.(tsx|jsx)$/);
99
- let filePlatforms = [];
100
- if (platformMatch) {
101
- const platform = platformMatch[1];
102
- if (platform === "web") filePlatforms = ["web"];else if (platform === "native" || platform === "ios" || platform === "android") filePlatforms = ["native"];
103
- } else {
104
- const basePath = sourcePath.replace(/\.(tsx|jsx)$/, "");
105
- const hasNative = allFiles.some(f => f === `${basePath}.native.tsx` || f === `${basePath}.native.jsx` || f === `${basePath}.ios.tsx` || f === `${basePath}.ios.jsx` || f === `${basePath}.android.tsx` || f === `${basePath}.android.jsx`);
106
- const hasWeb = allFiles.some(f => f === `${basePath}.web.tsx` || f === `${basePath}.web.jsx`);
107
- filePlatforms = targets.filter(target => {
108
- if (target === "native" && hasNative) return false;
109
- if (target === "web" && hasWeb) return false;
110
- return true;
111
- });
112
- if (hasWeb && hasNative) filePlatforms = [];
113
- }
114
- if (filePlatforms.length > 0) fileToTargets.set(sourcePath, filePlatforms);
115
- }
116
- const stats = {
117
- filesProcessed: 0,
118
- optimized: 0,
119
- flattened: 0,
120
- styled: 0,
121
- found: 0
122
- };
123
- const trackedFiles = [];
124
- const restoreDir = options.runCommand ? (0, import_node_path.join)((0, import_node_os.tmpdir)(), `tamagui-restore-${process.pid}`) : null;
125
- if (restoreDir) await (0, import_fs_extra.mkdir)(restoreDir, {
126
- recursive: true
127
- });
128
- const trackFile = async filePath => {
129
- if (!restoreDir) return;
130
- const hash = (0, import_node_crypto.createHash)("md5").update(filePath).digest("hex");
131
- const backupPath = (0, import_node_path.join)(restoreDir, hash);
132
- await (0, import_fs_extra.copyFile)(filePath, backupPath);
133
- trackedFiles.push({
134
- path: filePath,
135
- hardlinkPath: backupPath,
136
- mtimeAfterWrite: 0
137
- });
138
- };
139
- const recordMtime = async filePath => {
140
- if (!restoreDir) return;
141
- const tracked = trackedFiles.find(t => t.path === filePath);
142
- if (tracked) tracked.mtimeAfterWrite = (await (0, import_fs_extra.stat)(filePath)).mtimeMs;
143
- };
144
- for (const [sourcePath, filePlatforms] of fileToTargets) promises.push((async () => {
145
- if (options.debug) process.env.NODE_ENV ||= "development";
146
- const originalSource = await (0, import_fs_extra.readFile)(sourcePath, "utf-8");
147
- if (isDryRun) console.info(`
148
- ${sourcePath} [${filePlatforms.join(", ")}]`);
149
- if (filePlatforms.includes("web")) {
150
- process.env.TAMAGUI_TARGET = "web";
151
- const extractor = (0, import_static.createExtractor)({
152
- platform: "web"
153
- });
154
- const out = await (0, import_static.extractToClassNames)({
155
- extractor,
156
- source: originalSource,
157
- sourcePath,
158
- options: {
159
- ...buildOptions,
160
- platform: "web"
161
- },
162
- shouldPrintDebug: options.debug || false
163
- });
164
- if (out) {
165
- stats.filesProcessed++;
166
- stats.optimized += out.stats.optimized;
167
- stats.flattened += out.stats.flattened;
168
- stats.styled += out.stats.styled;
169
- stats.found += out.stats.found;
170
- if (isDryRun) {
171
- const jsContent = typeof out.js === "string" ? out.js : out.js.toString("utf-8");
172
- if (out.styles) console.info(`
55
+ const build = async (options) => {
56
+ const sourceDir = options.dir ?? ".";
57
+ const outputDir = options.output;
58
+ const outputAround = options.outputAround || false;
59
+ const promises = [];
60
+ const isDryRun = options.dryRun || false;
61
+ const trackedFiles = [];
62
+ const trackedPaths = /* @__PURE__ */ new Set();
63
+ const restoreDir = options.runCommand ? (0, import_node_path.join)((0, import_node_os.tmpdir)(), `tamagui-restore-${process.pid}`) : null;
64
+ if (restoreDir) await (0, import_fs_extra.mkdir)(restoreDir, { recursive: true });
65
+ let didRestore = false;
66
+ const restoreTrackedFiles = async (force = false) => {
67
+ if (didRestore) return;
68
+ await restoreFiles(trackedFiles, restoreDir, force);
69
+ didRestore = true;
70
+ };
71
+ try {
72
+ const trackFile = async (filePath) => {
73
+ if (!restoreDir || trackedPaths.has(filePath)) return;
74
+ trackedPaths.add(filePath);
75
+ if (!await (0, import_fs_extra.stat)(filePath).catch(() => null)) {
76
+ trackedFiles.push({
77
+ path: filePath,
78
+ hardlinkPath: "",
79
+ mtimeAfterWrite: 0
80
+ });
81
+ return;
82
+ }
83
+ const hash = (0, import_node_crypto.createHash)("md5").update(filePath).digest("hex");
84
+ const backupPath = (0, import_node_path.join)(restoreDir, hash);
85
+ await (0, import_fs_extra.copyFile)(filePath, backupPath);
86
+ trackedFiles.push({
87
+ path: filePath,
88
+ hardlinkPath: backupPath,
89
+ mtimeAfterWrite: 0
90
+ });
91
+ };
92
+ const recordMtime = async (filePath) => {
93
+ if (!restoreDir) return;
94
+ const tracked = trackedFiles.find((item) => item.path === filePath);
95
+ if (tracked) tracked.mtimeAfterWrite = (await (0, import_fs_extra.stat)(filePath)).mtimeMs;
96
+ };
97
+ if (isDryRun) console.info("[dry-run] no files will be written\n");
98
+ if (outputDir) await (0, import_fs_extra.mkdir)(outputDir, { recursive: true });
99
+ const loadedOptions = (0, import_static.loadTamaguiBuildConfigSync)(options.tamaguiOptions);
100
+ if (loadedOptions.disable) console.warn(`[tamagui] Note: "disable" option in tamagui.build.ts is being ignored for CLI build command`);
101
+ const buildOptions = {
102
+ ...loadedOptions,
103
+ disable: false,
104
+ disableExtraction: false
105
+ };
106
+ const targets = options.target === "both" || !options.target ? ["web", "native"] : [options.target];
107
+ const root = process.cwd();
108
+ const outputCSSPath = buildOptions.outputCSS ? (0, import_node_path.resolve)(root, buildOptions.outputCSS) : null;
109
+ if (outputCSSPath) await trackFile(outputCSSPath);
110
+ const require2 = (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : import_meta.url);
111
+ const configPath = (0, import_typescript.findConfigFile)(root, import_typescript.sys.fileExists, "tsconfig.json") || (0, import_typescript.findConfigFile)(root, import_typescript.sys.fileExists, "jsconfig.json");
112
+ const compilerOptions = configPath ? (0, import_typescript.parseJsonConfigFileContent)((() => {
113
+ const loaded = (0, import_typescript.readConfigFile)(configPath, import_typescript.sys.readFile);
114
+ if (loaded.error) throw new Error(String(loaded.error.messageText));
115
+ return loaded.config;
116
+ })(), import_typescript.sys, (0, import_node_path.dirname)(configPath)).options : {};
117
+ const resolveCompilerId = (specifier, importer) => {
118
+ const resolved = (0, import_typescript.nodeModuleNameResolver)(specifier, importer, compilerOptions, import_typescript.sys).resolvedModule?.resolvedFileName;
119
+ if (resolved && !resolved.endsWith(".d.ts")) return resolved;
120
+ try {
121
+ return require2.resolve(specifier, { paths: [(0, import_node_path.dirname)(importer), root] });
122
+ } catch {
123
+ return null;
124
+ }
125
+ };
126
+ const compilerFrontends = /* @__PURE__ */ new Map();
127
+ const compilerProjects = /* @__PURE__ */ new Map();
128
+ for (const target of targets) {
129
+ const targetOptions = {
130
+ ...buildOptions,
131
+ platform: target
132
+ };
133
+ const projectInfo = await (0, import_static.loadTamagui)(targetOptions);
134
+ if (!projectInfo) throw new Error(`Unable to load Tamagui for the ${target} build`);
135
+ const componentModules = [.../* @__PURE__ */ new Set(["@tamagui/core", ...targetOptions.components ?? []])].map((moduleName) => {
136
+ const id = resolveCompilerId(moduleName, (0, import_node_path.join)(root, "__tamagui_cli__.tsx"));
137
+ if (!id) throw new Error(`Unable to resolve compiler component ${moduleName}`);
138
+ return {
139
+ moduleName,
140
+ id
141
+ };
142
+ });
143
+ compilerProjects.set(target, {
144
+ projectInfo,
145
+ componentModules,
146
+ generation: (0, import_node_crypto.createHash)("sha256").update(target).update("\0").update(buildOptions.config ?? "tamagui.config.ts").update("\0").update(JSON.stringify(targetOptions.components ?? [])).update("\0").update(String(!!targetOptions.disablePartialExtraction)).update("\0").update(String(target === "native" && !!targetOptions.experimental?.nativeFastPath)).digest("hex"),
147
+ disablePartialExtraction: targetOptions.disablePartialExtraction,
148
+ experimentalNativeFastPath: target === "native" && targetOptions.experimental?.nativeFastPath === true,
149
+ cacheStamp: (0, import_static.compilerProjectStamp)({
150
+ stampSources: projectInfo.stampSources ?? [],
151
+ hostVersions: [`@tamagui/cli@${cliVersion}`],
152
+ target,
153
+ componentModules,
154
+ disablePartialExtraction: !!targetOptions.disablePartialExtraction,
155
+ experimentalNativeFastPath: target === "native" && targetOptions.experimental?.nativeFastPath === true,
156
+ zeroRuntime: false,
157
+ development: process.env.NODE_ENV === "development"
158
+ })
159
+ });
160
+ compilerFrontends.set(target, new import_static.CompilerFrontend());
161
+ }
162
+ if (outputCSSPath) await recordMtime(outputCSSPath);
163
+ const compileTarget = async (target, sourcePath, source) => {
164
+ const compiler = compilerFrontends.get(target);
165
+ const project = compilerProjects.get(target);
166
+ return compiler.compile({
167
+ id: sourcePath,
168
+ source,
169
+ root,
170
+ target,
171
+ project,
172
+ resolve: async (specifier, importer) => {
173
+ const id = resolveCompilerId(specifier, importer);
174
+ return id ? {
175
+ id,
176
+ external: id.includes("/node_modules/")
177
+ } : null;
178
+ },
179
+ load: async (id) => {
180
+ try {
181
+ return await (0, import_fs_extra.readFile)(id.split(/[?#]/, 1)[0], "utf8");
182
+ } catch {
183
+ return null;
184
+ }
185
+ }
186
+ });
187
+ };
188
+ const allFiles = [];
189
+ const watchPattern = sourceDir.match(/\.(tsx|jsx)$/) ? sourceDir : `${sourceDir}/**/*.{tsx,jsx}`;
190
+ const sourceRoot = sourceDir.match(/\.(tsx|jsx)$/) ? (0, import_node_path.dirname)((0, import_node_path.resolve)(sourceDir)) : (0, import_node_path.resolve)(sourceDir);
191
+ await new Promise((res) => {
192
+ const watcher = import_chokidar.default.watch(watchPattern, { ignoreInitial: false });
193
+ watcher.on("add", (relativePath) => {
194
+ const sourcePath = (0, import_node_path.resolve)(process.cwd(), relativePath);
195
+ if (options.exclude && import_micromatch.default.contains(relativePath, options.exclude)) return;
196
+ if (options.include && !import_micromatch.default.contains(relativePath, options.include)) return;
197
+ allFiles.push(sourcePath);
198
+ }).on("ready", () => {
199
+ watcher.close().then(() => res());
200
+ });
201
+ });
202
+ allFiles.sort();
203
+ const fileToTargets = /* @__PURE__ */ new Map();
204
+ for (const sourcePath of allFiles) {
205
+ const platformMatch = sourcePath.match(/\.(web|native|ios|android)\.(tsx|jsx)$/);
206
+ let filePlatforms = [];
207
+ if (platformMatch) {
208
+ const platform = platformMatch[1];
209
+ if (platform === "web") filePlatforms = ["web"];
210
+ else if (platform === "native" || platform === "ios" || platform === "android") filePlatforms = ["native"];
211
+ } else {
212
+ const basePath = sourcePath.replace(/\.(tsx|jsx)$/, "");
213
+ const hasNative = allFiles.some((f) => f === `${basePath}.native.tsx` || f === `${basePath}.native.jsx` || f === `${basePath}.ios.tsx` || f === `${basePath}.ios.jsx` || f === `${basePath}.android.tsx` || f === `${basePath}.android.jsx`);
214
+ const hasWeb = allFiles.some((f) => f === `${basePath}.web.tsx` || f === `${basePath}.web.jsx`);
215
+ filePlatforms = targets.filter((target) => {
216
+ if (target === "native" && hasNative) return false;
217
+ if (target === "web" && hasWeb) return false;
218
+ return true;
219
+ });
220
+ if (hasWeb && hasNative) filePlatforms = [];
221
+ }
222
+ if (filePlatforms.length > 0) fileToTargets.set(sourcePath, filePlatforms);
223
+ }
224
+ const stats = {
225
+ filesProcessed: 0,
226
+ optimized: 0,
227
+ flattened: 0,
228
+ styled: 0,
229
+ found: 0,
230
+ bailed: 0
231
+ };
232
+ const countedFiles = /* @__PURE__ */ new Set();
233
+ const addStats = (sourcePath, fileStats) => {
234
+ if (!countedFiles.has(sourcePath)) {
235
+ countedFiles.add(sourcePath);
236
+ stats.filesProcessed++;
237
+ }
238
+ stats.optimized += fileStats.lowered - fileStats.flattened;
239
+ stats.flattened += fileStats.flattened;
240
+ stats.styled += fileStats.styled;
241
+ stats.found += fileStats.found;
242
+ stats.bailed += fileStats.bailed;
243
+ };
244
+ if (options.debug) process.env.NODE_ENV ||= "development";
245
+ const sources = /* @__PURE__ */ new Map();
246
+ await Promise.all([...fileToTargets.keys()].map(async (sourcePath) => {
247
+ sources.set(sourcePath, await (0, import_fs_extra.readFile)(sourcePath, "utf-8"));
248
+ }));
249
+ const buildWebFile = async (sourcePath, originalSource) => {
250
+ if (isDryRun) console.info(`
251
+ ${sourcePath} [web]`);
252
+ const out = await compileTarget("web", sourcePath, originalSource);
253
+ if (out.output.changed || out.plan.stats.found > 0) {
254
+ addStats(sourcePath, out.plan.stats);
255
+ if (isDryRun) {
256
+ if (out.plan.css) console.info(`
173
257
  css:
174
- ${out.styles}`);
175
- console.info(`
258
+ ${out.plan.css}`);
259
+ console.info(`
176
260
  js:
177
- ${jsContent}`);
178
- } else {
179
- const relPath = outputDir ? (0, import_node_path.relative)((0, import_node_path.resolve)(sourceDir), sourcePath) : (0, import_node_path.basename)(sourcePath);
180
- const cssName = "_" + (0, import_node_path.basename)(sourcePath, (0, import_node_path.extname)(sourcePath));
181
- const outputBase = outputDir ? (0, import_node_path.join)(outputDir, (0, import_node_path.dirname)(relPath)) : (0, import_node_path.dirname)(sourcePath);
182
- if (outputDir) await (0, import_fs_extra.mkdir)(outputBase, {
183
- recursive: true
184
- });
185
- const stylePath = (0, import_node_path.join)(outputBase, cssName + ".css");
186
- const cssImport = `import "./${cssName}.css"`;
187
- const code = insertCssImport(typeof out.js === "string" ? out.js : out.js.toString("utf-8"), cssImport);
188
- const webOutputPath = outputDir ? (0, import_node_path.join)(outputDir, relPath) : sourcePath;
189
- if (!outputDir) await trackFile(sourcePath);
190
- await (0, import_fs_extra.writeFile)(webOutputPath, code, "utf-8");
191
- if (!outputDir) await recordMtime(sourcePath);
192
- await (0, import_fs_extra.writeFile)(stylePath, out.styles, "utf-8");
193
- if (!outputDir) trackedFiles.push({
194
- path: stylePath,
195
- hardlinkPath: "",
196
- mtimeAfterWrite: (await (0, import_fs_extra.stat)(stylePath)).mtimeMs
197
- });
198
- }
199
- } else if (isDryRun) console.info(` web: no output`);
200
- }
201
- if (filePlatforms.includes("native")) {
202
- process.env.TAMAGUI_TARGET = "native";
203
- const nativeTamaguiOptions = {
204
- ...buildOptions,
205
- platform: "native"
206
- };
207
- const nativeOut = (0, import_static.extractToNative)(sourcePath, originalSource, nativeTamaguiOptions);
208
- if (isDryRun) {
209
- if (nativeOut.code) console.info(`
261
+ ${out.output.code}`);
262
+ } else {
263
+ const relPath = outputDir ? (0, import_node_path.relative)(sourceRoot, sourcePath) : (0, import_node_path.basename)(sourcePath);
264
+ const cssName = "_" + (0, import_node_path.basename)(sourcePath, (0, import_node_path.extname)(sourcePath));
265
+ const outputBase = outputDir ? (0, import_node_path.join)(outputDir, (0, import_node_path.dirname)(relPath)) : (0, import_node_path.dirname)(sourcePath);
266
+ if (outputDir) await (0, import_fs_extra.mkdir)(outputBase, { recursive: true });
267
+ const stylePath = (0, import_node_path.join)(outputBase, cssName + ".css");
268
+ const cssImport = `import "./${cssName}.css"`;
269
+ const code = out.plan.css ? insertCssImport(out.output.code, cssImport) : out.output.code;
270
+ const webOutputPath = outputDir ? (0, import_node_path.join)(outputDir, relPath) : sourcePath;
271
+ if (!outputDir) await trackFile(sourcePath);
272
+ await (0, import_fs_extra.writeFile)(webOutputPath, code, "utf-8");
273
+ if (!outputDir) await recordMtime(sourcePath);
274
+ if (out.plan.css) await (0, import_fs_extra.writeFile)(stylePath, out.plan.css, "utf-8");
275
+ if (!outputDir && out.plan.css) trackedFiles.push({
276
+ path: stylePath,
277
+ hardlinkPath: "",
278
+ mtimeAfterWrite: (await (0, import_fs_extra.stat)(stylePath)).mtimeMs
279
+ });
280
+ }
281
+ } else if (isDryRun) console.info(` web: no output`);
282
+ };
283
+ const buildNativeFile = async (sourcePath, filePlatforms, originalSource) => {
284
+ if (isDryRun) console.info(`
285
+ ${sourcePath} [native]`);
286
+ const nativeOut = await compileTarget("native", sourcePath, originalSource);
287
+ if (nativeOut.output.changed || nativeOut.plan.stats.found > 0) addStats(sourcePath, nativeOut.plan.stats);
288
+ if (isDryRun) if (nativeOut.output.code) console.info(`
210
289
  native:
211
- ${nativeOut.code}`);else console.info(` native: no output`);
212
- } else {
213
- let nativeOutputPath = sourcePath;
214
- const needsNativeSuffix = !/\.(web|native|ios|android)\.(tsx|jsx)$/.test(sourcePath) && (filePlatforms.length > 1 || outputAround);
215
- if (outputAround) {
216
- nativeOutputPath = sourcePath.replace(/\.(tsx|jsx)$/, ".native.$1");
217
- if (await (0, import_fs_extra.stat)(nativeOutputPath).catch(() => null)) throw new Error(`--output-around: ${nativeOutputPath} already exists. Remove it first or use --output instead.`);
218
- } else if (outputDir) {
219
- const relPath = (0, import_node_path.relative)((0, import_node_path.resolve)(sourceDir), sourcePath);
220
- const outputRelPath = needsNativeSuffix ? relPath.replace(/\.(tsx|jsx)$/, ".native.$1") : relPath;
221
- nativeOutputPath = (0, import_node_path.join)(outputDir, outputRelPath);
222
- await (0, import_fs_extra.mkdir)((0, import_node_path.dirname)(nativeOutputPath), {
223
- recursive: true
224
- });
225
- } else if (needsNativeSuffix) nativeOutputPath = sourcePath.replace(/\.(tsx|jsx)$/, ".native.$1");
226
- if (nativeOut.code) {
227
- if (nativeOut.code.includes("__ReactNativeStyleSheet") || nativeOut.code.includes("_withStableStyle")) {
228
- stats.filesProcessed++;
229
- const wrapperMatches = nativeOut.code.match(/_withStableStyle/g);
230
- if (wrapperMatches) stats.flattened += wrapperMatches.length;
231
- }
232
- if (!outputDir && !outputAround && (nativeOutputPath === sourcePath || filePlatforms.length === 1)) await trackFile(nativeOutputPath);
233
- await (0, import_fs_extra.writeFile)(nativeOutputPath, nativeOut.code, "utf-8");
234
- if (!outputDir && !outputAround) await recordMtime(nativeOutputPath);
235
- if (!outputDir && !outputAround && nativeOutputPath !== sourcePath && filePlatforms.length > 1) trackedFiles.push({
236
- path: nativeOutputPath,
237
- hardlinkPath: "",
238
- mtimeAfterWrite: (await (0, import_fs_extra.stat)(nativeOutputPath)).mtimeMs
239
- });
240
- if (outputAround) console.info(` \u2192 ${nativeOutputPath}`);
241
- }
242
- }
243
- }
244
- })());
245
- await Promise.all(promises);
246
- if (isDryRun) console.info(`
247
- ${stats.filesProcessed} files | ${stats.found} found | ${stats.optimized} optimized | ${stats.flattened} flattened | ${stats.styled} styled
290
+ ${nativeOut.output.code}`);
291
+ else console.info(` native: no output`);
292
+ else {
293
+ let nativeOutputPath = sourcePath;
294
+ const needsNativeSuffix = !/\.(web|native|ios|android)\.(tsx|jsx)$/.test(sourcePath) && (filePlatforms.length > 1 || outputAround);
295
+ if (outputAround) {
296
+ nativeOutputPath = sourcePath.replace(/\.(tsx|jsx)$/, ".native.$1");
297
+ if (await (0, import_fs_extra.stat)(nativeOutputPath).catch(() => null)) throw new Error(`--output-around: ${nativeOutputPath} already exists. Remove it first or use --output instead.`);
298
+ } else if (outputDir) {
299
+ const relPath = (0, import_node_path.relative)(sourceRoot, sourcePath);
300
+ const outputRelPath = needsNativeSuffix ? relPath.replace(/\.(tsx|jsx)$/, ".native.$1") : relPath;
301
+ nativeOutputPath = (0, import_node_path.join)(outputDir, outputRelPath);
302
+ await (0, import_fs_extra.mkdir)((0, import_node_path.dirname)(nativeOutputPath), { recursive: true });
303
+ } else if (needsNativeSuffix) nativeOutputPath = sourcePath.replace(/\.(tsx|jsx)$/, ".native.$1");
304
+ if (nativeOut.output.code) {
305
+ if (nativeOut.output.changed) {
306
+ stats.filesProcessed++;
307
+ stats.flattened += nativeOut.plan.stats.flattened;
308
+ }
309
+ if (!outputDir && !outputAround && (nativeOutputPath === sourcePath || filePlatforms.length === 1)) await trackFile(nativeOutputPath);
310
+ await (0, import_fs_extra.writeFile)(nativeOutputPath, nativeOut.output.code, "utf-8");
311
+ if (!outputDir && !outputAround) await recordMtime(nativeOutputPath);
312
+ if (!outputDir && !outputAround && nativeOutputPath !== sourcePath && filePlatforms.length > 1) trackedFiles.push({
313
+ path: nativeOutputPath,
314
+ hardlinkPath: "",
315
+ mtimeAfterWrite: (await (0, import_fs_extra.stat)(nativeOutputPath)).mtimeMs
316
+ });
317
+ if (outputAround) console.info(` \u2192 ${nativeOutputPath}`);
318
+ }
319
+ }
320
+ };
321
+ for (const target of targets) {
322
+ process.env.TAMAGUI_TARGET = target;
323
+ const targetPromises = [...fileToTargets].filter(([, filePlatforms]) => filePlatforms.includes(target)).map(([sourcePath, filePlatforms]) => target === "web" ? buildWebFile(sourcePath, sources.get(sourcePath)) : buildNativeFile(sourcePath, filePlatforms, sources.get(sourcePath)));
324
+ promises.push(...targetPromises);
325
+ await Promise.all(targetPromises);
326
+ }
327
+ if (isDryRun) console.info(`
328
+ ${stats.filesProcessed} files | ${stats.found} found | ${stats.optimized} optimized | ${stats.flattened} flattened | ${stats.styled} styled | ${stats.bailed} bailed
248
329
  `);
249
- if (options.expectOptimizations !== void 0) {
250
- const totalOptimizations = stats.optimized + stats.flattened;
251
- if (totalOptimizations < options.expectOptimizations) {
252
- console.error(`
330
+ if (options.expectOptimizations !== void 0) {
331
+ const totalOptimizations = stats.optimized + stats.flattened;
332
+ if (totalOptimizations < options.expectOptimizations) {
333
+ console.error(`
253
334
  Expected at least ${options.expectOptimizations} optimizations but only got ${totalOptimizations}`);
254
- console.error(`Stats: ${JSON.stringify(stats, null, 2)}`);
255
- process.exit(1);
256
- }
257
- console.info(`
335
+ console.error(`Stats: ${JSON.stringify(stats, null, 2)}`);
336
+ await restoreTrackedFiles(true);
337
+ throw new Error(`Expected at least ${options.expectOptimizations} optimizations but only got ${totalOptimizations}`);
338
+ }
339
+ console.info(`
258
340
  \u2713 Met optimization target: ${totalOptimizations} >= ${options.expectOptimizations}`);
259
- }
260
- if (options.runCommand && options.runCommand.length > 0) {
261
- const command = options.runCommand.join(" ");
262
- console.info(`
263
- Running: ${command}
264
- `);
265
- try {
266
- (0, import_node_child_process.execSync)(command, {
267
- stdio: "inherit"
268
- });
269
- } catch (err) {
270
- console.error(`
341
+ }
342
+ if (options.runCommand && options.runCommand.length > 0) {
343
+ let commandFailed = true;
344
+ try {
345
+ await runCommand(options.runCommand, () => restoreTrackedFiles(true));
346
+ commandFailed = false;
347
+ } catch (err) {
348
+ console.error(`
271
349
  Command failed with exit code ${err.status || 1}`);
272
- process.exitCode = err.status || 1;
273
- } finally {
274
- await restoreFiles(trackedFiles, restoreDir);
275
- }
276
- }
277
- return {
278
- stats,
279
- trackedFiles
280
- };
350
+ throw err;
351
+ } finally {
352
+ await restoreTrackedFiles(commandFailed);
353
+ }
354
+ }
355
+ return {
356
+ stats,
357
+ trackedFiles
358
+ };
359
+ } catch (error) {
360
+ await Promise.allSettled(promises);
361
+ await restoreTrackedFiles(true);
362
+ throw error;
363
+ }
281
364
  };
282
- async function restoreFiles(trackedFiles, restoreDir) {
283
- if (!restoreDir || trackedFiles.length === 0) return;
284
- console.info(`
365
+ async function runCommand(command, restore) {
366
+ await new Promise((resolveCommand, rejectCommand) => {
367
+ const child = (0, import_node_child_process.spawn)(command[0], command.slice(1), { stdio: "inherit" });
368
+ let settled = false;
369
+ let listenersRestored = false;
370
+ const interruptListeners = process.listeners("SIGINT");
371
+ const terminateListeners = process.listeners("SIGTERM");
372
+ process.removeAllListeners("SIGINT");
373
+ process.removeAllListeners("SIGTERM");
374
+ const cleanup = () => {
375
+ process.off("SIGINT", onInterrupt);
376
+ process.off("SIGTERM", onTerminate);
377
+ if (listenersRestored) return;
378
+ listenersRestored = true;
379
+ for (const listener of interruptListeners) process.on("SIGINT", listener);
380
+ for (const listener of terminateListeners) process.on("SIGTERM", listener);
381
+ };
382
+ const finishSignal = (signal) => {
383
+ if (settled) return;
384
+ settled = true;
385
+ (async () => {
386
+ const childExited = child.exitCode === null && child.signalCode === null ? new Promise((resolveExit) => child.once("exit", () => resolveExit())) : Promise.resolve();
387
+ child.kill(signal);
388
+ await childExited;
389
+ try {
390
+ await restore();
391
+ } finally {
392
+ cleanup();
393
+ process.exit(signal === "SIGINT" ? 130 : 143);
394
+ }
395
+ })();
396
+ };
397
+ const onInterrupt = () => finishSignal("SIGINT");
398
+ const onTerminate = () => finishSignal("SIGTERM");
399
+ process.once("SIGINT", onInterrupt);
400
+ process.once("SIGTERM", onTerminate);
401
+ console.info(`
402
+ Running: ${command.join(" ")}
403
+ `);
404
+ child.once("error", (error) => {
405
+ if (settled) return;
406
+ settled = true;
407
+ cleanup();
408
+ rejectCommand(error);
409
+ });
410
+ child.once("exit", (code, signal) => {
411
+ if (settled) return;
412
+ settled = true;
413
+ cleanup();
414
+ if (code === 0) {
415
+ resolveCommand();
416
+ return;
417
+ }
418
+ const error = /* @__PURE__ */ new Error(signal ? `Command terminated by ${signal}` : `Command exited with code ${code}`);
419
+ error.status = code ?? 1;
420
+ rejectCommand(error);
421
+ });
422
+ });
423
+ }
424
+ async function restoreFiles(trackedFiles, restoreDir, force = false) {
425
+ if (!restoreDir) return;
426
+ if (trackedFiles.length === 0) {
427
+ await (0, import_fs_extra.rm)(restoreDir, {
428
+ recursive: true,
429
+ force: true
430
+ });
431
+ return;
432
+ }
433
+ console.info(`
285
434
  Restoring ${trackedFiles.length} files...`);
286
- let restored = 0;
287
- let skipped = 0;
288
- let deleted = 0;
289
- for (const tracked of trackedFiles) try {
290
- const currentStat = await (0, import_fs_extra.stat)(tracked.path).catch(() => null);
291
- if (currentStat && currentStat.mtimeMs !== tracked.mtimeAfterWrite) {
292
- console.warn(` Skipping ${tracked.path} - modified during build`);
293
- skipped++;
294
- continue;
295
- }
296
- if (tracked.hardlinkPath === "") {
297
- await (0, import_fs_extra.rm)(tracked.path, {
298
- force: true
299
- });
300
- deleted++;
301
- } else {
302
- await (0, import_fs_extra.copyFile)(tracked.hardlinkPath, tracked.path);
303
- restored++;
304
- }
305
- } catch (err) {
306
- console.warn(` Failed to restore ${tracked.path}: ${err.message}`);
307
- skipped++;
308
- }
309
- await (0, import_fs_extra.rm)(restoreDir, {
310
- recursive: true,
311
- force: true
312
- });
313
- console.info(` Restored: ${restored}, Deleted: ${deleted}, Skipped: ${skipped}`);
314
- }
435
+ let restored = 0;
436
+ let skipped = 0;
437
+ let deleted = 0;
438
+ for (const tracked of trackedFiles) try {
439
+ const currentStat = await (0, import_fs_extra.stat)(tracked.path).catch(() => null);
440
+ if (!force && currentStat && currentStat.mtimeMs !== tracked.mtimeAfterWrite) {
441
+ console.warn(` Skipping ${tracked.path} - modified during build`);
442
+ skipped++;
443
+ continue;
444
+ }
445
+ if (tracked.hardlinkPath === "") {
446
+ await (0, import_fs_extra.rm)(tracked.path, { force: true });
447
+ deleted++;
448
+ } else {
449
+ await (0, import_fs_extra.copyFile)(tracked.hardlinkPath, tracked.path);
450
+ restored++;
451
+ }
452
+ } catch (err) {
453
+ console.warn(` Failed to restore ${tracked.path}: ${err.message}`);
454
+ skipped++;
455
+ }
456
+ await (0, import_fs_extra.rm)(restoreDir, {
457
+ recursive: true,
458
+ force: true
459
+ });
460
+ console.info(` Restored: ${restored}, Deleted: ${deleted}, Skipped: ${skipped}`);
461
+ }