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