@tamagui/metro-plugin 3.0.0-beta.831.1 → 3.0.0-beta.889.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.
@@ -31,7 +31,7 @@ var import_promises = require("node:fs/promises");
31
31
  var import_node_path = require("node:path");
32
32
  var import_compiler_core = require("@tamagui/compiler-core");
33
33
  var import_diagnostics = require("./diagnostics.cjs");
34
- const METRO_COMPILER_CACHE_VERSION = 6;
34
+ const METRO_COMPILER_CACHE_VERSION = 7;
35
35
  var MetroCompilerCacheError = class extends Error {
36
36
  constructor(diagnostic) {
37
37
  super(diagnostic.message);
@@ -44,7 +44,7 @@ var import_compilerCache = require("./compilerCache.cjs");
44
44
  var import_diagnostics = require("./diagnostics.cjs");
45
45
  var import_metroResolver = require("./metroResolver.cjs");
46
46
  const import_meta = {};
47
- const METRO_RECORD_CACHE_VERSION = 1;
47
+ const METRO_RECORD_CACHE_VERSION = 2;
48
48
  function compareCodeUnits(left, right) {
49
49
  return left < right ? -1 : left > right ? 1 : 0;
50
50
  }
@@ -162,6 +162,9 @@ var MetroCompilerFrontend = class {
162
162
  #resolver;
163
163
  #graph = null;
164
164
  #host = null;
165
+ #registry = null;
166
+ #discovery = new import_static.ComponentDiscovery();
167
+ #externalClosure = (0, import_compiler_core.createExternalClosureLookup)();
165
168
  #projectGeneration = null;
166
169
  #publishedGeneration = null;
167
170
  #scanOptions = null;
@@ -253,14 +256,18 @@ var MetroCompilerFrontend = class {
253
256
  this.#host = null;
254
257
  if (unplanned.length || retainsLiveGraph(options)) {
255
258
  this.#graph = new import_compiler_core.ProjectGraph(import_compiler_core.yukuFactory, { modules: [...this.#records.values()].map(({ input }) => input) });
259
+ const componentModules = compilerProject.componentModules.map(({ moduleName, id }) => ({
260
+ moduleName,
261
+ resolvedId: id
262
+ }));
263
+ this.#discovery.clear();
264
+ this.#registry = (0, import_static.createComponentRegistry)(compilerProject.projectInfo.components, componentModules);
256
265
  this.#host = (0, import_static.createTamaguiCompilerHost)({
257
266
  target: compilerTarget(options.platform),
258
267
  tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,
259
268
  components: compilerProject.projectInfo.components,
260
- componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({
261
- moduleName,
262
- resolvedId: id
263
- })),
269
+ componentModules,
270
+ registry: this.#registry,
264
271
  disablePartialExtraction: compilerProject.disablePartialExtraction,
265
272
  experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,
266
273
  zeroRuntime: compilerProject.zeroRuntime
@@ -278,7 +285,7 @@ var MetroCompilerFrontend = class {
278
285
  zero.erasedExports.clear();
279
286
  this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(import_compiler_core.resolvedModuleId));
280
287
  }
281
- for (const id of unplanned) this.#refreshEntry(id);
288
+ for (const id of unplanned) await this.#refreshEntry(id);
282
289
  await this.#storePlans(unplanned);
283
290
  }
284
291
  if (zero) {
@@ -364,7 +371,7 @@ var MetroCompilerFrontend = class {
364
371
  this.#records.delete(id);
365
372
  this.#entries.delete(id);
366
373
  for (const affected of invalidation2.invalidatedIds) {
367
- if (affected !== id) this.#refreshEntry(affected);
374
+ if (affected !== id) await this.#refreshEntry(affected);
368
375
  }
369
376
  const generation2 = await this.#publish(options.platform);
370
377
  result = {
@@ -386,7 +393,7 @@ var MetroCompilerFrontend = class {
386
393
  }
387
394
  this.#records.set(record.input.id, record);
388
395
  const invalidation = graph.updateModule(record.input);
389
- for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected);
396
+ for (const affected of invalidation.invalidatedIds) await this.#refreshEntry(affected);
390
397
  const generation = invalidation.changed ? await this.#publish(options.platform) : null;
391
398
  result = {
392
399
  changed: invalidation.changed,
@@ -428,6 +435,7 @@ var MetroCompilerFrontend = class {
428
435
  this.#planKeys.clear();
429
436
  this.#graph = null;
430
437
  this.#host = null;
438
+ this.#registry = null;
431
439
  this.#projectGeneration = null;
432
440
  }
433
441
  async #loadCompilerProject(options, importer, diagnostics) {
@@ -588,7 +596,7 @@ var MetroCompilerFrontend = class {
588
596
  this.#records.set(id, record);
589
597
  const invalidation = this.#graph?.updateModule(record.input);
590
598
  for (const affected of invalidation?.invalidatedIds ?? [id]) {
591
- this.#refreshEntry(affected);
599
+ await this.#refreshEntry(affected);
592
600
  }
593
601
  if (this.config.watch !== false && this.#scanOptions && retainsLiveGraph(this.#scanOptions)) {
594
602
  this.#watchModule(id);
@@ -597,14 +605,22 @@ var MetroCompilerFrontend = class {
597
605
  visiting.delete(id);
598
606
  }
599
607
  }
600
- #refreshEntry(id) {
608
+ async #refreshEntry(id) {
601
609
  const graph = this.#graph;
602
610
  const host = this.#host;
611
+ const registry = this.#registry;
603
612
  const record = this.#records.get(id);
604
- if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration) return;
613
+ if (!graph || !host || !registry || !record || !this.#scanOptions || !this.#projectGeneration) {
614
+ return;
615
+ }
605
616
  const target = compilerTarget(this.#scanOptions.platform);
617
+ const materialized = (0, import_compiler_core.materializeModule)(graph, id);
618
+ await this.#discovery.prepare(materialized, registry, ({ id: moduleId }) => import_static.default.evaluateComponentModule({
619
+ ...this.config.tamaguiOptions,
620
+ platform: target
621
+ }, moduleId));
606
622
  const plan = (0, import_compiler_core.lowerModule)({
607
- module: (0, import_compiler_core.materializeModule)(graph, id),
623
+ module: materialized,
608
624
  source: record.input.source,
609
625
  target,
610
626
  host,
@@ -686,7 +702,7 @@ var MetroCompilerFrontend = class {
686
702
  let node = nodes.get(id);
687
703
  if (node === void 0) {
688
704
  const record = this.#records.get(id);
689
- node = record ? (0, import_compiler_core.moduleClosureNode)(record.input) : null;
705
+ node = record ? (0, import_compiler_core.moduleClosureNode)(record.input, { includeExternal: true }) : this.#externalClosure(id);
690
706
  nodes.set(id, node);
691
707
  }
692
708
  return node;
@@ -4,7 +4,7 @@ import { join } from "node:path";
4
4
  import { LOWERED_MODULE_PLAN_VERSION, contentHash, stableStringify, tamaguiCacheRoot } from "@tamagui/compiler-core";
5
5
  import { metroDiagnostic } from "./diagnostics.mjs";
6
6
 
7
- const METRO_COMPILER_CACHE_VERSION = 6;
7
+ const METRO_COMPILER_CACHE_VERSION = 7;
8
8
  var MetroCompilerCacheError = class extends Error {
9
9
  constructor(diagnostic) {
10
10
  super(diagnostic.message);
@@ -35,8 +35,8 @@ function defaultMetroCompilerCacheRoot(projectRoot) {
35
35
  var MetroCompilerCache = class {
36
36
  constructor(root) {
37
37
  this.root = root;
38
- this.#blobsDirectory = join(root, `v${6}`, "blobs");
39
- this.#manifestPath = join(root, `v${6}`, "manifest.json");
38
+ this.#blobsDirectory = join(root, `v${7}`, "blobs");
39
+ this.#manifestPath = join(root, `v${7}`, "manifest.json");
40
40
  }
41
41
  root;
42
42
  #blobsDirectory;
@@ -45,7 +45,7 @@ var MetroCompilerCache = class {
45
45
  await mkdir(this.#blobsDirectory, { recursive: true });
46
46
  const descriptors = {};
47
47
  for (const entry of [...entries].sort((left, right) => compareCodeUnits(left.moduleId, right.moduleId))) {
48
- if (entry.schemaVersion !== 6) {
48
+ if (entry.schemaVersion !== 7) {
49
49
  throw new Error(`Cannot publish cache schema ${entry.schemaVersion}`);
50
50
  }
51
51
  const serialized = `${stableStringify(entry)}
@@ -73,13 +73,13 @@ var MetroCompilerCache = class {
73
73
  }
74
74
  const generation = contentHash(stableStringify(descriptors));
75
75
  const manifest = {
76
- schemaVersion: 6,
76
+ schemaVersion: 7,
77
77
  generation,
78
78
  optionsHash,
79
79
  platform,
80
80
  entries: descriptors
81
81
  };
82
- const manifestDirectory = join(this.root, `v${6}`);
82
+ const manifestDirectory = join(this.root, `v${7}`);
83
83
  const temporaryPath = join(manifestDirectory, `.manifest-${process.pid}-${randomBytes(6).toString("hex")}.json`);
84
84
  await writeFile(temporaryPath, `${stableStringify(manifest)}
85
85
  `, "utf8");
@@ -147,10 +147,10 @@ var MetroCompilerCache = class {
147
147
  await rm(this.#manifestPath, { force: true });
148
148
  }
149
149
  #zeroSidecarPath(generation) {
150
- return join(this.root, `v${6}`, `zero-${generation.slice(0, 32)}.json`);
150
+ return join(this.root, `v${7}`, `zero-${generation.slice(0, 32)}.json`);
151
151
  }
152
152
  async publishZeroCSS(sidecar) {
153
- await mkdir(join(this.root, `v${6}`), { recursive: true });
153
+ await mkdir(join(this.root, `v${7}`), { recursive: true });
154
154
  const file = this.#zeroSidecarPath(sidecar.generation);
155
155
  const temporaryPath = `${file}.${process.pid}-${randomBytes(6).toString("hex")}.tmp`;
156
156
  await writeFile(temporaryPath, `${stableStringify(sidecar)}
@@ -167,7 +167,7 @@ var MetroCompilerCache = class {
167
167
  throw error;
168
168
  }
169
169
  const sidecar = parseJson(source, "Zero CSS sidecar");
170
- if (sidecar.schemaVersion !== 6 || sidecar.generation !== generation || typeof sidecar.configCSS !== "string" || !sidecar.zeroModuleCSS || typeof sidecar.zeroModuleCSS !== "object" || !sidecar.bridgeCSS || typeof sidecar.bridgeCSS !== "object" || !sidecar.bridges || typeof sidecar.bridges !== "object") {
170
+ if (sidecar.schemaVersion !== 7 || sidecar.generation !== generation || typeof sidecar.configCSS !== "string" || !sidecar.zeroModuleCSS || typeof sidecar.zeroModuleCSS !== "object" || !sidecar.bridgeCSS || typeof sidecar.bridgeCSS !== "object" || !sidecar.bridges || typeof sidecar.bridges !== "object") {
171
171
  return null;
172
172
  }
173
173
  return sidecar;
@@ -181,7 +181,7 @@ var MetroCompilerCache = class {
181
181
  throw error;
182
182
  }
183
183
  const manifest = parseJson(source, "Cache manifest");
184
- if (manifest.schemaVersion !== 6 || typeof manifest.generation !== "string" || typeof manifest.optionsHash !== "string" || !manifest.entries || typeof manifest.entries !== "object") {
184
+ if (manifest.schemaVersion !== 7 || typeof manifest.generation !== "string" || typeof manifest.optionsHash !== "string" || !manifest.entries || typeof manifest.entries !== "object") {
185
185
  throw cacheCorrupt("Cache manifest has an unsupported schema");
186
186
  }
187
187
  return manifest;
@@ -201,7 +201,7 @@ var MetroCompilerCache = class {
201
201
  throw cacheCorrupt(`Cache blob ${descriptor.blobHash} failed its hash`, moduleId);
202
202
  }
203
203
  const entry = parseJson(source, `Cache blob ${descriptor.blobHash}`, moduleId);
204
- if (entry.schemaVersion !== 6 || entry.moduleId !== moduleId || typeof entry.sourceHash !== "string" || entry.sourceHash !== descriptor.sourceHash || !entry.plan || entry.plan.version !== LOWERED_MODULE_PLAN_VERSION || entry.plan.id !== moduleId || entry.plan.sourceHash !== entry.sourceHash || !Array.isArray(entry.plan.edits) || !Array.isArray(entry.plan.diagnostics) || !Array.isArray(entry.diagnostics)) {
204
+ if (entry.schemaVersion !== 7 || entry.moduleId !== moduleId || typeof entry.sourceHash !== "string" || entry.sourceHash !== descriptor.sourceHash || !entry.plan || entry.plan.version !== LOWERED_MODULE_PLAN_VERSION || entry.plan.id !== moduleId || entry.plan.sourceHash !== entry.sourceHash || !Array.isArray(entry.plan.edits) || !Array.isArray(entry.plan.diagnostics) || !Array.isArray(entry.diagnostics)) {
205
205
  throw cacheCorrupt(`Cache blob ${descriptor.blobHash} has invalid contents`, moduleId);
206
206
  }
207
207
  return entry;
@@ -1 +1 @@
1
- {"version":3,"file":"compilerCache.js","names":["#blobsDirectory","#manifestPath","#readManifest","#readBlob","#zeroSidecarPath"],"sources":["esm/compilerCache.js"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n LOWERED_MODULE_PLAN_VERSION,\n contentHash,\n stableStringify,\n tamaguiCacheRoot\n} from \"@tamagui/compiler-core\";\nimport { metroDiagnostic } from \"./diagnostics\";\nconst METRO_COMPILER_CACHE_VERSION = 6;\nclass MetroCompilerCacheError extends Error {\n constructor(diagnostic) {\n super(diagnostic.message);\n this.diagnostic = diagnostic;\n this.name = \"MetroCompilerCacheError\";\n }\n diagnostic;\n}\nfunction compareCodeUnits(left, right) {\n return left < right ? -1 : left > right ? 1 : 0;\n}\nfunction stableEntries(record) {\n return Object.entries(record).sort(([left], [right]) => compareCodeUnits(left, right));\n}\nfunction cacheCorrupt(message, moduleId) {\n return new MetroCompilerCacheError(\n metroDiagnostic(\"metro/cache-corrupt\", message, { moduleId })\n );\n}\nfunction parseJson(source, description, moduleId) {\n try {\n return JSON.parse(source);\n } catch (error) {\n throw cacheCorrupt(\n `${description} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n moduleId\n );\n }\n}\nfunction defaultMetroCompilerCacheRoot(projectRoot) {\n return join(tamaguiCacheRoot(projectRoot), \"metro-compiler\");\n}\nclass MetroCompilerCache {\n constructor(root) {\n this.root = root;\n this.#blobsDirectory = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, \"blobs\");\n this.#manifestPath = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, \"manifest.json\");\n }\n root;\n #blobsDirectory;\n #manifestPath;\n async publish(platform, entries, optionsHash) {\n await mkdir(this.#blobsDirectory, { recursive: true });\n const descriptors = {};\n for (const entry of [...entries].sort(\n (left, right) => compareCodeUnits(left.moduleId, right.moduleId)\n )) {\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION) {\n throw new Error(`Cannot publish cache schema ${entry.schemaVersion}`);\n }\n const serialized = `${stableStringify(entry)}\n`;\n const blobHash = contentHash(serialized);\n const blobPath = join(this.#blobsDirectory, `${blobHash}.json`);\n try {\n await writeFile(blobPath, serialized, { encoding: \"utf8\", flag: \"wx\" });\n } catch (error) {\n if (error.code !== \"EEXIST\") throw error;\n const existing = await readFile(blobPath, \"utf8\");\n if (contentHash(existing) !== blobHash) {\n const temporaryBlobPath = `${blobPath}.${process.pid}-${randomBytes(6).toString(\"hex\")}.tmp`;\n await writeFile(temporaryBlobPath, serialized, \"utf8\");\n await rename(temporaryBlobPath, blobPath);\n }\n }\n descriptors[entry.moduleId] = {\n blobHash,\n sourceHash: entry.sourceHash\n };\n }\n const generation = contentHash(stableStringify(descriptors));\n const manifest = {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n optionsHash,\n platform,\n entries: descriptors\n };\n const manifestDirectory = join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`);\n const temporaryPath = join(\n manifestDirectory,\n `.manifest-${process.pid}-${randomBytes(6).toString(\"hex\")}.json`\n );\n await writeFile(temporaryPath, `${stableStringify(manifest)}\n`, \"utf8\");\n await rename(temporaryPath, this.#manifestPath);\n return generation;\n }\n async read(moduleId, rawSource, onMiss) {\n const manifest = await this.#readManifest();\n if (!manifest) return null;\n const descriptor = manifest.entries[moduleId];\n if (!descriptor) {\n onMiss?.(\"no-entry\");\n return null;\n }\n const sourceHash = contentHash(rawSource);\n if (sourceHash !== descriptor.sourceHash) {\n onMiss?.(\n \"source-hash-mismatch\",\n `worker ${sourceHash.slice(0, 12)} vs plan ${descriptor.sourceHash.slice(0, 12)}`\n );\n return null;\n }\n return await this.#readBlob(moduleId, descriptor);\n }\n async validate() {\n const diagnostics = [];\n try {\n const manifest = await this.#readManifest();\n if (!manifest) {\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null\n };\n }\n const sourceHashes = {};\n for (const [moduleId, descriptor] of stableEntries(manifest.entries)) {\n await this.#readBlob(moduleId, descriptor);\n sourceHashes[moduleId] = descriptor.sourceHash;\n }\n return {\n valid: true,\n diagnostics,\n generation: manifest.generation,\n moduleIds: Object.keys(manifest.entries).sort(compareCodeUnits),\n sourceHashes,\n optionsHash: manifest.optionsHash\n };\n } catch (error) {\n if (error instanceof MetroCompilerCacheError) {\n diagnostics.push(error.diagnostic);\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null\n };\n }\n throw error;\n }\n }\n async discardManifest() {\n await rm(this.#manifestPath, { force: true });\n }\n #zeroSidecarPath(generation) {\n return join(\n this.root,\n `v${METRO_COMPILER_CACHE_VERSION}`,\n `zero-${generation.slice(0, 32)}.json`\n );\n }\n async publishZeroCSS(sidecar) {\n await mkdir(join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`), { recursive: true });\n const file = this.#zeroSidecarPath(sidecar.generation);\n const temporaryPath = `${file}.${process.pid}-${randomBytes(6).toString(\"hex\")}.tmp`;\n await writeFile(temporaryPath, `${stableStringify(sidecar)}\n`, \"utf8\");\n await rename(temporaryPath, file);\n }\n /** Null whenever the sidecar is absent or does not describe this generation. */\n async readZeroCSS(generation) {\n let source;\n try {\n source = await readFile(this.#zeroSidecarPath(generation), \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") return null;\n throw error;\n }\n const sidecar = parseJson(source, \"Zero CSS sidecar\");\n if (sidecar.schemaVersion !== METRO_COMPILER_CACHE_VERSION || sidecar.generation !== generation || typeof sidecar.configCSS !== \"string\" || !sidecar.zeroModuleCSS || typeof sidecar.zeroModuleCSS !== \"object\" || !sidecar.bridgeCSS || typeof sidecar.bridgeCSS !== \"object\" || !sidecar.bridges || typeof sidecar.bridges !== \"object\") {\n return null;\n }\n return sidecar;\n }\n async #readManifest() {\n let source;\n try {\n source = await readFile(this.#manifestPath, \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") return null;\n throw error;\n }\n const manifest = parseJson(source, \"Cache manifest\");\n if (manifest.schemaVersion !== METRO_COMPILER_CACHE_VERSION || typeof manifest.generation !== \"string\" || typeof manifest.optionsHash !== \"string\" || !manifest.entries || typeof manifest.entries !== \"object\") {\n throw cacheCorrupt(\"Cache manifest has an unsupported schema\");\n }\n return manifest;\n }\n async #readBlob(moduleId, descriptor) {\n const blobPath = join(this.#blobsDirectory, `${descriptor.blobHash}.json`);\n let source;\n try {\n source = await readFile(blobPath, \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} is missing`, moduleId);\n }\n throw error;\n }\n if (contentHash(source) !== descriptor.blobHash) {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} failed its hash`, moduleId);\n }\n const entry = parseJson(\n source,\n `Cache blob ${descriptor.blobHash}`,\n moduleId\n );\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION || entry.moduleId !== moduleId || typeof entry.sourceHash !== \"string\" || entry.sourceHash !== descriptor.sourceHash || !entry.plan || entry.plan.version !== LOWERED_MODULE_PLAN_VERSION || entry.plan.id !== moduleId || entry.plan.sourceHash !== entry.sourceHash || !Array.isArray(entry.plan.edits) || !Array.isArray(entry.plan.diagnostics) || !Array.isArray(entry.diagnostics)) {\n throw cacheCorrupt(\n `Cache blob ${descriptor.blobHash} has invalid contents`,\n moduleId\n );\n }\n return entry;\n }\n}\nexport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n MetroCompilerCacheError,\n defaultMetroCompilerCacheRoot\n};\n//# sourceMappingURL=compilerCache.js.map\n"],"mappings":"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"}
1
+ {"version":3,"file":"compilerCache.js","names":["#blobsDirectory","#manifestPath","#readManifest","#readBlob","#zeroSidecarPath"],"sources":["esm/compilerCache.js"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n LOWERED_MODULE_PLAN_VERSION,\n contentHash,\n stableStringify,\n tamaguiCacheRoot\n} from \"@tamagui/compiler-core\";\nimport { metroDiagnostic } from \"./diagnostics\";\nconst METRO_COMPILER_CACHE_VERSION = 7;\nclass MetroCompilerCacheError extends Error {\n constructor(diagnostic) {\n super(diagnostic.message);\n this.diagnostic = diagnostic;\n this.name = \"MetroCompilerCacheError\";\n }\n diagnostic;\n}\nfunction compareCodeUnits(left, right) {\n return left < right ? -1 : left > right ? 1 : 0;\n}\nfunction stableEntries(record) {\n return Object.entries(record).sort(([left], [right]) => compareCodeUnits(left, right));\n}\nfunction cacheCorrupt(message, moduleId) {\n return new MetroCompilerCacheError(\n metroDiagnostic(\"metro/cache-corrupt\", message, { moduleId })\n );\n}\nfunction parseJson(source, description, moduleId) {\n try {\n return JSON.parse(source);\n } catch (error) {\n throw cacheCorrupt(\n `${description} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n moduleId\n );\n }\n}\nfunction defaultMetroCompilerCacheRoot(projectRoot) {\n return join(tamaguiCacheRoot(projectRoot), \"metro-compiler\");\n}\nclass MetroCompilerCache {\n constructor(root) {\n this.root = root;\n this.#blobsDirectory = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, \"blobs\");\n this.#manifestPath = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, \"manifest.json\");\n }\n root;\n #blobsDirectory;\n #manifestPath;\n async publish(platform, entries, optionsHash) {\n await mkdir(this.#blobsDirectory, { recursive: true });\n const descriptors = {};\n for (const entry of [...entries].sort(\n (left, right) => compareCodeUnits(left.moduleId, right.moduleId)\n )) {\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION) {\n throw new Error(`Cannot publish cache schema ${entry.schemaVersion}`);\n }\n const serialized = `${stableStringify(entry)}\n`;\n const blobHash = contentHash(serialized);\n const blobPath = join(this.#blobsDirectory, `${blobHash}.json`);\n try {\n await writeFile(blobPath, serialized, { encoding: \"utf8\", flag: \"wx\" });\n } catch (error) {\n if (error.code !== \"EEXIST\") throw error;\n const existing = await readFile(blobPath, \"utf8\");\n if (contentHash(existing) !== blobHash) {\n const temporaryBlobPath = `${blobPath}.${process.pid}-${randomBytes(6).toString(\"hex\")}.tmp`;\n await writeFile(temporaryBlobPath, serialized, \"utf8\");\n await rename(temporaryBlobPath, blobPath);\n }\n }\n descriptors[entry.moduleId] = {\n blobHash,\n sourceHash: entry.sourceHash\n };\n }\n const generation = contentHash(stableStringify(descriptors));\n const manifest = {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n optionsHash,\n platform,\n entries: descriptors\n };\n const manifestDirectory = join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`);\n const temporaryPath = join(\n manifestDirectory,\n `.manifest-${process.pid}-${randomBytes(6).toString(\"hex\")}.json`\n );\n await writeFile(temporaryPath, `${stableStringify(manifest)}\n`, \"utf8\");\n await rename(temporaryPath, this.#manifestPath);\n return generation;\n }\n async read(moduleId, rawSource, onMiss) {\n const manifest = await this.#readManifest();\n if (!manifest) return null;\n const descriptor = manifest.entries[moduleId];\n if (!descriptor) {\n onMiss?.(\"no-entry\");\n return null;\n }\n const sourceHash = contentHash(rawSource);\n if (sourceHash !== descriptor.sourceHash) {\n onMiss?.(\n \"source-hash-mismatch\",\n `worker ${sourceHash.slice(0, 12)} vs plan ${descriptor.sourceHash.slice(0, 12)}`\n );\n return null;\n }\n return await this.#readBlob(moduleId, descriptor);\n }\n async validate() {\n const diagnostics = [];\n try {\n const manifest = await this.#readManifest();\n if (!manifest) {\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null\n };\n }\n const sourceHashes = {};\n for (const [moduleId, descriptor] of stableEntries(manifest.entries)) {\n await this.#readBlob(moduleId, descriptor);\n sourceHashes[moduleId] = descriptor.sourceHash;\n }\n return {\n valid: true,\n diagnostics,\n generation: manifest.generation,\n moduleIds: Object.keys(manifest.entries).sort(compareCodeUnits),\n sourceHashes,\n optionsHash: manifest.optionsHash\n };\n } catch (error) {\n if (error instanceof MetroCompilerCacheError) {\n diagnostics.push(error.diagnostic);\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null\n };\n }\n throw error;\n }\n }\n async discardManifest() {\n await rm(this.#manifestPath, { force: true });\n }\n #zeroSidecarPath(generation) {\n return join(\n this.root,\n `v${METRO_COMPILER_CACHE_VERSION}`,\n `zero-${generation.slice(0, 32)}.json`\n );\n }\n async publishZeroCSS(sidecar) {\n await mkdir(join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`), { recursive: true });\n const file = this.#zeroSidecarPath(sidecar.generation);\n const temporaryPath = `${file}.${process.pid}-${randomBytes(6).toString(\"hex\")}.tmp`;\n await writeFile(temporaryPath, `${stableStringify(sidecar)}\n`, \"utf8\");\n await rename(temporaryPath, file);\n }\n /** Null whenever the sidecar is absent or does not describe this generation. */\n async readZeroCSS(generation) {\n let source;\n try {\n source = await readFile(this.#zeroSidecarPath(generation), \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") return null;\n throw error;\n }\n const sidecar = parseJson(source, \"Zero CSS sidecar\");\n if (sidecar.schemaVersion !== METRO_COMPILER_CACHE_VERSION || sidecar.generation !== generation || typeof sidecar.configCSS !== \"string\" || !sidecar.zeroModuleCSS || typeof sidecar.zeroModuleCSS !== \"object\" || !sidecar.bridgeCSS || typeof sidecar.bridgeCSS !== \"object\" || !sidecar.bridges || typeof sidecar.bridges !== \"object\") {\n return null;\n }\n return sidecar;\n }\n async #readManifest() {\n let source;\n try {\n source = await readFile(this.#manifestPath, \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") return null;\n throw error;\n }\n const manifest = parseJson(source, \"Cache manifest\");\n if (manifest.schemaVersion !== METRO_COMPILER_CACHE_VERSION || typeof manifest.generation !== \"string\" || typeof manifest.optionsHash !== \"string\" || !manifest.entries || typeof manifest.entries !== \"object\") {\n throw cacheCorrupt(\"Cache manifest has an unsupported schema\");\n }\n return manifest;\n }\n async #readBlob(moduleId, descriptor) {\n const blobPath = join(this.#blobsDirectory, `${descriptor.blobHash}.json`);\n let source;\n try {\n source = await readFile(blobPath, \"utf8\");\n } catch (error) {\n if (error.code === \"ENOENT\") {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} is missing`, moduleId);\n }\n throw error;\n }\n if (contentHash(source) !== descriptor.blobHash) {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} failed its hash`, moduleId);\n }\n const entry = parseJson(\n source,\n `Cache blob ${descriptor.blobHash}`,\n moduleId\n );\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION || entry.moduleId !== moduleId || typeof entry.sourceHash !== \"string\" || entry.sourceHash !== descriptor.sourceHash || !entry.plan || entry.plan.version !== LOWERED_MODULE_PLAN_VERSION || entry.plan.id !== moduleId || entry.plan.sourceHash !== entry.sourceHash || !Array.isArray(entry.plan.edits) || !Array.isArray(entry.plan.diagnostics) || !Array.isArray(entry.diagnostics)) {\n throw cacheCorrupt(\n `Cache blob ${descriptor.blobHash} has invalid contents`,\n moduleId\n );\n }\n return entry;\n }\n}\nexport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n MetroCompilerCacheError,\n defaultMetroCompilerCacheRoot\n};\n//# sourceMappingURL=compilerCache.js.map\n"],"mappings":"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"}
@@ -3,15 +3,15 @@ import { readFile, readdir, realpath } from "node:fs/promises";
3
3
  import { createRequire } from "node:module";
4
4
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
5
  import ignore from "ignore";
6
- import { JsonFileCache, ModulePlanCache, PLAN_CACHE_SCHEMA_VERSION, ProjectGraph, contentHash, defaultPlanCacheRoot, isTamaguiSpecifier, lowerModule, materializeModule, moduleClosureDigest, moduleClosureNode, planCacheKey, resolvedModuleId, stableStringify, yukuFactory } from "@tamagui/compiler-core";
7
- import Static, { createTamaguiCompilerHost } from "@tamagui/static";
6
+ import { JsonFileCache, ModulePlanCache, PLAN_CACHE_SCHEMA_VERSION, ProjectGraph, contentHash, createExternalClosureLookup, defaultPlanCacheRoot, isTamaguiSpecifier, lowerModule, materializeModule, moduleClosureDigest, moduleClosureNode, planCacheKey, resolvedModuleId, stableStringify, yukuFactory } from "@tamagui/compiler-core";
7
+ import Static, { ComponentDiscovery, createComponentRegistry, createTamaguiCompilerHost } from "@tamagui/static";
8
8
  import { compileWithUserBabel, userBabelCacheKey } from "./babel.mjs";
9
9
  import { zeroModuleKey } from "./zeroRuntime.mjs";
10
10
  import { METRO_COMPILER_CACHE_VERSION, MetroCompilerCache, defaultMetroCompilerCacheRoot } from "./compilerCache.mjs";
11
11
  import { metroDiagnostic } from "./diagnostics.mjs";
12
12
  import { createMetroCompilerResolver, isCompilerSourceFile, moduleSpecifiersFromAst } from "./metroResolver.mjs";
13
13
 
14
- const METRO_RECORD_CACHE_VERSION = 1;
14
+ const METRO_RECORD_CACHE_VERSION = 2;
15
15
  function compareCodeUnits(left, right) {
16
16
  return left < right ? -1 : left > right ? 1 : 0;
17
17
  }
@@ -129,6 +129,9 @@ var MetroCompilerFrontend = class {
129
129
  #resolver;
130
130
  #graph = null;
131
131
  #host = null;
132
+ #registry = null;
133
+ #discovery = new ComponentDiscovery();
134
+ #externalClosure = createExternalClosureLookup();
132
135
  #projectGeneration = null;
133
136
  #publishedGeneration = null;
134
137
  #scanOptions = null;
@@ -220,14 +223,18 @@ var MetroCompilerFrontend = class {
220
223
  this.#host = null;
221
224
  if (unplanned.length || retainsLiveGraph(options)) {
222
225
  this.#graph = new ProjectGraph(yukuFactory, { modules: [...this.#records.values()].map(({ input }) => input) });
226
+ const componentModules = compilerProject.componentModules.map(({ moduleName, id }) => ({
227
+ moduleName,
228
+ resolvedId: id
229
+ }));
230
+ this.#discovery.clear();
231
+ this.#registry = createComponentRegistry(compilerProject.projectInfo.components, componentModules);
223
232
  this.#host = createTamaguiCompilerHost({
224
233
  target: compilerTarget(options.platform),
225
234
  tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,
226
235
  components: compilerProject.projectInfo.components,
227
- componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({
228
- moduleName,
229
- resolvedId: id
230
- })),
236
+ componentModules,
237
+ registry: this.#registry,
231
238
  disablePartialExtraction: compilerProject.disablePartialExtraction,
232
239
  experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,
233
240
  zeroRuntime: compilerProject.zeroRuntime
@@ -245,7 +252,7 @@ var MetroCompilerFrontend = class {
245
252
  zero.erasedExports.clear();
246
253
  this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId));
247
254
  }
248
- for (const id of unplanned) this.#refreshEntry(id);
255
+ for (const id of unplanned) await this.#refreshEntry(id);
249
256
  await this.#storePlans(unplanned);
250
257
  }
251
258
  if (zero) {
@@ -331,7 +338,7 @@ var MetroCompilerFrontend = class {
331
338
  this.#records.delete(id);
332
339
  this.#entries.delete(id);
333
340
  for (const affected of invalidation2.invalidatedIds) {
334
- if (affected !== id) this.#refreshEntry(affected);
341
+ if (affected !== id) await this.#refreshEntry(affected);
335
342
  }
336
343
  const generation2 = await this.#publish(options.platform);
337
344
  result = {
@@ -353,7 +360,7 @@ var MetroCompilerFrontend = class {
353
360
  }
354
361
  this.#records.set(record.input.id, record);
355
362
  const invalidation = graph.updateModule(record.input);
356
- for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected);
363
+ for (const affected of invalidation.invalidatedIds) await this.#refreshEntry(affected);
357
364
  const generation = invalidation.changed ? await this.#publish(options.platform) : null;
358
365
  result = {
359
366
  changed: invalidation.changed,
@@ -395,6 +402,7 @@ var MetroCompilerFrontend = class {
395
402
  this.#planKeys.clear();
396
403
  this.#graph = null;
397
404
  this.#host = null;
405
+ this.#registry = null;
398
406
  this.#projectGeneration = null;
399
407
  }
400
408
  async #loadCompilerProject(options, importer, diagnostics) {
@@ -457,7 +465,7 @@ var MetroCompilerFrontend = class {
457
465
  if (cache && key) {
458
466
  const cached = await cache.read(key, (value) => {
459
467
  const entry = value;
460
- return entry?.schemaVersion === 1 && entry.sourceHash === sourceHash && Array.isArray(entry.imports) && Array.isArray(entry.requireSpecifiers) && Array.isArray(entry.diagnostics) ? entry : null;
468
+ return entry?.schemaVersion === 2 && entry.sourceHash === sourceHash && Array.isArray(entry.imports) && Array.isArray(entry.requireSpecifiers) && Array.isArray(entry.diagnostics) ? entry : null;
461
469
  });
462
470
  if (cached) {
463
471
  for (const diagnostic of cached.diagnostics) {
@@ -503,7 +511,7 @@ var MetroCompilerFrontend = class {
503
511
  }
504
512
  if (cache && key) {
505
513
  await cache.write(key, {
506
- schemaVersion: 1,
514
+ schemaVersion: 2,
507
515
  sourceHash,
508
516
  imports,
509
517
  requireSpecifiers,
@@ -555,7 +563,7 @@ var MetroCompilerFrontend = class {
555
563
  this.#records.set(id, record);
556
564
  const invalidation = this.#graph?.updateModule(record.input);
557
565
  for (const affected of invalidation?.invalidatedIds ?? [id]) {
558
- this.#refreshEntry(affected);
566
+ await this.#refreshEntry(affected);
559
567
  }
560
568
  if (this.config.watch !== false && this.#scanOptions && retainsLiveGraph(this.#scanOptions)) {
561
569
  this.#watchModule(id);
@@ -564,14 +572,22 @@ var MetroCompilerFrontend = class {
564
572
  visiting.delete(id);
565
573
  }
566
574
  }
567
- #refreshEntry(id) {
575
+ async #refreshEntry(id) {
568
576
  const graph = this.#graph;
569
577
  const host = this.#host;
578
+ const registry = this.#registry;
570
579
  const record = this.#records.get(id);
571
- if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration) return;
580
+ if (!graph || !host || !registry || !record || !this.#scanOptions || !this.#projectGeneration) {
581
+ return;
582
+ }
572
583
  const target = compilerTarget(this.#scanOptions.platform);
584
+ const materialized = materializeModule(graph, id);
585
+ await this.#discovery.prepare(materialized, registry, ({ id: moduleId }) => Static.evaluateComponentModule({
586
+ ...this.config.tamaguiOptions,
587
+ platform: target
588
+ }, moduleId));
573
589
  const plan = lowerModule({
574
- module: materializeModule(graph, id),
590
+ module: materialized,
575
591
  source: record.input.source,
576
592
  target,
577
593
  host,
@@ -617,9 +633,9 @@ var MetroCompilerFrontend = class {
617
633
  #installCaches(options, project, projectSourcesHash) {
618
634
  const platform = options.platform ?? "default";
619
635
  const root = defaultPlanCacheRoot(this.config.projectRoot, platform);
620
- this.#recordCache = new JsonFileCache(join(root, "records"), 1);
636
+ this.#recordCache = new JsonFileCache(join(root, "records"), 2);
621
637
  this.#recordCacheIdentity = contentHash(stableStringify({
622
- schema: 1,
638
+ schema: 2,
623
639
  resolver: this.#resolver.version,
624
640
  babel: userBabelCacheKey(this.config.originalBabelTransformerPath),
625
641
  projectSourcesHash,
@@ -653,7 +669,7 @@ var MetroCompilerFrontend = class {
653
669
  let node = nodes.get(id);
654
670
  if (node === void 0) {
655
671
  const record = this.#records.get(id);
656
- node = record ? moduleClosureNode(record.input) : null;
672
+ node = record ? moduleClosureNode(record.input, { includeExternal: true }) : this.#externalClosure(id);
657
673
  nodes.set(id, node);
658
674
  }
659
675
  return node;
@@ -1 +1 @@
1
- {"version":3,"file":"frontend.js","names":["#cacheBaseRoot","#resolver","#planCache","#recordCache","#enqueue","#scan","#scanOptions","#publishedGeneration","#loadCompilerProject","#projectGeneration","#scanOptionsHash","#installCaches","#watchers","#records","#compileRecord","#report","#tamaguiConfig","#entries","#restorePlans","#graph","#host","#zeroEntryGraph","#reachableFrom","#refreshEntry","#storePlans","#publish","#installWatchers","#releaseGraph","#ensureValidCache","#sourcesAreFresh","#rehydrateZeroCSS","#addDependency","#watchModule","#operationQueue","#planKeys","#recordCacheIdentity","#babelArgs","#babelOptions","#zeroPlanFor","#entryFor","#planCacheStamp"],"sources":["esm/frontend.js"],"sourcesContent":["import { existsSync, watch } from \"node:fs\";\nimport { readFile, readdir, realpath } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport ignore from \"ignore\";\nimport {\n JsonFileCache,\n ModulePlanCache,\n PLAN_CACHE_SCHEMA_VERSION,\n ProjectGraph,\n contentHash,\n defaultPlanCacheRoot,\n isTamaguiSpecifier,\n lowerModule,\n materializeModule,\n moduleClosureDigest,\n moduleClosureNode,\n planCacheKey,\n resolvedModuleId,\n stableStringify,\n yukuFactory\n} from \"@tamagui/compiler-core\";\nimport Static, { createTamaguiCompilerHost } from \"@tamagui/static\";\nimport {\n compileWithUserBabel,\n userBabelCacheKey\n} from \"./babel\";\nimport { zeroModuleKey } from \"./zeroRuntime\";\nimport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n defaultMetroCompilerCacheRoot\n} from \"./compilerCache\";\nimport { metroDiagnostic } from \"./diagnostics\";\nimport {\n createMetroCompilerResolver,\n isCompilerSourceFile,\n moduleSpecifiersFromAst\n} from \"./metroResolver\";\nconst METRO_RECORD_CACHE_VERSION = 1;\nfunction compareCodeUnits(left, right) {\n return left < right ? -1 : left > right ? 1 : 0;\n}\nconst requireFromFrontend = createRequire(\n typeof __filename === \"string\" ? __filename : import.meta.url\n);\nconst compilerImplementationVersions = [\"@tamagui/metro-plugin\", \"@tamagui/static\", \"@tamagui/compiler-core\"].map((packageName) => {\n const { version } = requireFromFrontend(`${packageName}/package.json`);\n return `${packageName}@${version}`;\n});\nfunction scanOptionsHash(options, projectGeneration, projectSourcesHash) {\n return contentHash(JSON.stringify({ options, projectGeneration, projectSourcesHash }));\n}\nconst speculativeWalkExcludedDirs = /* @__PURE__ */ new Set([\n \"__tests__\",\n \"e2e\",\n \"flows\",\n \"plugins\",\n \"screenshots\",\n \"scripts\",\n \"test\",\n \"test-results\",\n \"tests\"\n]);\nasync function walkProjectSources(root) {\n const inherited = [];\n let ancestor = root;\n while (!existsSync(join(ancestor, \".git\"))) {\n const parent = dirname(ancestor);\n if (parent === ancestor) break;\n inherited.unshift(parent);\n ancestor = parent;\n }\n const rootScopes = [];\n for (const dir of inherited) {\n const source = await readFile(join(dir, \".gitignore\"), \"utf8\").catch(() => null);\n if (source) rootScopes.push({ dir, matcher: ignore().add(source) });\n }\n const found = [];\n const stack = [\n { dir: root, scopes: rootScopes }\n ];\n while (stack.length) {\n const { dir, scopes } = stack.pop();\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n let active = scopes;\n if (entries.some((entry) => entry.isFile() && entry.name === \".gitignore\")) {\n const source = await readFile(join(dir, \".gitignore\"), \"utf8\").catch(() => null);\n if (source) active = [...scopes, { dir, matcher: ignore().add(source) }];\n }\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n const isDirectory = entry.isDirectory();\n if (isDirectory && speculativeWalkExcludedDirs.has(entry.name)) continue;\n if (!isDirectory && !(entry.isFile() && isCompilerSourceFile(entry.name))) continue;\n if (!isDirectory && (/(?:^|[-.])(?:probe|run|spec|tests?)(?:[-.]|$)/i.test(entry.name) || /\\.(?:build|config|workspace)\\.[cm]?[jt]sx?$/.test(entry.name))) {\n continue;\n }\n const path = join(dir, entry.name);\n let ignored = false;\n for (const scope of active) {\n const relativePath = relative(scope.dir, path);\n if (!relativePath || relativePath.startsWith(\"..\")) continue;\n const candidate = relativePath.split(sep).join(\"/\") + (isDirectory ? \"/\" : \"\");\n if (scope.matcher.ignores(candidate)) {\n ignored = true;\n break;\n }\n }\n if (ignored) continue;\n if (isDirectory) stack.push({ dir: path, scopes: active });\n else found.push(path);\n }\n }\n return found.sort(compareCodeUnits);\n}\nfunction compilerTarget(platform) {\n return platform === \"web\" ? \"web\" : \"native\";\n}\nfunction retainsLiveGraph(options) {\n return options.dev && options.hot;\n}\nclass MetroCompilerFrontend {\n constructor(config) {\n this.config = config;\n this.#cacheBaseRoot = config.cacheRoot ?? defaultMetroCompilerCacheRoot(config.projectRoot);\n this.#resolver = createMetroCompilerResolver(config);\n }\n config;\n #cacheBaseRoot;\n #entries = /* @__PURE__ */ new Map();\n #records = /* @__PURE__ */ new Map();\n #watchers = /* @__PURE__ */ new Map();\n #resolver;\n #graph = null;\n #host = null;\n #projectGeneration = null;\n #publishedGeneration = null;\n #scanOptions = null;\n #scanOptionsHash = null;\n #operationQueue = Promise.resolve();\n #tamaguiConfig = null;\n #zeroEntryGraph = null;\n #planKeys = /* @__PURE__ */ new Map();\n #recordCache = null;\n #recordCacheIdentity = null;\n #planCache = null;\n #planCacheStamp = null;\n get metroResolverVersion() {\n return this.#resolver.version;\n }\n /**\n * Per-file cache accounting for the last scan. The point of these caches is\n * that one edited module leaves every other module's entry valid, and this is\n * how that is observed rather than assumed.\n */\n get compileCacheStats() {\n const empty = { hits: 0, misses: 0, writes: 0 };\n return {\n plans: this.#planCache?.stats ?? empty,\n records: this.#recordCache?.stats ?? empty\n };\n }\n cacheRootFor(platform) {\n return join(this.#cacheBaseRoot, platform ?? \"default\");\n }\n scan(options) {\n return this.#enqueue(() => this.#scan(options));\n }\n async #scan(options, preparedProject, preparedProjectSources) {\n this.#scanOptions = options;\n this.#publishedGeneration = null;\n const diagnostics = [];\n const entryRoots = (await Promise.all(\n options.entryFiles.map((path) => realpath(resolve(this.config.projectRoot, path)))\n )).sort(compareCodeUnits);\n const compilerProject = preparedProject ?? await this.#loadCompilerProject(options, entryRoots[0], diagnostics);\n this.#projectGeneration = compilerProject.generation;\n const projectSources = preparedProjectSources ?? await walkProjectSources(this.config.projectRoot);\n const projectSourcesHash = contentHash(JSON.stringify(projectSources));\n this.#scanOptionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n projectSourcesHash\n );\n this.#installCaches(options, compilerProject, projectSourcesHash);\n const speculativeRoots = /* @__PURE__ */ new Set();\n for (const file of projectSources) {\n try {\n const id = await realpath(file);\n if (!entryRoots.includes(id)) speculativeRoots.add(id);\n } catch {\n }\n }\n const roots = [.../* @__PURE__ */ new Set([...entryRoots, ...speculativeRoots])].sort(\n compareCodeUnits\n );\n const queue = [...roots];\n const queued = new Set(queue);\n for (const watcher of this.#watchers.values()) watcher.close();\n this.#watchers.clear();\n this.#records.clear();\n while (queue.length) {\n const path = queue.shift();\n try {\n const record = await this.#compileRecord(path, options, diagnostics);\n this.#records.set(record.input.id, record);\n for (const dependency of record.input.imports) {\n if (dependency.external || !isCompilerSourceFile(dependency.resolvedId) || queued.has(dependency.resolvedId)) {\n continue;\n }\n queued.add(dependency.resolvedId);\n queue.push(dependency.resolvedId);\n }\n } catch (error) {\n if (speculativeRoots.has(path)) continue;\n const diagnostic = metroDiagnostic(\n \"metro/transform-failed\",\n `Failed to compile ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n if (!compilerProject.projectInfo.tamaguiConfig || !compilerProject.projectInfo.components) {\n throw new Error(\"Metro compiler project has no Tamagui config or components\");\n }\n this.#tamaguiConfig = compilerProject.projectInfo.tamaguiConfig;\n this.#entries.clear();\n const unplanned = await this.#restorePlans(options);\n const zero = this.config.zero;\n this.#graph = null;\n this.#host = null;\n if (unplanned.length || retainsLiveGraph(options)) {\n this.#graph = new ProjectGraph(yukuFactory, {\n modules: [...this.#records.values()].map(({ input }) => input)\n });\n this.#host = createTamaguiCompilerHost({\n target: compilerTarget(options.platform),\n tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,\n components: compilerProject.projectInfo.components,\n componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({\n moduleName,\n resolvedId: id\n })),\n disablePartialExtraction: compilerProject.disablePartialExtraction,\n experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,\n zeroRuntime: compilerProject.zeroRuntime\n });\n if (zero) {\n if (zero.isEnforcing) {\n Static.assertZeroConfigDrivers(compilerProject.projectInfo.tamaguiConfig);\n }\n zero.plansRestoredFromCache = false;\n zero.configCSS = compilerProject.projectInfo.tamaguiConfig.getCSS?.() ?? \"\";\n zero.artifact.clearGraphs();\n zero.bridges.clear();\n zero.violations.length = 0;\n zero.transformed.clear();\n zero.erasedExports.clear();\n this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId));\n }\n for (const id of unplanned) this.#refreshEntry(id);\n await this.#storePlans(unplanned);\n }\n if (zero) {\n Static.writeZeroViolationReport(zero.resolved.outDir, \"metro-zero\", {\n integration: \"metro-web\",\n mode: zero.isEnforcing ? \"enforce\" : \"report\",\n violations: zero.violations\n });\n if (zero.isEnforcing && zero.violations.length) {\n throw new Error(Static.formatZeroViolations(zero.violations));\n }\n }\n const totalFound = [...this.#entries.values()].reduce(\n (sum, entry) => sum + entry.plan.stats.found,\n 0\n );\n if (this.#entries.size > 0 && totalFound === 0) {\n const componentNames = compilerProject.componentModules.map(\n ({ moduleName }) => moduleName\n );\n const cjsComponentImporters = [...this.#records.values()].filter(\n (record) => record.requireSpecifiers.some(\n (specifier) => componentNames.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`)\n )\n )\n ).length;\n if (cjsComponentImporters > 0) {\n const diagnostic = metroDiagnostic(\n \"metro/no-linked-components\",\n `The Tamagui compiler linked 0 components across ${this.#entries.size} modules even though ${cjsComponentImporters} module(s) reference ${componentNames.join(\", \")} through require() calls. Metro compiled modules to CommonJS before the compiler could analyze them, so component imports cannot be linked and nothing will be optimized. Enable experimentalImportSupport in your transformer's getTransformOptions (Expo enables it by default) to restore Tamagui compilation.`\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n const generation = await this.#publish(options.platform);\n const moduleIds = [...this.#records.keys()].sort(compareCodeUnits);\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#installWatchers();\n } else if (!retainsLiveGraph(options)) {\n this.#releaseGraph();\n }\n return {\n generation,\n moduleIds,\n diagnostics\n };\n }\n ensureValidCache(options) {\n return this.#enqueue(() => this.#ensureValidCache(options));\n }\n async #ensureValidCache(options) {\n const diagnostics = [];\n const firstEntry = options.entryFiles[0];\n const importer = firstEntry ? await realpath(resolve(this.config.projectRoot, firstEntry)) : this.config.projectRoot;\n const compilerProject = await this.#loadCompilerProject(\n options,\n importer,\n diagnostics\n );\n const cache = new MetroCompilerCache(this.cacheRootFor(options.platform));\n const validation = await cache.validate();\n const projectSources = await walkProjectSources(this.config.projectRoot);\n const optionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n contentHash(JSON.stringify(projectSources))\n );\n if (validation.valid && validation.generation && validation.optionsHash === optionsHash && await this.#sourcesAreFresh(validation.sourceHashes) && (!retainsLiveGraph(options) && !this.#graph || this.#publishedGeneration && this.#scanOptionsHash === optionsHash) && // A zero build owns the one CSS artifact, and its contents are produced by\n // the scan. Reusing a published plan without restoring the artifact would\n // emit one missing every rule this process never collected, while still\n // deriving TAMAGUI_DID_OUTPUT_CSS from it. The sidecar carries exactly\n // those side effects; without it there is nothing safe to reuse.\n await this.#rehydrateZeroCSS(cache, validation.generation)) {\n this.#publishedGeneration = validation.generation;\n this.#scanOptions = options;\n this.#scanOptionsHash = optionsHash;\n this.#projectGeneration = compilerProject.generation;\n return {\n generation: validation.generation,\n moduleIds: validation.moduleIds,\n diagnostics\n };\n }\n for (const diagnostic of validation.diagnostics) this.#report(diagnostic);\n await cache.discardManifest();\n return await this.#scan(options, compilerProject, projectSources);\n }\n async updateFile(path) {\n let result = {\n changed: false,\n affectedIds: [],\n generation: null\n };\n return this.#enqueue(async () => {\n const graph = this.#graph;\n const options = this.#scanOptions;\n if (!graph || !options) return result;\n let record;\n const diagnostics = [];\n try {\n record = await this.#compileRecord(path, options, diagnostics);\n } catch (error) {\n if (error.code === \"ENOENT\") {\n const id = resolvedModuleId(resolve(path));\n const invalidation2 = graph.removeModule(id);\n this.#watchers.get(id)?.close();\n this.#watchers.delete(id);\n this.#records.delete(id);\n this.#entries.delete(id);\n for (const affected of invalidation2.invalidatedIds) {\n if (affected !== id) this.#refreshEntry(affected);\n }\n const generation2 = await this.#publish(options.platform);\n result = {\n changed: invalidation2.changed,\n affectedIds: invalidation2.invalidatedIds,\n generation: generation2\n };\n return result;\n }\n const diagnostic = metroDiagnostic(\n \"metro/transform-failed\",\n `Failed to update ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n );\n this.#report(diagnostic);\n return result;\n }\n for (const dependency of record.input.imports) {\n if (dependency.external || !isCompilerSourceFile(dependency.resolvedId) || this.#records.has(dependency.resolvedId)) {\n continue;\n }\n await this.#addDependency(dependency.resolvedId, options, diagnostics);\n }\n this.#records.set(record.input.id, record);\n const invalidation = graph.updateModule(record.input);\n for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected);\n const generation = invalidation.changed ? await this.#publish(options.platform) : null;\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation\n };\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#watchModule(record.input.id);\n }\n return result;\n });\n }\n /** A published plan only applies while every recorded module source is unchanged. */\n async #sourcesAreFresh(sourceHashes) {\n const checks = Object.entries(sourceHashes).map(async ([moduleId, sourceHash]) => {\n try {\n return contentHash(await readFile(moduleId, \"utf8\")) === sourceHash;\n } catch {\n return false;\n }\n });\n return (await Promise.all(checks)).every(Boolean);\n }\n #enqueue(operation) {\n const queued = this.#operationQueue.then(operation);\n this.#operationQueue = queued.then(\n () => void 0,\n () => void 0\n );\n return queued;\n }\n close() {\n return this.#enqueue(async () => {\n this.#releaseGraph();\n });\n }\n #releaseGraph() {\n for (const watcher of this.#watchers.values()) watcher.close();\n this.#watchers.clear();\n this.#entries.clear();\n this.#records.clear();\n this.#planKeys.clear();\n this.#graph = null;\n this.#host = null;\n this.#projectGeneration = null;\n }\n async #loadCompilerProject(options, importer, diagnostics) {\n const target = compilerTarget(options.platform);\n if (this.config.loadCompilerProject) {\n return await this.config.loadCompilerProject(target, options.platform);\n }\n return Static.loadCompilerProject({\n root: this.config.projectRoot,\n target,\n options: this.config.tamaguiOptions ?? {},\n hostVersions: compilerImplementationVersions,\n missingProjectMessage: \"Unable to load the Tamagui project for Metro compilation\",\n generation: (projectInfo, componentModules, normalizedOptions) => {\n return contentHash(\n JSON.stringify({\n cacheVersion: METRO_COMPILER_CACHE_VERSION,\n compilerImplementationVersions,\n componentModules,\n configCss: projectInfo.tamaguiConfig?.getCSS?.() ?? \"\",\n disablePartialExtraction: !!normalizedOptions.disablePartialExtraction,\n experimentalNativeFastPath: target === \"native\" && normalizedOptions.experimental?.nativeFastPath === true,\n target,\n // the host's diagnostics are mode-aware, so a plan built in one mode is\n // not a plan the other mode may reuse\n zeroRuntime: !!this.config.zero\n })\n );\n },\n resolveComponents: async (moduleNames) => {\n const componentModules = [];\n for (const moduleName of moduleNames) {\n try {\n const resolution = this.#resolver.resolve(\n importer,\n { specifier: moduleName, isESMImport: true },\n options.platform\n );\n if (!resolution) continue;\n componentModules.push({ moduleName, id: resolution.resolvedId });\n } catch (error) {\n const diagnostic = metroDiagnostic(\n \"metro/resolve-failed\",\n `Failed to resolve compiler component ${moduleName}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: importer, dependency: moduleName }\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n return componentModules;\n }\n });\n }\n async #compileRecord(rawPath, options, diagnostics) {\n const path = await realpath(resolve(rawPath));\n const source = await readFile(path, \"utf8\");\n const sourceHash = contentHash(source);\n const id = resolvedModuleId(path);\n const cache = this.#recordCache;\n const identity = this.#recordCacheIdentity;\n const key = cache && identity ? contentHash(`${identity}\\0${sourceHash}`) : null;\n if (cache && key) {\n const cached = await cache.read(key, (value) => {\n const entry = value;\n return entry?.schemaVersion === METRO_RECORD_CACHE_VERSION && entry.sourceHash === sourceHash && Array.isArray(entry.imports) && Array.isArray(entry.requireSpecifiers) && Array.isArray(entry.diagnostics) ? entry : null;\n });\n if (cached) {\n for (const diagnostic of cached.diagnostics) {\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n return {\n input: { id, source, imports: cached.imports },\n sourceHash,\n requireSpecifiers: cached.requireSpecifiers\n };\n }\n }\n const args = this.#babelArgs(path, source, options);\n const compiled = await compileWithUserBabel(\n this.config.originalBabelTransformerPath,\n args\n );\n const imports = [];\n const requireSpecifiers = [];\n const recordDiagnostics = [];\n for (const dependency of moduleSpecifiersFromAst(compiled.result.ast)) {\n if (!dependency.isESMImport) requireSpecifiers.push(dependency.specifier);\n try {\n const resolution = this.#resolver.resolve(path, dependency, options.platform);\n if (!resolution) continue;\n imports.push({\n specifier: resolution.specifier,\n resolvedId: resolvedModuleId(resolution.resolvedId),\n external: resolution.external\n });\n } catch (error) {\n recordDiagnostics.push(\n metroDiagnostic(\n \"metro/resolve-failed\",\n `Failed to resolve ${dependency.specifier} from ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path, dependency: dependency.specifier }\n )\n );\n }\n }\n for (const diagnostic of recordDiagnostics) {\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n if (cache && key) {\n await cache.write(key, {\n schemaVersion: METRO_RECORD_CACHE_VERSION,\n sourceHash,\n imports,\n requireSpecifiers,\n diagnostics: recordDiagnostics\n });\n }\n return {\n // The graph and plans operate on raw source: workers apply plan edits to\n // the raw module before their own Babel pass, so plans never depend on\n // this process's Babel output matching the workers' byte for byte.\n input: { id, source, imports },\n sourceHash,\n requireSpecifiers\n };\n }\n #babelOptions(options) {\n const transformer = this.config.transformer ?? {};\n return {\n ...options.transform,\n dev: options.dev,\n hot: options.hot,\n platform: options.platform,\n projectRoot: this.config.projectRoot,\n enableBabelRCLookup: transformer.enableBabelRCLookup ?? true,\n enableBabelRuntime: transformer.enableBabelRuntime ?? true,\n hermesParser: transformer.hermesParser ?? false,\n publicPath: transformer.publicPath ?? \"/assets\"\n };\n }\n #babelArgs(filename, src, options) {\n return { filename, src, plugins: [], options: this.#babelOptions(options) };\n }\n async #addDependency(id, options, diagnostics, visiting = /* @__PURE__ */ new Set()) {\n if (this.#records.has(id) || visiting.has(id)) return;\n visiting.add(id);\n try {\n const record = await this.#compileRecord(id, options, diagnostics);\n for (const dependency of record.input.imports) {\n if (!dependency.external && isCompilerSourceFile(dependency.resolvedId)) {\n await this.#addDependency(dependency.resolvedId, options, diagnostics, visiting);\n }\n }\n this.#records.set(id, record);\n const invalidation = this.#graph?.updateModule(record.input);\n for (const affected of invalidation?.invalidatedIds ?? [id]) {\n this.#refreshEntry(affected);\n }\n if (this.config.watch !== false && this.#scanOptions && retainsLiveGraph(this.#scanOptions)) {\n this.#watchModule(id);\n }\n } finally {\n visiting.delete(id);\n }\n }\n #refreshEntry(id) {\n const graph = this.#graph;\n const host = this.#host;\n const record = this.#records.get(id);\n if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration)\n return;\n const target = compilerTarget(this.#scanOptions.platform);\n const plan = lowerModule({\n module: materializeModule(graph, id),\n source: record.input.source,\n target,\n host,\n options: { projectGeneration: this.#projectGeneration }\n });\n const zeroPlan = this.#zeroPlanFor(id, record.input.source, plan);\n this.#entries.set(id, this.#entryFor(id, record, zeroPlan ?? plan));\n }\n /**\n * One plan becomes one cache entry the same way whether the plan was just\n * lowered or read back off disk, so a restored build reports exactly the\n * diagnostics a fresh one did.\n */\n #entryFor(id, record, plan) {\n return {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n moduleId: id,\n sourceHash: record.sourceHash,\n plan,\n diagnostics: plan.diagnostics.map(\n ({ code, message, dependencyId, span, component }) => {\n const { line, column } = Static.offsetToLineColumn(\n record.input.source,\n span.start\n );\n return metroDiagnostic(\n code.startsWith(\"linked/\") ? \"metro/resolve-failed\" : \"metro/transform-failed\",\n message,\n { moduleId: id, dependency: dependencyId, span, line, column, component }\n );\n }\n )\n };\n }\n /**\n * Both per-file caches for this scan. A project with no content stamp gets\n * neither: a stamp that cannot see a config change would serve styles built\n * against the old config, so the answer is no cache rather than a partial one.\n *\n * Zero builds opt out of the plan cache because a zero plan is produced\n * alongside side effects that do not travel in the plan - the CSS artifact,\n * the bridge manifest, the violation list - so replaying one module's plan\n * without them would emit an artifact missing its rules.\n */\n #installCaches(options, project, projectSourcesHash) {\n const platform = options.platform ?? \"default\";\n const root = defaultPlanCacheRoot(this.config.projectRoot, platform);\n this.#recordCache = new JsonFileCache(\n join(root, \"records\"),\n METRO_RECORD_CACHE_VERSION\n );\n this.#recordCacheIdentity = contentHash(\n stableStringify({\n schema: METRO_RECORD_CACHE_VERSION,\n resolver: this.#resolver.version,\n babel: userBabelCacheKey(this.config.originalBabelTransformerPath),\n // resolutions depend on which files exist, so the walked source list is\n // part of a record's identity exactly as it is for the plan manifest\n projectSourcesHash,\n platform,\n transform: this.#babelOptions(options)\n })\n );\n const stamp = project.cacheStamp;\n const usePlanCache = typeof stamp === \"string\" && stamp !== \"\" && !this.config.zero;\n this.#planCache = usePlanCache ? new ModulePlanCache(join(root, \"plans\")) : null;\n this.#planCacheStamp = usePlanCache ? stamp : null;\n }\n /**\n * Fills `#entries` from disk for every module whose whole compile input is\n * unchanged, and returns the ids that still have to be compiled. This is the\n * per-file property: one edited module leaves every other module's entry\n * valid, where the plan manifest would have discarded all of them.\n */\n async #restorePlans(options) {\n this.#planKeys.clear();\n const cache = this.#planCache;\n const stamp = this.#planCacheStamp;\n if (!cache || !stamp) return [...this.#records.keys()].sort(compareCodeUnits);\n const target = compilerTarget(options.platform);\n const identity = {\n stamp,\n target,\n structuralPassHash: `${target}-noop-v1`\n };\n const nodes = /* @__PURE__ */ new Map();\n const lookup = (id) => {\n let node = nodes.get(id);\n if (node === void 0) {\n const record = this.#records.get(id);\n node = record ? moduleClosureNode(record.input) : null;\n nodes.set(id, node);\n }\n return node;\n };\n const memo = /* @__PURE__ */ new Map();\n const unplanned = [];\n for (const id of [...this.#records.keys()].sort(compareCodeUnits)) {\n const record = this.#records.get(id);\n const digest = moduleClosureDigest(id, lookup, memo);\n const key = digest && planCacheKey(identity, id, digest);\n const entry = key && digest ? await cache.read(key, id, digest) : null;\n if (entry) {\n this.#entries.set(id, this.#entryFor(id, record, entry.plan));\n continue;\n }\n if (key && digest) this.#planKeys.set(id, { key, digest });\n unplanned.push(id);\n }\n return unplanned;\n }\n async #storePlans(ids) {\n const cache = this.#planCache;\n if (!cache) return;\n const pending = ids.flatMap((id) => {\n const entry = this.#entries.get(id);\n const key = this.#planKeys.get(id);\n return entry && key ? [{ id, entry, key }] : [];\n });\n for (let index = 0; index < pending.length; index += 32) {\n await Promise.all(\n pending.slice(index, index + 32).map(\n ({ id, entry, key }) => cache.write(key.key, {\n schemaVersion: PLAN_CACHE_SCHEMA_VERSION,\n moduleId: id,\n closureDigest: key.digest,\n plan: entry.plan\n })\n )\n );\n }\n }\n /** Modules reachable from the bundle's entry, over the frontend's own graph. */\n #reachableFrom(roots) {\n const reached = /* @__PURE__ */ new Set();\n const queue = [...roots];\n while (queue.length) {\n const id = queue.pop();\n if (reached.has(id)) continue;\n reached.add(id);\n for (const dependency of this.#records.get(id)?.input.imports ?? []) {\n if (!dependency.external) queue.push(dependency.resolvedId);\n }\n }\n return reached;\n }\n /**\n * The zero transform for one module, returning a plan whose edits also carry\n * the static Theme lowering, the island bridge, and reference erasure.\n */\n #zeroPlanFor(id, source, plan) {\n const zero = this.config.zero;\n const config = this.#tamaguiConfig;\n if (!zero || !config) return null;\n if (zero.islandBuild) {\n zero.artifact.setIslandModuleCSS(zero.islandBuild, id, plan.css);\n return null;\n }\n if (this.#zeroEntryGraph && !this.#zeroEntryGraph.has(id)) return null;\n const relativePath = relative(this.config.projectRoot, id);\n if (relativePath === \"\" || relativePath.startsWith(\"..\") || relativePath.split(/[\\\\/]/).includes(\"node_modules\")) {\n return null;\n }\n const result = Static.transformZeroModule({\n mode: zero.isEnforcing ? \"enforce\" : \"report\",\n id,\n root: this.config.projectRoot,\n source,\n plan,\n config,\n isTamaguiSpecifier,\n resolveIslandLoader: (specifier) => {\n const islandId = zero.loaderIds.get(zeroModuleKey(resolve(id, \"..\", specifier)));\n return islandId ? { islandId } : null;\n },\n resolveIslandModule: (specifier) => zero.islandModuleIds.get(zeroModuleKey(resolve(id, \"..\", specifier))) ?? null\n });\n zero.transformed.add(id);\n if (result.erased.exports.length) {\n zero.erasedExports.set(id, result.erased.exports);\n }\n for (const violation of result.violations) {\n const { line, column } = Static.offsetToLineColumn(source, violation.span.start);\n zero.violations.push({\n file: relativePath,\n line,\n column,\n rule: violation.rule,\n code: violation.code,\n component: violation.component,\n message: violation.message\n });\n }\n if (result.violations.length || !zero.isEnforcing) return null;\n Static.mergeIslandBridges(zero.bridges, result.bridges);\n for (const [identifier, rules] of result.bridgeCSS) {\n zero.artifact.setBridgeRules(identifier, rules);\n }\n zero.artifact.setZeroModuleCSS(id, plan.css);\n return { ...plan, edits: [...plan.edits, ...result.edits] };\n }\n async #publish(platform) {\n const cache = new MetroCompilerCache(this.cacheRootFor(platform));\n const generation = await cache.publish(\n platform,\n [...this.#entries.values()],\n this.#scanOptionsHash ?? \"\"\n );\n const zero = this.config.zero;\n if (zero && !zero.islandBuild) {\n await cache.publishZeroCSS({\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n configCSS: zero.configCSS,\n zeroModuleCSS: Object.fromEntries(zero.artifact.zeroModuleEntries()),\n bridgeCSS: Object.fromEntries(zero.artifact.bridgeEntries()),\n bridges: Object.fromEntries(zero.bridges)\n });\n }\n this.#publishedGeneration = generation;\n return generation;\n }\n /**\n * Restores the zero build's CSS side effects from the sidecar published with\n * this plan generation. Returns false when there is nothing trustworthy to\n * restore, which sends the caller to a full scan.\n */\n async #rehydrateZeroCSS(cache, generation) {\n const zero = this.config.zero;\n if (!zero || zero.islandBuild) return true;\n const sidecar = await cache.readZeroCSS(generation);\n if (!sidecar) return false;\n zero.artifact.clearGraphs();\n zero.bridges.clear();\n zero.violations.length = 0;\n zero.configCSS = sidecar.configCSS;\n for (const [moduleId, css] of Object.entries(sidecar.zeroModuleCSS)) {\n zero.artifact.setZeroModuleCSS(moduleId, css);\n }\n for (const [bridgeId, css] of Object.entries(sidecar.bridgeCSS)) {\n zero.artifact.setBridgeRules(bridgeId, css);\n }\n for (const [islandId, bridges] of Object.entries(sidecar.bridges)) {\n zero.bridges.set(islandId, bridges);\n }\n zero.plansRestoredFromCache = true;\n return true;\n }\n #installWatchers() {\n for (const id of this.#records.keys()) this.#watchModule(id);\n }\n #watchModule(id) {\n if (this.#watchers.has(id)) return;\n try {\n const watcher = watch(id, { persistent: false }, () => {\n void this.updateFile(id);\n });\n watcher.unref();\n this.#watchers.set(id, watcher);\n } catch {\n }\n }\n #report(diagnostic) {\n this.config.reportDiagnostic?.(diagnostic);\n }\n}\nfunction describeMetroCompilerRoot(projectRoot, moduleId) {\n const path = relative(projectRoot, moduleId);\n return path.startsWith(\"..\") ? basename(moduleId) : path;\n}\nexport {\n METRO_RECORD_CACHE_VERSION,\n MetroCompilerFrontend,\n describeMetroCompilerRoot\n};\n//# sourceMappingURL=frontend.js.map\n"],"mappings":"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"}
1
+ {"version":3,"file":"frontend.js","names":["#cacheBaseRoot","#resolver","#planCache","#recordCache","#enqueue","#scan","#scanOptions","#publishedGeneration","#loadCompilerProject","#projectGeneration","#scanOptionsHash","#installCaches","#watchers","#records","#compileRecord","#report","#tamaguiConfig","#entries","#restorePlans","#graph","#host","#discovery","#registry","#zeroEntryGraph","#reachableFrom","#refreshEntry","#storePlans","#publish","#installWatchers","#releaseGraph","#ensureValidCache","#sourcesAreFresh","#rehydrateZeroCSS","#addDependency","#watchModule","#operationQueue","#planKeys","#recordCacheIdentity","#babelArgs","#babelOptions","#zeroPlanFor","#entryFor","#planCacheStamp","#externalClosure"],"sources":["esm/frontend.js"],"sourcesContent":["import { existsSync, watch } from \"node:fs\";\nimport { readFile, readdir, realpath } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport ignore from \"ignore\";\nimport {\n JsonFileCache,\n ModulePlanCache,\n PLAN_CACHE_SCHEMA_VERSION,\n ProjectGraph,\n contentHash,\n createExternalClosureLookup,\n defaultPlanCacheRoot,\n isTamaguiSpecifier,\n lowerModule,\n materializeModule,\n moduleClosureDigest,\n moduleClosureNode,\n planCacheKey,\n resolvedModuleId,\n stableStringify,\n yukuFactory\n} from \"@tamagui/compiler-core\";\nimport Static, {\n ComponentDiscovery,\n createComponentRegistry,\n createTamaguiCompilerHost\n} from \"@tamagui/static\";\nimport {\n compileWithUserBabel,\n userBabelCacheKey\n} from \"./babel\";\nimport { zeroModuleKey } from \"./zeroRuntime\";\nimport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n defaultMetroCompilerCacheRoot\n} from \"./compilerCache\";\nimport { metroDiagnostic } from \"./diagnostics\";\nimport {\n createMetroCompilerResolver,\n isCompilerSourceFile,\n moduleSpecifiersFromAst\n} from \"./metroResolver\";\nconst METRO_RECORD_CACHE_VERSION = 2;\nfunction compareCodeUnits(left, right) {\n return left < right ? -1 : left > right ? 1 : 0;\n}\nconst requireFromFrontend = createRequire(\n typeof __filename === \"string\" ? __filename : import.meta.url\n);\nconst compilerImplementationVersions = [\"@tamagui/metro-plugin\", \"@tamagui/static\", \"@tamagui/compiler-core\"].map((packageName) => {\n const { version } = requireFromFrontend(`${packageName}/package.json`);\n return `${packageName}@${version}`;\n});\nfunction scanOptionsHash(options, projectGeneration, projectSourcesHash) {\n return contentHash(JSON.stringify({ options, projectGeneration, projectSourcesHash }));\n}\nconst speculativeWalkExcludedDirs = /* @__PURE__ */ new Set([\n \"__tests__\",\n \"e2e\",\n \"flows\",\n \"plugins\",\n \"screenshots\",\n \"scripts\",\n \"test\",\n \"test-results\",\n \"tests\"\n]);\nasync function walkProjectSources(root) {\n const inherited = [];\n let ancestor = root;\n while (!existsSync(join(ancestor, \".git\"))) {\n const parent = dirname(ancestor);\n if (parent === ancestor) break;\n inherited.unshift(parent);\n ancestor = parent;\n }\n const rootScopes = [];\n for (const dir of inherited) {\n const source = await readFile(join(dir, \".gitignore\"), \"utf8\").catch(() => null);\n if (source) rootScopes.push({ dir, matcher: ignore().add(source) });\n }\n const found = [];\n const stack = [\n { dir: root, scopes: rootScopes }\n ];\n while (stack.length) {\n const { dir, scopes } = stack.pop();\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n let active = scopes;\n if (entries.some((entry) => entry.isFile() && entry.name === \".gitignore\")) {\n const source = await readFile(join(dir, \".gitignore\"), \"utf8\").catch(() => null);\n if (source) active = [...scopes, { dir, matcher: ignore().add(source) }];\n }\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n const isDirectory = entry.isDirectory();\n if (isDirectory && speculativeWalkExcludedDirs.has(entry.name)) continue;\n if (!isDirectory && !(entry.isFile() && isCompilerSourceFile(entry.name))) continue;\n if (!isDirectory && (/(?:^|[-.])(?:probe|run|spec|tests?)(?:[-.]|$)/i.test(entry.name) || /\\.(?:build|config|workspace)\\.[cm]?[jt]sx?$/.test(entry.name))) {\n continue;\n }\n const path = join(dir, entry.name);\n let ignored = false;\n for (const scope of active) {\n const relativePath = relative(scope.dir, path);\n if (!relativePath || relativePath.startsWith(\"..\")) continue;\n const candidate = relativePath.split(sep).join(\"/\") + (isDirectory ? \"/\" : \"\");\n if (scope.matcher.ignores(candidate)) {\n ignored = true;\n break;\n }\n }\n if (ignored) continue;\n if (isDirectory) stack.push({ dir: path, scopes: active });\n else found.push(path);\n }\n }\n return found.sort(compareCodeUnits);\n}\nfunction compilerTarget(platform) {\n return platform === \"web\" ? \"web\" : \"native\";\n}\nfunction retainsLiveGraph(options) {\n return options.dev && options.hot;\n}\nclass MetroCompilerFrontend {\n constructor(config) {\n this.config = config;\n this.#cacheBaseRoot = config.cacheRoot ?? defaultMetroCompilerCacheRoot(config.projectRoot);\n this.#resolver = createMetroCompilerResolver(config);\n }\n config;\n #cacheBaseRoot;\n #entries = /* @__PURE__ */ new Map();\n #records = /* @__PURE__ */ new Map();\n #watchers = /* @__PURE__ */ new Map();\n #resolver;\n #graph = null;\n #host = null;\n #registry = null;\n #discovery = new ComponentDiscovery();\n #externalClosure = createExternalClosureLookup();\n #projectGeneration = null;\n #publishedGeneration = null;\n #scanOptions = null;\n #scanOptionsHash = null;\n #operationQueue = Promise.resolve();\n #tamaguiConfig = null;\n #zeroEntryGraph = null;\n #planKeys = /* @__PURE__ */ new Map();\n #recordCache = null;\n #recordCacheIdentity = null;\n #planCache = null;\n #planCacheStamp = null;\n get metroResolverVersion() {\n return this.#resolver.version;\n }\n /**\n * Per-file cache accounting for the last scan. The point of these caches is\n * that one edited module leaves every other module's entry valid, and this is\n * how that is observed rather than assumed.\n */\n get compileCacheStats() {\n const empty = { hits: 0, misses: 0, writes: 0 };\n return {\n plans: this.#planCache?.stats ?? empty,\n records: this.#recordCache?.stats ?? empty\n };\n }\n cacheRootFor(platform) {\n return join(this.#cacheBaseRoot, platform ?? \"default\");\n }\n scan(options) {\n return this.#enqueue(() => this.#scan(options));\n }\n async #scan(options, preparedProject, preparedProjectSources) {\n this.#scanOptions = options;\n this.#publishedGeneration = null;\n const diagnostics = [];\n const entryRoots = (await Promise.all(\n options.entryFiles.map((path) => realpath(resolve(this.config.projectRoot, path)))\n )).sort(compareCodeUnits);\n const compilerProject = preparedProject ?? await this.#loadCompilerProject(options, entryRoots[0], diagnostics);\n this.#projectGeneration = compilerProject.generation;\n const projectSources = preparedProjectSources ?? await walkProjectSources(this.config.projectRoot);\n const projectSourcesHash = contentHash(JSON.stringify(projectSources));\n this.#scanOptionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n projectSourcesHash\n );\n this.#installCaches(options, compilerProject, projectSourcesHash);\n const speculativeRoots = /* @__PURE__ */ new Set();\n for (const file of projectSources) {\n try {\n const id = await realpath(file);\n if (!entryRoots.includes(id)) speculativeRoots.add(id);\n } catch {\n }\n }\n const roots = [.../* @__PURE__ */ new Set([...entryRoots, ...speculativeRoots])].sort(\n compareCodeUnits\n );\n const queue = [...roots];\n const queued = new Set(queue);\n for (const watcher of this.#watchers.values()) watcher.close();\n this.#watchers.clear();\n this.#records.clear();\n while (queue.length) {\n const path = queue.shift();\n try {\n const record = await this.#compileRecord(path, options, diagnostics);\n this.#records.set(record.input.id, record);\n for (const dependency of record.input.imports) {\n if (dependency.external || !isCompilerSourceFile(dependency.resolvedId) || queued.has(dependency.resolvedId)) {\n continue;\n }\n queued.add(dependency.resolvedId);\n queue.push(dependency.resolvedId);\n }\n } catch (error) {\n if (speculativeRoots.has(path)) continue;\n const diagnostic = metroDiagnostic(\n \"metro/transform-failed\",\n `Failed to compile ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n if (!compilerProject.projectInfo.tamaguiConfig || !compilerProject.projectInfo.components) {\n throw new Error(\"Metro compiler project has no Tamagui config or components\");\n }\n this.#tamaguiConfig = compilerProject.projectInfo.tamaguiConfig;\n this.#entries.clear();\n const unplanned = await this.#restorePlans(options);\n const zero = this.config.zero;\n this.#graph = null;\n this.#host = null;\n if (unplanned.length || retainsLiveGraph(options)) {\n this.#graph = new ProjectGraph(yukuFactory, {\n modules: [...this.#records.values()].map(({ input }) => input)\n });\n const componentModules = compilerProject.componentModules.map(\n ({ moduleName, id }) => ({ moduleName, resolvedId: id })\n );\n this.#discovery.clear();\n this.#registry = createComponentRegistry(\n compilerProject.projectInfo.components,\n componentModules\n );\n this.#host = createTamaguiCompilerHost({\n target: compilerTarget(options.platform),\n tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,\n components: compilerProject.projectInfo.components,\n componentModules,\n registry: this.#registry,\n disablePartialExtraction: compilerProject.disablePartialExtraction,\n experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,\n zeroRuntime: compilerProject.zeroRuntime\n });\n if (zero) {\n if (zero.isEnforcing) {\n Static.assertZeroConfigDrivers(compilerProject.projectInfo.tamaguiConfig);\n }\n zero.plansRestoredFromCache = false;\n zero.configCSS = compilerProject.projectInfo.tamaguiConfig.getCSS?.() ?? \"\";\n zero.artifact.clearGraphs();\n zero.bridges.clear();\n zero.violations.length = 0;\n zero.transformed.clear();\n zero.erasedExports.clear();\n this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId));\n }\n for (const id of unplanned) await this.#refreshEntry(id);\n await this.#storePlans(unplanned);\n }\n if (zero) {\n Static.writeZeroViolationReport(zero.resolved.outDir, \"metro-zero\", {\n integration: \"metro-web\",\n mode: zero.isEnforcing ? \"enforce\" : \"report\",\n violations: zero.violations\n });\n if (zero.isEnforcing && zero.violations.length) {\n throw new Error(Static.formatZeroViolations(zero.violations));\n }\n }\n const totalFound = [...this.#entries.values()].reduce(\n (sum, entry) => sum + entry.plan.stats.found,\n 0\n );\n if (this.#entries.size > 0 && totalFound === 0) {\n const componentNames = compilerProject.componentModules.map(\n ({ moduleName }) => moduleName\n );\n const cjsComponentImporters = [...this.#records.values()].filter(\n (record) => record.requireSpecifiers.some(\n (specifier) => componentNames.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`)\n )\n )\n ).length;\n if (cjsComponentImporters > 0) {\n const diagnostic = metroDiagnostic(\n \"metro/no-linked-components\",\n `The Tamagui compiler linked 0 components across ${this.#entries.size} modules even though ${cjsComponentImporters} module(s) reference ${componentNames.join(\", \")} through require() calls. Metro compiled modules to CommonJS before the compiler could analyze them, so component imports cannot be linked and nothing will be optimized. Enable experimentalImportSupport in your transformer's getTransformOptions (Expo enables it by default) to restore Tamagui compilation.`\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n const generation = await this.#publish(options.platform);\n const moduleIds = [...this.#records.keys()].sort(compareCodeUnits);\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#installWatchers();\n } else if (!retainsLiveGraph(options)) {\n this.#releaseGraph();\n }\n return {\n generation,\n moduleIds,\n diagnostics\n };\n }\n ensureValidCache(options) {\n return this.#enqueue(() => this.#ensureValidCache(options));\n }\n async #ensureValidCache(options) {\n const diagnostics = [];\n const firstEntry = options.entryFiles[0];\n const importer = firstEntry ? await realpath(resolve(this.config.projectRoot, firstEntry)) : this.config.projectRoot;\n const compilerProject = await this.#loadCompilerProject(\n options,\n importer,\n diagnostics\n );\n const cache = new MetroCompilerCache(this.cacheRootFor(options.platform));\n const validation = await cache.validate();\n const projectSources = await walkProjectSources(this.config.projectRoot);\n const optionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n contentHash(JSON.stringify(projectSources))\n );\n if (validation.valid && validation.generation && validation.optionsHash === optionsHash && await this.#sourcesAreFresh(validation.sourceHashes) && (!retainsLiveGraph(options) && !this.#graph || this.#publishedGeneration && this.#scanOptionsHash === optionsHash) && // A zero build owns the one CSS artifact, and its contents are produced by\n // the scan. Reusing a published plan without restoring the artifact would\n // emit one missing every rule this process never collected, while still\n // deriving TAMAGUI_DID_OUTPUT_CSS from it. The sidecar carries exactly\n // those side effects; without it there is nothing safe to reuse.\n await this.#rehydrateZeroCSS(cache, validation.generation)) {\n this.#publishedGeneration = validation.generation;\n this.#scanOptions = options;\n this.#scanOptionsHash = optionsHash;\n this.#projectGeneration = compilerProject.generation;\n return {\n generation: validation.generation,\n moduleIds: validation.moduleIds,\n diagnostics\n };\n }\n for (const diagnostic of validation.diagnostics) this.#report(diagnostic);\n await cache.discardManifest();\n return await this.#scan(options, compilerProject, projectSources);\n }\n async updateFile(path) {\n let result = {\n changed: false,\n affectedIds: [],\n generation: null\n };\n return this.#enqueue(async () => {\n const graph = this.#graph;\n const options = this.#scanOptions;\n if (!graph || !options) return result;\n let record;\n const diagnostics = [];\n try {\n record = await this.#compileRecord(path, options, diagnostics);\n } catch (error) {\n if (error.code === \"ENOENT\") {\n const id = resolvedModuleId(resolve(path));\n const invalidation2 = graph.removeModule(id);\n this.#watchers.get(id)?.close();\n this.#watchers.delete(id);\n this.#records.delete(id);\n this.#entries.delete(id);\n for (const affected of invalidation2.invalidatedIds) {\n if (affected !== id) await this.#refreshEntry(affected);\n }\n const generation2 = await this.#publish(options.platform);\n result = {\n changed: invalidation2.changed,\n affectedIds: invalidation2.invalidatedIds,\n generation: generation2\n };\n return result;\n }\n const diagnostic = metroDiagnostic(\n \"metro/transform-failed\",\n `Failed to update ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n );\n this.#report(diagnostic);\n return result;\n }\n for (const dependency of record.input.imports) {\n if (dependency.external || !isCompilerSourceFile(dependency.resolvedId) || this.#records.has(dependency.resolvedId)) {\n continue;\n }\n await this.#addDependency(dependency.resolvedId, options, diagnostics);\n }\n this.#records.set(record.input.id, record);\n const invalidation = graph.updateModule(record.input);\n for (const affected of invalidation.invalidatedIds)\n await this.#refreshEntry(affected);\n const generation = invalidation.changed ? await this.#publish(options.platform) : null;\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation\n };\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#watchModule(record.input.id);\n }\n return result;\n });\n }\n /** A published plan only applies while every recorded module source is unchanged. */\n async #sourcesAreFresh(sourceHashes) {\n const checks = Object.entries(sourceHashes).map(async ([moduleId, sourceHash]) => {\n try {\n return contentHash(await readFile(moduleId, \"utf8\")) === sourceHash;\n } catch {\n return false;\n }\n });\n return (await Promise.all(checks)).every(Boolean);\n }\n #enqueue(operation) {\n const queued = this.#operationQueue.then(operation);\n this.#operationQueue = queued.then(\n () => void 0,\n () => void 0\n );\n return queued;\n }\n close() {\n return this.#enqueue(async () => {\n this.#releaseGraph();\n });\n }\n #releaseGraph() {\n for (const watcher of this.#watchers.values()) watcher.close();\n this.#watchers.clear();\n this.#entries.clear();\n this.#records.clear();\n this.#planKeys.clear();\n this.#graph = null;\n this.#host = null;\n this.#registry = null;\n this.#projectGeneration = null;\n }\n async #loadCompilerProject(options, importer, diagnostics) {\n const target = compilerTarget(options.platform);\n if (this.config.loadCompilerProject) {\n return await this.config.loadCompilerProject(target, options.platform);\n }\n return Static.loadCompilerProject({\n root: this.config.projectRoot,\n target,\n options: this.config.tamaguiOptions ?? {},\n hostVersions: compilerImplementationVersions,\n missingProjectMessage: \"Unable to load the Tamagui project for Metro compilation\",\n generation: (projectInfo, componentModules, normalizedOptions) => {\n return contentHash(\n JSON.stringify({\n cacheVersion: METRO_COMPILER_CACHE_VERSION,\n compilerImplementationVersions,\n componentModules,\n configCss: projectInfo.tamaguiConfig?.getCSS?.() ?? \"\",\n disablePartialExtraction: !!normalizedOptions.disablePartialExtraction,\n experimentalNativeFastPath: target === \"native\" && normalizedOptions.experimental?.nativeFastPath === true,\n target,\n // the host's diagnostics are mode-aware, so a plan built in one mode is\n // not a plan the other mode may reuse\n zeroRuntime: !!this.config.zero\n })\n );\n },\n resolveComponents: async (moduleNames) => {\n const componentModules = [];\n for (const moduleName of moduleNames) {\n try {\n const resolution = this.#resolver.resolve(\n importer,\n { specifier: moduleName, isESMImport: true },\n options.platform\n );\n if (!resolution) continue;\n componentModules.push({ moduleName, id: resolution.resolvedId });\n } catch (error) {\n const diagnostic = metroDiagnostic(\n \"metro/resolve-failed\",\n `Failed to resolve compiler component ${moduleName}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: importer, dependency: moduleName }\n );\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n }\n return componentModules;\n }\n });\n }\n async #compileRecord(rawPath, options, diagnostics) {\n const path = await realpath(resolve(rawPath));\n const source = await readFile(path, \"utf8\");\n const sourceHash = contentHash(source);\n const id = resolvedModuleId(path);\n const cache = this.#recordCache;\n const identity = this.#recordCacheIdentity;\n const key = cache && identity ? contentHash(`${identity}\\0${sourceHash}`) : null;\n if (cache && key) {\n const cached = await cache.read(key, (value) => {\n const entry = value;\n return entry?.schemaVersion === METRO_RECORD_CACHE_VERSION && entry.sourceHash === sourceHash && Array.isArray(entry.imports) && Array.isArray(entry.requireSpecifiers) && Array.isArray(entry.diagnostics) ? entry : null;\n });\n if (cached) {\n for (const diagnostic of cached.diagnostics) {\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n return {\n input: { id, source, imports: cached.imports },\n sourceHash,\n requireSpecifiers: cached.requireSpecifiers\n };\n }\n }\n const args = this.#babelArgs(path, source, options);\n const compiled = await compileWithUserBabel(\n this.config.originalBabelTransformerPath,\n args\n );\n const imports = [];\n const requireSpecifiers = [];\n const recordDiagnostics = [];\n for (const dependency of moduleSpecifiersFromAst(compiled.result.ast)) {\n if (!dependency.isESMImport) requireSpecifiers.push(dependency.specifier);\n try {\n const resolution = this.#resolver.resolve(path, dependency, options.platform);\n if (!resolution) continue;\n imports.push({\n specifier: resolution.specifier,\n resolvedId: resolvedModuleId(resolution.resolvedId),\n external: resolution.external\n });\n } catch (error) {\n recordDiagnostics.push(\n metroDiagnostic(\n \"metro/resolve-failed\",\n `Failed to resolve ${dependency.specifier} from ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path, dependency: dependency.specifier }\n )\n );\n }\n }\n for (const diagnostic of recordDiagnostics) {\n diagnostics.push(diagnostic);\n this.#report(diagnostic);\n }\n if (cache && key) {\n await cache.write(key, {\n schemaVersion: METRO_RECORD_CACHE_VERSION,\n sourceHash,\n imports,\n requireSpecifiers,\n diagnostics: recordDiagnostics\n });\n }\n return {\n // The graph and plans operate on raw source: workers apply plan edits to\n // the raw module before their own Babel pass, so plans never depend on\n // this process's Babel output matching the workers' byte for byte.\n input: { id, source, imports },\n sourceHash,\n requireSpecifiers\n };\n }\n #babelOptions(options) {\n const transformer = this.config.transformer ?? {};\n return {\n ...options.transform,\n dev: options.dev,\n hot: options.hot,\n platform: options.platform,\n projectRoot: this.config.projectRoot,\n enableBabelRCLookup: transformer.enableBabelRCLookup ?? true,\n enableBabelRuntime: transformer.enableBabelRuntime ?? true,\n hermesParser: transformer.hermesParser ?? false,\n publicPath: transformer.publicPath ?? \"/assets\"\n };\n }\n #babelArgs(filename, src, options) {\n return { filename, src, plugins: [], options: this.#babelOptions(options) };\n }\n async #addDependency(id, options, diagnostics, visiting = /* @__PURE__ */ new Set()) {\n if (this.#records.has(id) || visiting.has(id)) return;\n visiting.add(id);\n try {\n const record = await this.#compileRecord(id, options, diagnostics);\n for (const dependency of record.input.imports) {\n if (!dependency.external && isCompilerSourceFile(dependency.resolvedId)) {\n await this.#addDependency(dependency.resolvedId, options, diagnostics, visiting);\n }\n }\n this.#records.set(id, record);\n const invalidation = this.#graph?.updateModule(record.input);\n for (const affected of invalidation?.invalidatedIds ?? [id]) {\n await this.#refreshEntry(affected);\n }\n if (this.config.watch !== false && this.#scanOptions && retainsLiveGraph(this.#scanOptions)) {\n this.#watchModule(id);\n }\n } finally {\n visiting.delete(id);\n }\n }\n async #refreshEntry(id) {\n const graph = this.#graph;\n const host = this.#host;\n const registry = this.#registry;\n const record = this.#records.get(id);\n if (!graph || !host || !registry || !record || !this.#scanOptions || !this.#projectGeneration) {\n return;\n }\n const target = compilerTarget(this.#scanOptions.platform);\n const materialized = materializeModule(graph, id);\n await this.#discovery.prepare(\n materialized,\n registry,\n ({ id: moduleId }) => Static.evaluateComponentModule(\n { ...this.config.tamaguiOptions, platform: target },\n moduleId\n )\n );\n const plan = lowerModule({\n module: materialized,\n source: record.input.source,\n target,\n host,\n options: { projectGeneration: this.#projectGeneration }\n });\n const zeroPlan = this.#zeroPlanFor(id, record.input.source, plan);\n this.#entries.set(id, this.#entryFor(id, record, zeroPlan ?? plan));\n }\n /**\n * One plan becomes one cache entry the same way whether the plan was just\n * lowered or read back off disk, so a restored build reports exactly the\n * diagnostics a fresh one did.\n */\n #entryFor(id, record, plan) {\n return {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n moduleId: id,\n sourceHash: record.sourceHash,\n plan,\n diagnostics: plan.diagnostics.map(\n ({ code, message, dependencyId, span, component }) => {\n const { line, column } = Static.offsetToLineColumn(\n record.input.source,\n span.start\n );\n return metroDiagnostic(\n code.startsWith(\"linked/\") ? \"metro/resolve-failed\" : \"metro/transform-failed\",\n message,\n { moduleId: id, dependency: dependencyId, span, line, column, component }\n );\n }\n )\n };\n }\n /**\n * Both per-file caches for this scan. A project with no content stamp gets\n * neither: a stamp that cannot see a config change would serve styles built\n * against the old config, so the answer is no cache rather than a partial one.\n *\n * Zero builds opt out of the plan cache because a zero plan is produced\n * alongside side effects that do not travel in the plan - the CSS artifact,\n * the bridge manifest, the violation list - so replaying one module's plan\n * without them would emit an artifact missing its rules.\n */\n #installCaches(options, project, projectSourcesHash) {\n const platform = options.platform ?? \"default\";\n const root = defaultPlanCacheRoot(this.config.projectRoot, platform);\n this.#recordCache = new JsonFileCache(\n join(root, \"records\"),\n METRO_RECORD_CACHE_VERSION\n );\n this.#recordCacheIdentity = contentHash(\n stableStringify({\n schema: METRO_RECORD_CACHE_VERSION,\n resolver: this.#resolver.version,\n babel: userBabelCacheKey(this.config.originalBabelTransformerPath),\n // resolutions depend on which files exist, so the walked source list is\n // part of a record's identity exactly as it is for the plan manifest\n projectSourcesHash,\n platform,\n transform: this.#babelOptions(options)\n })\n );\n const stamp = project.cacheStamp;\n const usePlanCache = typeof stamp === \"string\" && stamp !== \"\" && !this.config.zero;\n this.#planCache = usePlanCache ? new ModulePlanCache(join(root, \"plans\")) : null;\n this.#planCacheStamp = usePlanCache ? stamp : null;\n }\n /**\n * Fills `#entries` from disk for every module whose whole compile input is\n * unchanged, and returns the ids that still have to be compiled. This is the\n * per-file property: one edited module leaves every other module's entry\n * valid, where the plan manifest would have discarded all of them.\n */\n async #restorePlans(options) {\n this.#planKeys.clear();\n const cache = this.#planCache;\n const stamp = this.#planCacheStamp;\n if (!cache || !stamp) return [...this.#records.keys()].sort(compareCodeUnits);\n const target = compilerTarget(options.platform);\n const identity = {\n stamp,\n target,\n structuralPassHash: `${target}-noop-v1`\n };\n const nodes = /* @__PURE__ */ new Map();\n const lookup = (id) => {\n let node = nodes.get(id);\n if (node === void 0) {\n const record = this.#records.get(id);\n node = record ? moduleClosureNode(record.input, { includeExternal: true }) : this.#externalClosure(id);\n nodes.set(id, node);\n }\n return node;\n };\n const memo = /* @__PURE__ */ new Map();\n const unplanned = [];\n for (const id of [...this.#records.keys()].sort(compareCodeUnits)) {\n const record = this.#records.get(id);\n const digest = moduleClosureDigest(id, lookup, memo);\n const key = digest && planCacheKey(identity, id, digest);\n const entry = key && digest ? await cache.read(key, id, digest) : null;\n if (entry) {\n this.#entries.set(id, this.#entryFor(id, record, entry.plan));\n continue;\n }\n if (key && digest) this.#planKeys.set(id, { key, digest });\n unplanned.push(id);\n }\n return unplanned;\n }\n async #storePlans(ids) {\n const cache = this.#planCache;\n if (!cache) return;\n const pending = ids.flatMap((id) => {\n const entry = this.#entries.get(id);\n const key = this.#planKeys.get(id);\n return entry && key ? [{ id, entry, key }] : [];\n });\n for (let index = 0; index < pending.length; index += 32) {\n await Promise.all(\n pending.slice(index, index + 32).map(\n ({ id, entry, key }) => cache.write(key.key, {\n schemaVersion: PLAN_CACHE_SCHEMA_VERSION,\n moduleId: id,\n closureDigest: key.digest,\n plan: entry.plan\n })\n )\n );\n }\n }\n /** Modules reachable from the bundle's entry, over the frontend's own graph. */\n #reachableFrom(roots) {\n const reached = /* @__PURE__ */ new Set();\n const queue = [...roots];\n while (queue.length) {\n const id = queue.pop();\n if (reached.has(id)) continue;\n reached.add(id);\n for (const dependency of this.#records.get(id)?.input.imports ?? []) {\n if (!dependency.external) queue.push(dependency.resolvedId);\n }\n }\n return reached;\n }\n /**\n * The zero transform for one module, returning a plan whose edits also carry\n * the static Theme lowering, the island bridge, and reference erasure.\n */\n #zeroPlanFor(id, source, plan) {\n const zero = this.config.zero;\n const config = this.#tamaguiConfig;\n if (!zero || !config) return null;\n if (zero.islandBuild) {\n zero.artifact.setIslandModuleCSS(zero.islandBuild, id, plan.css);\n return null;\n }\n if (this.#zeroEntryGraph && !this.#zeroEntryGraph.has(id)) return null;\n const relativePath = relative(this.config.projectRoot, id);\n if (relativePath === \"\" || relativePath.startsWith(\"..\") || relativePath.split(/[\\\\/]/).includes(\"node_modules\")) {\n return null;\n }\n const result = Static.transformZeroModule({\n mode: zero.isEnforcing ? \"enforce\" : \"report\",\n id,\n root: this.config.projectRoot,\n source,\n plan,\n config,\n isTamaguiSpecifier,\n resolveIslandLoader: (specifier) => {\n const islandId = zero.loaderIds.get(zeroModuleKey(resolve(id, \"..\", specifier)));\n return islandId ? { islandId } : null;\n },\n resolveIslandModule: (specifier) => zero.islandModuleIds.get(zeroModuleKey(resolve(id, \"..\", specifier))) ?? null\n });\n zero.transformed.add(id);\n if (result.erased.exports.length) {\n zero.erasedExports.set(id, result.erased.exports);\n }\n for (const violation of result.violations) {\n const { line, column } = Static.offsetToLineColumn(source, violation.span.start);\n zero.violations.push({\n file: relativePath,\n line,\n column,\n rule: violation.rule,\n code: violation.code,\n component: violation.component,\n message: violation.message\n });\n }\n if (result.violations.length || !zero.isEnforcing) return null;\n Static.mergeIslandBridges(zero.bridges, result.bridges);\n for (const [identifier, rules] of result.bridgeCSS) {\n zero.artifact.setBridgeRules(identifier, rules);\n }\n zero.artifact.setZeroModuleCSS(id, plan.css);\n return { ...plan, edits: [...plan.edits, ...result.edits] };\n }\n async #publish(platform) {\n const cache = new MetroCompilerCache(this.cacheRootFor(platform));\n const generation = await cache.publish(\n platform,\n [...this.#entries.values()],\n this.#scanOptionsHash ?? \"\"\n );\n const zero = this.config.zero;\n if (zero && !zero.islandBuild) {\n await cache.publishZeroCSS({\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n configCSS: zero.configCSS,\n zeroModuleCSS: Object.fromEntries(zero.artifact.zeroModuleEntries()),\n bridgeCSS: Object.fromEntries(zero.artifact.bridgeEntries()),\n bridges: Object.fromEntries(zero.bridges)\n });\n }\n this.#publishedGeneration = generation;\n return generation;\n }\n /**\n * Restores the zero build's CSS side effects from the sidecar published with\n * this plan generation. Returns false when there is nothing trustworthy to\n * restore, which sends the caller to a full scan.\n */\n async #rehydrateZeroCSS(cache, generation) {\n const zero = this.config.zero;\n if (!zero || zero.islandBuild) return true;\n const sidecar = await cache.readZeroCSS(generation);\n if (!sidecar) return false;\n zero.artifact.clearGraphs();\n zero.bridges.clear();\n zero.violations.length = 0;\n zero.configCSS = sidecar.configCSS;\n for (const [moduleId, css] of Object.entries(sidecar.zeroModuleCSS)) {\n zero.artifact.setZeroModuleCSS(moduleId, css);\n }\n for (const [bridgeId, css] of Object.entries(sidecar.bridgeCSS)) {\n zero.artifact.setBridgeRules(bridgeId, css);\n }\n for (const [islandId, bridges] of Object.entries(sidecar.bridges)) {\n zero.bridges.set(islandId, bridges);\n }\n zero.plansRestoredFromCache = true;\n return true;\n }\n #installWatchers() {\n for (const id of this.#records.keys()) this.#watchModule(id);\n }\n #watchModule(id) {\n if (this.#watchers.has(id)) return;\n try {\n const watcher = watch(id, { persistent: false }, () => {\n void this.updateFile(id);\n });\n watcher.unref();\n this.#watchers.set(id, watcher);\n } catch {\n }\n }\n #report(diagnostic) {\n this.config.reportDiagnostic?.(diagnostic);\n }\n}\nfunction describeMetroCompilerRoot(projectRoot, moduleId) {\n const path = relative(projectRoot, moduleId);\n return path.startsWith(\"..\") ? basename(moduleId) : path;\n}\nexport {\n METRO_RECORD_CACHE_VERSION,\n MetroCompilerFrontend,\n describeMetroCompilerRoot\n};\n//# sourceMappingURL=frontend.js.map\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/metro-plugin",
3
- "version": "3.0.0-beta.831.1",
3
+ "version": "3.0.0-beta.889.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "source": "src/index.ts",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@jridgewell/trace-mapping": "0.3.31",
44
- "@tamagui/compiler-core": "3.0.0-beta.831.1",
45
- "@tamagui/static": "3.0.0-beta.831.1",
44
+ "@tamagui/compiler-core": "3.0.0-beta.889.1",
45
+ "@tamagui/static": "3.0.0-beta.889.1",
46
46
  "ignore": "^5.3.2"
47
47
  },
48
48
  "devDependencies": {
49
- "@tamagui/build": "3.0.0-beta.831.1",
49
+ "@tamagui/build": "3.0.0-beta.889.1",
50
50
  "metro": "0.84.4",
51
51
  "metro-resolver": "0.84.4"
52
52
  },
@@ -12,7 +12,7 @@ import type { LoweredModulePlan } from '@tamagui/compiler-core'
12
12
 
13
13
  import { metroDiagnostic, type MetroCompilerDiagnostic } from './diagnostics'
14
14
 
15
- export const METRO_COMPILER_CACHE_VERSION = 6
15
+ export const METRO_COMPILER_CACHE_VERSION = 7
16
16
 
17
17
  export interface MetroCompilerCacheEntry {
18
18
  schemaVersion: typeof METRO_COMPILER_CACHE_VERSION
package/src/frontend.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  PLAN_CACHE_SCHEMA_VERSION,
12
12
  ProjectGraph,
13
13
  contentHash,
14
+ createExternalClosureLookup,
14
15
  defaultPlanCacheRoot,
15
16
  isTamaguiSpecifier,
16
17
  lowerModule,
@@ -29,7 +30,11 @@ import {
29
30
  type ModuleClosureNode,
30
31
  type ResolvedModuleId,
31
32
  } from '@tamagui/compiler-core'
32
- import Static, { createTamaguiCompilerHost } from '@tamagui/static'
33
+ import Static, {
34
+ ComponentDiscovery,
35
+ createComponentRegistry,
36
+ createTamaguiCompilerHost,
37
+ } from '@tamagui/static'
33
38
  import type {
34
39
  IslandThemeBridge,
35
40
  TamaguiOptions,
@@ -69,7 +74,7 @@ interface CompiledRecord {
69
74
  * prepass. The result is a pure function of the module's own bytes plus the
70
75
  * resolver and Babel identity, so it caches per file with no closure involved.
71
76
  */
72
- export const METRO_RECORD_CACHE_VERSION = 1
77
+ export const METRO_RECORD_CACHE_VERSION = 2
73
78
 
74
79
  interface CachedRecord {
75
80
  schemaVersion: typeof METRO_RECORD_CACHE_VERSION
@@ -257,6 +262,9 @@ export class MetroCompilerFrontend {
257
262
  readonly #resolver
258
263
  #graph: ProjectGraph | null = null
259
264
  #host: CompilerLoweringHost | null = null
265
+ #registry: Static.CompilerComponentRegistry | null = null
266
+ readonly #discovery = new ComponentDiscovery()
267
+ readonly #externalClosure = createExternalClosureLookup()
260
268
  #projectGeneration: string | null = null
261
269
  #publishedGeneration: string | null = null
262
270
  #scanOptions: MetroCompilerScanOptions | null = null
@@ -398,14 +406,22 @@ export class MetroCompilerFrontend {
398
406
  this.#graph = new ProjectGraph(yukuFactory, {
399
407
  modules: [...this.#records.values()].map(({ input }) => input),
400
408
  })
409
+ const componentModules = compilerProject.componentModules.map(
410
+ ({ moduleName, id }) => ({ moduleName, resolvedId: id })
411
+ )
412
+ // a new project generation may carry new static configs, so discovery
413
+ // starts over with it and re-registers what it finds
414
+ this.#discovery.clear()
415
+ this.#registry = createComponentRegistry(
416
+ compilerProject.projectInfo.components,
417
+ componentModules
418
+ )
401
419
  this.#host = createTamaguiCompilerHost({
402
420
  target: compilerTarget(options.platform),
403
421
  tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,
404
422
  components: compilerProject.projectInfo.components,
405
- componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({
406
- moduleName,
407
- resolvedId: id,
408
- })),
423
+ componentModules,
424
+ registry: this.#registry,
409
425
  disablePartialExtraction: compilerProject.disablePartialExtraction,
410
426
  experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,
411
427
  zeroRuntime: compilerProject.zeroRuntime,
@@ -427,7 +443,7 @@ export class MetroCompilerFrontend {
427
443
  // contract they are not part of.
428
444
  this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId))
429
445
  }
430
- for (const id of unplanned) this.#refreshEntry(id)
446
+ for (const id of unplanned) await this.#refreshEntry(id)
431
447
  await this.#storePlans(unplanned)
432
448
  }
433
449
  if (zero) {
@@ -562,7 +578,7 @@ export class MetroCompilerFrontend {
562
578
  this.#records.delete(id)
563
579
  this.#entries.delete(id)
564
580
  for (const affected of invalidation.invalidatedIds) {
565
- if (affected !== id) this.#refreshEntry(affected)
581
+ if (affected !== id) await this.#refreshEntry(affected)
566
582
  }
567
583
  const generation = await this.#publish(options.platform)
568
584
  result = {
@@ -593,7 +609,8 @@ export class MetroCompilerFrontend {
593
609
  }
594
610
  this.#records.set(record.input.id, record)
595
611
  const invalidation = graph.updateModule(record.input)
596
- for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected)
612
+ for (const affected of invalidation.invalidatedIds)
613
+ await this.#refreshEntry(affected)
597
614
  const generation = invalidation.changed
598
615
  ? await this.#publish(options.platform)
599
616
  : null
@@ -644,6 +661,7 @@ export class MetroCompilerFrontend {
644
661
  this.#planKeys.clear()
645
662
  this.#graph = null
646
663
  this.#host = null
664
+ this.#registry = null
647
665
  this.#projectGeneration = null
648
666
  }
649
667
 
@@ -834,7 +852,7 @@ export class MetroCompilerFrontend {
834
852
  this.#records.set(id, record)
835
853
  const invalidation = this.#graph?.updateModule(record.input)
836
854
  for (const affected of invalidation?.invalidatedIds ?? [id]) {
837
- this.#refreshEntry(affected)
855
+ await this.#refreshEntry(affected)
838
856
  }
839
857
  if (
840
858
  this.config.watch !== false &&
@@ -848,15 +866,33 @@ export class MetroCompilerFrontend {
848
866
  }
849
867
  }
850
868
 
851
- #refreshEntry(id: ResolvedModuleId): void {
869
+ async #refreshEntry(id: ResolvedModuleId): Promise<void> {
852
870
  const graph = this.#graph
853
871
  const host = this.#host
872
+ const registry = this.#registry
854
873
  const record = this.#records.get(id)
855
- if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration)
874
+ if (
875
+ !graph ||
876
+ !host ||
877
+ !registry ||
878
+ !record ||
879
+ !this.#scanOptions ||
880
+ !this.#projectGeneration
881
+ ) {
856
882
  return
883
+ }
857
884
  const target = compilerTarget(this.#scanOptions.platform)
885
+ const materialized = materializeModule(graph, id)
886
+ // Metro has no module runner; packages a file uses that are not in
887
+ // `components` evaluate under the static-evaluation require hooks, once
888
+ await this.#discovery.prepare(materialized, registry, ({ id: moduleId }) =>
889
+ Static.evaluateComponentModule(
890
+ { ...this.config.tamaguiOptions, platform: target },
891
+ moduleId
892
+ )
893
+ )
858
894
  const plan = lowerModule({
859
- module: materializeModule(graph, id),
895
+ module: materialized,
860
896
  source: record.input.source,
861
897
  target,
862
898
  host,
@@ -964,7 +1000,9 @@ export class MetroCompilerFrontend {
964
1000
  let node = nodes.get(id)
965
1001
  if (node === undefined) {
966
1002
  const record = this.#records.get(id)
967
- node = record ? moduleClosureNode(record.input) : null
1003
+ node = record
1004
+ ? moduleClosureNode(record.input, { includeExternal: true })
1005
+ : this.#externalClosure(id)
968
1006
  nodes.set(id, node)
969
1007
  }
970
1008
  return node
@@ -1,6 +1,6 @@
1
1
  import type { LoweredModulePlan } from "@tamagui/compiler-core";
2
2
  import { type MetroCompilerDiagnostic } from "./diagnostics";
3
- export declare const METRO_COMPILER_CACHE_VERSION = 6;
3
+ export declare const METRO_COMPILER_CACHE_VERSION = 7;
4
4
  export interface MetroCompilerCacheEntry {
5
5
  schemaVersion: typeof METRO_COMPILER_CACHE_VERSION;
6
6
  moduleId: string;
@@ -6,6 +6,6 @@
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nimport {\n LOWERED_MODULE_PLAN_VERSION,\n contentHash,\n stableStringify,\n tamaguiCacheRoot,\n} from '@tamagui/compiler-core'\nimport type { LoweredModulePlan } from '@tamagui/compiler-core'\n\nimport { metroDiagnostic, type MetroCompilerDiagnostic } from './diagnostics'\n\nexport const METRO_COMPILER_CACHE_VERSION = 6\n\nexport interface MetroCompilerCacheEntry {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n moduleId: string\n /** Hash of the raw on-disk module source the plan was generated from. */\n sourceHash: string\n plan: LoweredModulePlan\n diagnostics: MetroCompilerDiagnostic[]\n}\n\ninterface MetroCompilerCacheDescriptor {\n blobHash: string\n sourceHash: string\n}\n\ninterface MetroCompilerCacheManifest {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n generation: string\n optionsHash: string\n platform: string | null\n entries: Record<string, MetroCompilerCacheDescriptor>\n}\n\nexport interface MetroCompilerCacheValidation {\n valid: boolean\n diagnostics: MetroCompilerDiagnostic[]\n generation: string | null\n moduleIds: string[]\n /** Raw module source hash by module id, for host freshness checks. */\n sourceHashes: Record<string, string>\n optionsHash: string | null\n}\n\n/**\n * The zero build's CSS side effects, persisted beside the plan cache.\n *\n * A published plan generation and its artifact contents are the same fact\n * observed twice: the scan produces both. Persisting only the plans means a\n * warm build reuses them while emitting an artifact missing every rule it never\n * collected, and still derives TAMAGUI_DID_OUTPUT_CSS from it. This is what lets\n * the warm path skip the scan without that divergence.\n */\nexport interface MetroZeroCSSSidecar {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n generation: string\n configCSS: string\n /** Per-module compiler atomic CSS, by resolved module id. */\n zeroModuleCSS: Record<string, string>\n /** Theme-bridge class rules, by bridge id. */\n bridgeCSS: Record<string, string>\n /** The bridge manifest, by island id. */\n bridges: Record<string, unknown[]>\n}\n\nexport class MetroCompilerCacheError extends Error {\n constructor(readonly diagnostic: MetroCompilerDiagnostic) {\n super(diagnostic.message)\n this.name = 'MetroCompilerCacheError'\n }\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction stableEntries<T>(record: Record<string, T>): [string, T][] {\n return Object.entries(record).sort(([left], [right]) => compareCodeUnits(left, right))\n}\n\nfunction cacheCorrupt(message: string, moduleId?: string): MetroCompilerCacheError {\n return new MetroCompilerCacheError(\n metroDiagnostic('metro/cache-corrupt', message, { moduleId })\n )\n}\n\nfunction parseJson<T>(source: string, description: string, moduleId?: string): T {\n try {\n return JSON.parse(source) as T\n } catch (error) {\n throw cacheCorrupt(\n `${description} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n moduleId\n )\n }\n}\n\nexport function defaultMetroCompilerCacheRoot(projectRoot: string): string {\n return join(tamaguiCacheRoot(projectRoot), 'metro-compiler')\n}\n\n/**\n * Filesystem handoff shared by the Metro main process and isolated transform workers.\n * Immutable blobs are content addressed; a single manifest rename publishes a generation.\n */\nexport class MetroCompilerCache {\n readonly #blobsDirectory: string\n readonly #manifestPath: string\n\n constructor(readonly root: string) {\n this.#blobsDirectory = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, 'blobs')\n this.#manifestPath = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, 'manifest.json')\n }\n\n async publish(\n platform: string | null,\n entries: readonly MetroCompilerCacheEntry[],\n optionsHash: string\n ): Promise<string> {\n await mkdir(this.#blobsDirectory, { recursive: true })\n const descriptors: Record<string, MetroCompilerCacheDescriptor> = {}\n\n for (const entry of [...entries].sort((left, right) =>\n compareCodeUnits(left.moduleId, right.moduleId)\n )) {\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION) {\n throw new Error(`Cannot publish cache schema ${entry.schemaVersion}`)\n }\n const serialized = `${stableStringify(entry)}\\n`\n const blobHash = contentHash(serialized)\n const blobPath = join(this.#blobsDirectory, `${blobHash}.json`)\n try {\n await writeFile(blobPath, serialized, { encoding: 'utf8', flag: 'wx' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\n const existing = await readFile(blobPath, 'utf8')\n if (contentHash(existing) !== blobHash) {\n const temporaryBlobPath = `${blobPath}.${process.pid}-${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temporaryBlobPath, serialized, 'utf8')\n await rename(temporaryBlobPath, blobPath)\n }\n }\n descriptors[entry.moduleId] = {\n blobHash,\n sourceHash: entry.sourceHash,\n }\n }\n\n const generation = contentHash(stableStringify(descriptors))\n const manifest: MetroCompilerCacheManifest = {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n optionsHash,\n platform,\n entries: descriptors,\n }\n const manifestDirectory = join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`)\n const temporaryPath = join(\n manifestDirectory,\n `.manifest-${process.pid}-${randomBytes(6).toString('hex')}.json`\n )\n await writeFile(temporaryPath, `${stableStringify(manifest)}\\n`, 'utf8')\n await rename(temporaryPath, this.#manifestPath)\n return generation\n }\n\n async read(\n moduleId: string,\n rawSource: string,\n onMiss?: (reason: 'no-entry' | 'source-hash-mismatch', detail?: string) => void\n ): Promise<MetroCompilerCacheEntry | null> {\n const manifest = await this.#readManifest()\n if (!manifest) return null\n const descriptor = manifest.entries[moduleId]\n if (!descriptor) {\n onMiss?.('no-entry')\n return null\n }\n const sourceHash = contentHash(rawSource)\n if (sourceHash !== descriptor.sourceHash) {\n onMiss?.(\n 'source-hash-mismatch',\n `worker ${sourceHash.slice(0, 12)} vs plan ${descriptor.sourceHash.slice(0, 12)}`\n )\n return null\n }\n return await this.#readBlob(moduleId, descriptor)\n }\n\n async validate(): Promise<MetroCompilerCacheValidation> {\n const diagnostics: MetroCompilerDiagnostic[] = []\n try {\n const manifest = await this.#readManifest()\n if (!manifest) {\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null,\n }\n }\n const sourceHashes: Record<string, string> = {}\n for (const [moduleId, descriptor] of stableEntries(manifest.entries)) {\n await this.#readBlob(moduleId, descriptor)\n sourceHashes[moduleId] = descriptor.sourceHash\n }\n return {\n valid: true,\n diagnostics,\n generation: manifest.generation,\n moduleIds: Object.keys(manifest.entries).sort(compareCodeUnits),\n sourceHashes,\n optionsHash: manifest.optionsHash,\n }\n } catch (error) {\n if (error instanceof MetroCompilerCacheError) {\n diagnostics.push(error.diagnostic)\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null,\n }\n }\n throw error\n }\n }\n\n async discardManifest(): Promise<void> {\n await rm(this.#manifestPath, { force: true })\n }\n\n #zeroSidecarPath(generation: string): string {\n return join(\n this.root,\n `v${METRO_COMPILER_CACHE_VERSION}`,\n `zero-${generation.slice(0, 32)}.json`\n )\n }\n\n async publishZeroCSS(sidecar: MetroZeroCSSSidecar): Promise<void> {\n await mkdir(join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`), { recursive: true })\n const file = this.#zeroSidecarPath(sidecar.generation)\n const temporaryPath = `${file}.${process.pid}-${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temporaryPath, `${stableStringify(sidecar)}\\n`, 'utf8')\n await rename(temporaryPath, file)\n }\n\n /** Null whenever the sidecar is absent or does not describe this generation. */\n async readZeroCSS(generation: string): Promise<MetroZeroCSSSidecar | null> {\n let source: string\n try {\n source = await readFile(this.#zeroSidecarPath(generation), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n const sidecar = parseJson<MetroZeroCSSSidecar>(source, 'Zero CSS sidecar')\n if (\n sidecar.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n sidecar.generation !== generation ||\n typeof sidecar.configCSS !== 'string' ||\n !sidecar.zeroModuleCSS ||\n typeof sidecar.zeroModuleCSS !== 'object' ||\n !sidecar.bridgeCSS ||\n typeof sidecar.bridgeCSS !== 'object' ||\n !sidecar.bridges ||\n typeof sidecar.bridges !== 'object'\n ) {\n return null\n }\n return sidecar\n }\n\n async #readManifest(): Promise<MetroCompilerCacheManifest | null> {\n let source: string\n try {\n source = await readFile(this.#manifestPath, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n const manifest = parseJson<MetroCompilerCacheManifest>(source, 'Cache manifest')\n if (\n manifest.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n typeof manifest.generation !== 'string' ||\n typeof manifest.optionsHash !== 'string' ||\n !manifest.entries ||\n typeof manifest.entries !== 'object'\n ) {\n throw cacheCorrupt('Cache manifest has an unsupported schema')\n }\n return manifest\n }\n\n async #readBlob(\n moduleId: string,\n descriptor: MetroCompilerCacheDescriptor\n ): Promise<MetroCompilerCacheEntry> {\n const blobPath = join(this.#blobsDirectory, `${descriptor.blobHash}.json`)\n let source: string\n try {\n source = await readFile(blobPath, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} is missing`, moduleId)\n }\n throw error\n }\n if (contentHash(source) !== descriptor.blobHash) {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} failed its hash`, moduleId)\n }\n const entry = parseJson<MetroCompilerCacheEntry>(\n source,\n `Cache blob ${descriptor.blobHash}`,\n moduleId\n )\n if (\n entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n entry.moduleId !== moduleId ||\n typeof entry.sourceHash !== 'string' ||\n entry.sourceHash !== descriptor.sourceHash ||\n !entry.plan ||\n entry.plan.version !== LOWERED_MODULE_PLAN_VERSION ||\n entry.plan.id !== moduleId ||\n entry.plan.sourceHash !== entry.sourceHash ||\n !Array.isArray(entry.plan.edits) ||\n !Array.isArray(entry.plan.diagnostics) ||\n !Array.isArray(entry.diagnostics)\n ) {\n throw cacheCorrupt(\n `Cache blob ${descriptor.blobHash} has invalid contents`,\n moduleId\n )\n }\n return entry\n }\n}\n"
9
+ "import { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nimport {\n LOWERED_MODULE_PLAN_VERSION,\n contentHash,\n stableStringify,\n tamaguiCacheRoot,\n} from '@tamagui/compiler-core'\nimport type { LoweredModulePlan } from '@tamagui/compiler-core'\n\nimport { metroDiagnostic, type MetroCompilerDiagnostic } from './diagnostics'\n\nexport const METRO_COMPILER_CACHE_VERSION = 7\n\nexport interface MetroCompilerCacheEntry {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n moduleId: string\n /** Hash of the raw on-disk module source the plan was generated from. */\n sourceHash: string\n plan: LoweredModulePlan\n diagnostics: MetroCompilerDiagnostic[]\n}\n\ninterface MetroCompilerCacheDescriptor {\n blobHash: string\n sourceHash: string\n}\n\ninterface MetroCompilerCacheManifest {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n generation: string\n optionsHash: string\n platform: string | null\n entries: Record<string, MetroCompilerCacheDescriptor>\n}\n\nexport interface MetroCompilerCacheValidation {\n valid: boolean\n diagnostics: MetroCompilerDiagnostic[]\n generation: string | null\n moduleIds: string[]\n /** Raw module source hash by module id, for host freshness checks. */\n sourceHashes: Record<string, string>\n optionsHash: string | null\n}\n\n/**\n * The zero build's CSS side effects, persisted beside the plan cache.\n *\n * A published plan generation and its artifact contents are the same fact\n * observed twice: the scan produces both. Persisting only the plans means a\n * warm build reuses them while emitting an artifact missing every rule it never\n * collected, and still derives TAMAGUI_DID_OUTPUT_CSS from it. This is what lets\n * the warm path skip the scan without that divergence.\n */\nexport interface MetroZeroCSSSidecar {\n schemaVersion: typeof METRO_COMPILER_CACHE_VERSION\n generation: string\n configCSS: string\n /** Per-module compiler atomic CSS, by resolved module id. */\n zeroModuleCSS: Record<string, string>\n /** Theme-bridge class rules, by bridge id. */\n bridgeCSS: Record<string, string>\n /** The bridge manifest, by island id. */\n bridges: Record<string, unknown[]>\n}\n\nexport class MetroCompilerCacheError extends Error {\n constructor(readonly diagnostic: MetroCompilerDiagnostic) {\n super(diagnostic.message)\n this.name = 'MetroCompilerCacheError'\n }\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction stableEntries<T>(record: Record<string, T>): [string, T][] {\n return Object.entries(record).sort(([left], [right]) => compareCodeUnits(left, right))\n}\n\nfunction cacheCorrupt(message: string, moduleId?: string): MetroCompilerCacheError {\n return new MetroCompilerCacheError(\n metroDiagnostic('metro/cache-corrupt', message, { moduleId })\n )\n}\n\nfunction parseJson<T>(source: string, description: string, moduleId?: string): T {\n try {\n return JSON.parse(source) as T\n } catch (error) {\n throw cacheCorrupt(\n `${description} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n moduleId\n )\n }\n}\n\nexport function defaultMetroCompilerCacheRoot(projectRoot: string): string {\n return join(tamaguiCacheRoot(projectRoot), 'metro-compiler')\n}\n\n/**\n * Filesystem handoff shared by the Metro main process and isolated transform workers.\n * Immutable blobs are content addressed; a single manifest rename publishes a generation.\n */\nexport class MetroCompilerCache {\n readonly #blobsDirectory: string\n readonly #manifestPath: string\n\n constructor(readonly root: string) {\n this.#blobsDirectory = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, 'blobs')\n this.#manifestPath = join(root, `v${METRO_COMPILER_CACHE_VERSION}`, 'manifest.json')\n }\n\n async publish(\n platform: string | null,\n entries: readonly MetroCompilerCacheEntry[],\n optionsHash: string\n ): Promise<string> {\n await mkdir(this.#blobsDirectory, { recursive: true })\n const descriptors: Record<string, MetroCompilerCacheDescriptor> = {}\n\n for (const entry of [...entries].sort((left, right) =>\n compareCodeUnits(left.moduleId, right.moduleId)\n )) {\n if (entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION) {\n throw new Error(`Cannot publish cache schema ${entry.schemaVersion}`)\n }\n const serialized = `${stableStringify(entry)}\\n`\n const blobHash = contentHash(serialized)\n const blobPath = join(this.#blobsDirectory, `${blobHash}.json`)\n try {\n await writeFile(blobPath, serialized, { encoding: 'utf8', flag: 'wx' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\n const existing = await readFile(blobPath, 'utf8')\n if (contentHash(existing) !== blobHash) {\n const temporaryBlobPath = `${blobPath}.${process.pid}-${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temporaryBlobPath, serialized, 'utf8')\n await rename(temporaryBlobPath, blobPath)\n }\n }\n descriptors[entry.moduleId] = {\n blobHash,\n sourceHash: entry.sourceHash,\n }\n }\n\n const generation = contentHash(stableStringify(descriptors))\n const manifest: MetroCompilerCacheManifest = {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n optionsHash,\n platform,\n entries: descriptors,\n }\n const manifestDirectory = join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`)\n const temporaryPath = join(\n manifestDirectory,\n `.manifest-${process.pid}-${randomBytes(6).toString('hex')}.json`\n )\n await writeFile(temporaryPath, `${stableStringify(manifest)}\\n`, 'utf8')\n await rename(temporaryPath, this.#manifestPath)\n return generation\n }\n\n async read(\n moduleId: string,\n rawSource: string,\n onMiss?: (reason: 'no-entry' | 'source-hash-mismatch', detail?: string) => void\n ): Promise<MetroCompilerCacheEntry | null> {\n const manifest = await this.#readManifest()\n if (!manifest) return null\n const descriptor = manifest.entries[moduleId]\n if (!descriptor) {\n onMiss?.('no-entry')\n return null\n }\n const sourceHash = contentHash(rawSource)\n if (sourceHash !== descriptor.sourceHash) {\n onMiss?.(\n 'source-hash-mismatch',\n `worker ${sourceHash.slice(0, 12)} vs plan ${descriptor.sourceHash.slice(0, 12)}`\n )\n return null\n }\n return await this.#readBlob(moduleId, descriptor)\n }\n\n async validate(): Promise<MetroCompilerCacheValidation> {\n const diagnostics: MetroCompilerDiagnostic[] = []\n try {\n const manifest = await this.#readManifest()\n if (!manifest) {\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null,\n }\n }\n const sourceHashes: Record<string, string> = {}\n for (const [moduleId, descriptor] of stableEntries(manifest.entries)) {\n await this.#readBlob(moduleId, descriptor)\n sourceHashes[moduleId] = descriptor.sourceHash\n }\n return {\n valid: true,\n diagnostics,\n generation: manifest.generation,\n moduleIds: Object.keys(manifest.entries).sort(compareCodeUnits),\n sourceHashes,\n optionsHash: manifest.optionsHash,\n }\n } catch (error) {\n if (error instanceof MetroCompilerCacheError) {\n diagnostics.push(error.diagnostic)\n return {\n valid: false,\n diagnostics,\n generation: null,\n moduleIds: [],\n sourceHashes: {},\n optionsHash: null,\n }\n }\n throw error\n }\n }\n\n async discardManifest(): Promise<void> {\n await rm(this.#manifestPath, { force: true })\n }\n\n #zeroSidecarPath(generation: string): string {\n return join(\n this.root,\n `v${METRO_COMPILER_CACHE_VERSION}`,\n `zero-${generation.slice(0, 32)}.json`\n )\n }\n\n async publishZeroCSS(sidecar: MetroZeroCSSSidecar): Promise<void> {\n await mkdir(join(this.root, `v${METRO_COMPILER_CACHE_VERSION}`), { recursive: true })\n const file = this.#zeroSidecarPath(sidecar.generation)\n const temporaryPath = `${file}.${process.pid}-${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temporaryPath, `${stableStringify(sidecar)}\\n`, 'utf8')\n await rename(temporaryPath, file)\n }\n\n /** Null whenever the sidecar is absent or does not describe this generation. */\n async readZeroCSS(generation: string): Promise<MetroZeroCSSSidecar | null> {\n let source: string\n try {\n source = await readFile(this.#zeroSidecarPath(generation), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n const sidecar = parseJson<MetroZeroCSSSidecar>(source, 'Zero CSS sidecar')\n if (\n sidecar.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n sidecar.generation !== generation ||\n typeof sidecar.configCSS !== 'string' ||\n !sidecar.zeroModuleCSS ||\n typeof sidecar.zeroModuleCSS !== 'object' ||\n !sidecar.bridgeCSS ||\n typeof sidecar.bridgeCSS !== 'object' ||\n !sidecar.bridges ||\n typeof sidecar.bridges !== 'object'\n ) {\n return null\n }\n return sidecar\n }\n\n async #readManifest(): Promise<MetroCompilerCacheManifest | null> {\n let source: string\n try {\n source = await readFile(this.#manifestPath, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n const manifest = parseJson<MetroCompilerCacheManifest>(source, 'Cache manifest')\n if (\n manifest.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n typeof manifest.generation !== 'string' ||\n typeof manifest.optionsHash !== 'string' ||\n !manifest.entries ||\n typeof manifest.entries !== 'object'\n ) {\n throw cacheCorrupt('Cache manifest has an unsupported schema')\n }\n return manifest\n }\n\n async #readBlob(\n moduleId: string,\n descriptor: MetroCompilerCacheDescriptor\n ): Promise<MetroCompilerCacheEntry> {\n const blobPath = join(this.#blobsDirectory, `${descriptor.blobHash}.json`)\n let source: string\n try {\n source = await readFile(blobPath, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} is missing`, moduleId)\n }\n throw error\n }\n if (contentHash(source) !== descriptor.blobHash) {\n throw cacheCorrupt(`Cache blob ${descriptor.blobHash} failed its hash`, moduleId)\n }\n const entry = parseJson<MetroCompilerCacheEntry>(\n source,\n `Cache blob ${descriptor.blobHash}`,\n moduleId\n )\n if (\n entry.schemaVersion !== METRO_COMPILER_CACHE_VERSION ||\n entry.moduleId !== moduleId ||\n typeof entry.sourceHash !== 'string' ||\n entry.sourceHash !== descriptor.sourceHash ||\n !entry.plan ||\n entry.plan.version !== LOWERED_MODULE_PLAN_VERSION ||\n entry.plan.id !== moduleId ||\n entry.plan.sourceHash !== entry.sourceHash ||\n !Array.isArray(entry.plan.edits) ||\n !Array.isArray(entry.plan.diagnostics) ||\n !Array.isArray(entry.diagnostics)\n ) {\n throw cacheCorrupt(\n `Cache blob ${descriptor.blobHash} has invalid contents`,\n moduleId\n )\n }\n return entry\n }\n}\n"
10
10
  ]
11
11
  }
@@ -10,7 +10,7 @@ import { type MetroResolverConfig } from "./metroResolver";
10
10
  * prepass. The result is a pure function of the module's own bytes plus the
11
11
  * resolver and Babel identity, so it caches per file with no closure involved.
12
12
  */
13
- export declare const METRO_RECORD_CACHE_VERSION = 1;
13
+ export declare const METRO_RECORD_CACHE_VERSION = 2;
14
14
  export interface MetroCompilerFrontendConfig extends MetroResolverConfig {
15
15
  cacheRoot?: string;
16
16
  /** Present only for an enforced zero-runtime web build. */
@@ -1,11 +1,11 @@
1
1
  {
2
- "mappings": "AAOA,cAiBO,sBAMA;AACP,OAAO,YAA2C;AAClD,cAEE,sBAEK;AAOP,cAA6B,2BAA2B;AAOxD,cAA+B,+BAA+B;AAC9D,cAIO,2BACA;;;;;;;AAeP,OAAO,cAAM,6BAA6B;AAW1C,iBAAiB,oCAAoC,oBAAoB;CACvE;;CAEA,OAAO;CACP;CACA,cAAc;CACd,iBAAiB,QAAQ;CACzB,uBACE,QAAQ,gBACR,4BACG,QAAQ;CACb;CACA,oBAAoB,YAAY;;AAGlC,iBAAiB,6BAA6B,OAAO,gBAAgB;AAErE,iBAAiB,yBAAyB;CACxC;CACA;CACA;CACA;CACA,YAAY;;AAGd,iBAAiB,wBAAwB;CACvC;CACA;CACA,aAAa;;AAGf,iBAAiB,oBAAoB;CACnC;CACA;CACA;;AAuIF,OAAO,cAAM,sBAAsB;;CAqBrB,iBAAiB;CAA7B,YAAY,AAAS,QAAQ;CAM7B,IAAI;;;;;;CASJ,IAAI,qBAAqB;EACvB,OAAO;GAAE;GAAc;GAAgB;;EACvC,SAAS;GAAE;GAAc;GAAgB;;;CAS3C,aAAa;CAIb,KAAK,SAAS,2BAA2B,QAAQ;CAyLjD,iBAAiB,SAAS,2BAA2B,QAAQ;CAsD7D,AAAM,WAAW,eAAe,QAAQ;CA2FxC,SAAS;;AA8hBX,OAAO,iBAAS,0BAA0B,qBAAqB",
2
+ "mappings": "AAOA,cAkBO,sBAMA;AACP,OAAO,YAIA;AACP,cAEE,sBAEK;AAOP,cAA6B,2BAA2B;AAOxD,cAA+B,+BAA+B;AAC9D,cAIO,2BACA;;;;;;;AAeP,OAAO,cAAM,6BAA6B;AAW1C,iBAAiB,oCAAoC,oBAAoB;CACvE;;CAEA,OAAO;CACP;CACA,cAAc;CACd,iBAAiB,QAAQ;CACzB,uBACE,QAAQ,gBACR,4BACG,QAAQ;CACb;CACA,oBAAoB,YAAY;;AAGlC,iBAAiB,6BAA6B,OAAO,gBAAgB;AAErE,iBAAiB,yBAAyB;CACxC;CACA;CACA;CACA;CACA,YAAY;;AAGd,iBAAiB,wBAAwB;CACvC;CACA;CACA,aAAa;;AAGf,iBAAiB,oBAAoB;CACnC;CACA;CACA;;AAuIF,OAAO,cAAM,sBAAsB;;CAwBrB,iBAAiB;CAA7B,YAAY,AAAS,QAAQ;CAM7B,IAAI;;;;;;CASJ,IAAI,qBAAqB;EACvB,OAAO;GAAE;GAAc;GAAgB;;EACvC,SAAS;GAAE;GAAc;GAAgB;;;CAS3C,aAAa;CAIb,KAAK,SAAS,2BAA2B,QAAQ;CAiMjD,iBAAiB,SAAS,2BAA2B,QAAQ;CAsD7D,AAAM,WAAW,eAAe,QAAQ;CA4FxC,SAAS;;AAmjBX,OAAO,iBAAS,0BAA0B,qBAAqB",
3
3
  "names": [],
4
4
  "sources": [
5
5
  "src/frontend.ts"
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import { existsSync, watch, type FSWatcher } from 'node:fs'\nimport { readFile, readdir, realpath } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path'\n\nimport ignore, { type Ignore } from 'ignore'\n\nimport {\n JsonFileCache,\n ModulePlanCache,\n PLAN_CACHE_SCHEMA_VERSION,\n ProjectGraph,\n contentHash,\n defaultPlanCacheRoot,\n isTamaguiSpecifier,\n lowerModule,\n materializeModule,\n moduleClosureDigest,\n moduleClosureNode,\n planCacheKey,\n resolvedModuleId,\n stableStringify,\n yukuFactory,\n type CompilerLoweringHost,\n type CompilerTarget,\n type HostModuleInput,\n type HostResolvedImport,\n type LoweredModulePlan,\n type ModuleClosureNode,\n type ResolvedModuleId,\n} from '@tamagui/compiler-core'\nimport Static, { createTamaguiCompilerHost } from '@tamagui/static'\nimport type {\n IslandThemeBridge,\n TamaguiOptions,\n TamaguiProjectInfo,\n} from '@tamagui/static'\n\nimport {\n compileWithUserBabel,\n userBabelCacheKey,\n type MetroBabelTransformArgs,\n} from './babel'\nimport { zeroModuleKey, type MetroZeroController } from './zeroRuntime'\nimport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n defaultMetroCompilerCacheRoot,\n type MetroCompilerCacheEntry,\n} from './compilerCache'\nimport { metroDiagnostic, type MetroCompilerDiagnostic } from './diagnostics'\nimport {\n createMetroCompilerResolver,\n isCompilerSourceFile,\n moduleSpecifiersFromAst,\n type MetroResolverConfig,\n} from './metroResolver'\n\ninterface CompiledRecord {\n input: HostModuleInput\n sourceHash: string\n /** Specifiers that reached the compiled output as require() calls instead of imports. */\n requireSpecifiers: string[]\n}\n\n/**\n * Metro runs the user's whole Babel transformer over every project source just\n * to read its import specifiers, which is the single most expensive step of the\n * prepass. The result is a pure function of the module's own bytes plus the\n * resolver and Babel identity, so it caches per file with no closure involved.\n */\nexport const METRO_RECORD_CACHE_VERSION = 1\n\ninterface CachedRecord {\n schemaVersion: typeof METRO_RECORD_CACHE_VERSION\n sourceHash: string\n imports: HostResolvedImport[]\n requireSpecifiers: string[]\n /** Resolve failures replayed on a hit, so a cached record reports what a fresh one did. */\n diagnostics: MetroCompilerDiagnostic[]\n}\n\nexport interface MetroCompilerFrontendConfig extends MetroResolverConfig {\n cacheRoot?: string\n /** Present only for an enforced zero-runtime web build. */\n zero?: MetroZeroController | null\n originalBabelTransformerPath: string\n transformer?: Record<string, any>\n tamaguiOptions?: Partial<TamaguiOptions>\n loadCompilerProject?: (\n target: CompilerTarget,\n platform: string | null\n ) => Promise<MetroCompilerProject>\n watch?: boolean\n reportDiagnostic?: (diagnostic: MetroCompilerDiagnostic) => void\n}\n\nexport interface MetroCompilerProject extends Static.CompilerProject {}\n\nexport interface MetroCompilerScanOptions {\n dev: boolean\n entryFiles: readonly string[]\n hot: boolean\n platform: string | null\n transform?: Record<string, any>\n}\n\nexport interface MetroCompilerGeneration {\n generation: string\n moduleIds: string[]\n diagnostics: MetroCompilerDiagnostic[]\n}\n\nexport interface MetroCompilerUpdate {\n changed: boolean\n affectedIds: string[]\n generation: string | null\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nconst requireFromFrontend = createRequire(\n typeof __filename === 'string' ? __filename : import.meta.url\n)\n\n// upgrading the compiler must invalidate published plans even when the Tamagui\n// config output is unchanged\nconst compilerImplementationVersions = (\n ['@tamagui/metro-plugin', '@tamagui/static', '@tamagui/compiler-core'] as const\n).map((packageName) => {\n const { version } = requireFromFrontend(`${packageName}/package.json`) as {\n version: string\n }\n return `${packageName}@${version}`\n})\n\nfunction scanOptionsHash(\n options: MetroCompilerScanOptions,\n projectGeneration: string,\n projectSourcesHash: string\n): string {\n return contentHash(JSON.stringify({ options, projectGeneration, projectSourcesHash }))\n}\n\n// Metro entries can live inside node_modules (expo-router's entry reaches app\n// source only through require.context), so reachability from the entry alone\n// discovers nothing there. Project source is walked directly and seeded into\n// the scan alongside the entry; imports then extend the graph outside the\n// project root (workspace packages) exactly as before.\n//\n// The walked list is both the seed set and the plan cache's options hash, so it\n// has to be authored source only. Build output is whatever the project already\n// declares as ignored, read with git's own rules: a directory-name list cannot\n// know that `dist-metro`, `out` or `public/assets` are output, and a sibling\n// bundler's content-hashed filenames then re-key the plan cache on every\n// unrelated rebuild, forcing Metro to rescan a project that never changed.\n// `node_modules` is skipped structurally instead, because that is the same\n// externality boundary the resolver draws and it must hold with or without a\n// declaration.\ninterface IgnoreScope {\n dir: string\n matcher: Ignore\n}\n\nconst speculativeWalkExcludedDirs = new Set([\n '__tests__',\n 'e2e',\n 'flows',\n 'plugins',\n 'screenshots',\n 'scripts',\n 'test',\n 'test-results',\n 'tests',\n])\n\nasync function walkProjectSources(root: string): Promise<string[]> {\n // git reads every .gitignore from the repository root down to the file, so an\n // app nested in a monorepo inherits the declarations made above it\n const inherited: string[] = []\n let ancestor = root\n while (!existsSync(join(ancestor, '.git'))) {\n const parent = dirname(ancestor)\n if (parent === ancestor) break\n inherited.unshift(parent)\n ancestor = parent\n }\n const rootScopes: IgnoreScope[] = []\n for (const dir of inherited) {\n const source = await readFile(join(dir, '.gitignore'), 'utf8').catch(() => null)\n if (source) rootScopes.push({ dir, matcher: ignore().add(source) })\n }\n\n const found: string[] = []\n const stack: { dir: string; scopes: IgnoreScope[] }[] = [\n { dir: root, scopes: rootScopes },\n ]\n while (stack.length) {\n const { dir, scopes } = stack.pop()!\n let entries\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n continue\n }\n let active = scopes\n if (entries.some((entry) => entry.isFile() && entry.name === '.gitignore')) {\n const source = await readFile(join(dir, '.gitignore'), 'utf8').catch(() => null)\n if (source) active = [...scopes, { dir, matcher: ignore().add(source) }]\n }\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue\n const isDirectory = entry.isDirectory()\n if (isDirectory && speculativeWalkExcludedDirs.has(entry.name)) continue\n if (!isDirectory && !(entry.isFile() && isCompilerSourceFile(entry.name))) continue\n if (\n !isDirectory &&\n (/(?:^|[-.])(?:probe|run|spec|tests?)(?:[-.]|$)/i.test(entry.name) ||\n /\\.(?:build|config|workspace)\\.[cm]?[jt]sx?$/.test(entry.name))\n ) {\n continue\n }\n const path = join(dir, entry.name)\n let ignored = false\n for (const scope of active) {\n const relativePath = relative(scope.dir, path)\n if (!relativePath || relativePath.startsWith('..')) continue\n const candidate = relativePath.split(sep).join('/') + (isDirectory ? '/' : '')\n if (scope.matcher.ignores(candidate)) {\n ignored = true\n break\n }\n }\n if (ignored) continue\n if (isDirectory) stack.push({ dir: path, scopes: active })\n else found.push(path)\n }\n }\n return found.sort(compareCodeUnits)\n}\n\nfunction compilerTarget(platform: string | null): CompilerTarget {\n return platform === 'web' ? 'web' : 'native'\n}\n\nfunction retainsLiveGraph(options: MetroCompilerScanOptions): boolean {\n return options.dev && options.hot\n}\n\nexport class MetroCompilerFrontend {\n readonly #cacheBaseRoot: string\n readonly #entries = new Map<ResolvedModuleId, MetroCompilerCacheEntry>()\n readonly #records = new Map<ResolvedModuleId, CompiledRecord>()\n readonly #watchers = new Map<ResolvedModuleId, FSWatcher>()\n readonly #resolver\n #graph: ProjectGraph | null = null\n #host: CompilerLoweringHost | null = null\n #projectGeneration: string | null = null\n #publishedGeneration: string | null = null\n #scanOptions: MetroCompilerScanOptions | null = null\n #scanOptionsHash: string | null = null\n #operationQueue: Promise<void> = Promise.resolve()\n #tamaguiConfig: TamaguiProjectInfo['tamaguiConfig'] | null = null\n #zeroEntryGraph: Set<ResolvedModuleId> | null = null\n readonly #planKeys = new Map<ResolvedModuleId, { key: string; digest: string }>()\n #recordCache: JsonFileCache | null = null\n #recordCacheIdentity: string | null = null\n #planCache: ModulePlanCache | null = null\n #planCacheStamp: string | null = null\n\n constructor(readonly config: MetroCompilerFrontendConfig) {\n this.#cacheBaseRoot =\n config.cacheRoot ?? defaultMetroCompilerCacheRoot(config.projectRoot)\n this.#resolver = createMetroCompilerResolver(config)\n }\n\n get metroResolverVersion(): string {\n return this.#resolver.version\n }\n\n /**\n * Per-file cache accounting for the last scan. The point of these caches is\n * that one edited module leaves every other module's entry valid, and this is\n * how that is observed rather than assumed.\n */\n get compileCacheStats(): {\n plans: { hits: number; misses: number; writes: number }\n records: { hits: number; misses: number; writes: number }\n } {\n const empty = { hits: 0, misses: 0, writes: 0 }\n return {\n plans: this.#planCache?.stats ?? empty,\n records: this.#recordCache?.stats ?? empty,\n }\n }\n\n cacheRootFor(platform: string | null): string {\n return join(this.#cacheBaseRoot, platform ?? 'default')\n }\n\n scan(options: MetroCompilerScanOptions): Promise<MetroCompilerGeneration> {\n return this.#enqueue(() => this.#scan(options))\n }\n\n async #scan(\n options: MetroCompilerScanOptions,\n preparedProject?: MetroCompilerProject,\n preparedProjectSources?: string[]\n ): Promise<MetroCompilerGeneration> {\n this.#scanOptions = options\n this.#publishedGeneration = null\n const diagnostics: MetroCompilerDiagnostic[] = []\n const entryRoots = (\n await Promise.all(\n options.entryFiles.map((path) => realpath(resolve(this.config.projectRoot, path)))\n )\n ).sort(compareCodeUnits)\n const compilerProject =\n preparedProject ??\n (await this.#loadCompilerProject(options, entryRoots[0], diagnostics))\n this.#projectGeneration = compilerProject.generation\n const projectSources =\n preparedProjectSources ?? (await walkProjectSources(this.config.projectRoot))\n const projectSourcesHash = contentHash(JSON.stringify(projectSources))\n this.#scanOptionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n projectSourcesHash\n )\n this.#installCaches(options, compilerProject, projectSourcesHash)\n const speculativeRoots = new Set<string>()\n for (const file of projectSources) {\n try {\n const id = await realpath(file)\n if (!entryRoots.includes(id)) speculativeRoots.add(id)\n } catch {}\n }\n const roots = [...new Set([...entryRoots, ...speculativeRoots])].sort(\n compareCodeUnits\n )\n const queue = [...roots]\n const queued = new Set(queue)\n for (const watcher of this.#watchers.values()) watcher.close()\n this.#watchers.clear()\n this.#records.clear()\n\n while (queue.length) {\n const path = queue.shift()!\n try {\n const record = await this.#compileRecord(path, options, diagnostics)\n this.#records.set(record.input.id, record)\n for (const dependency of record.input.imports) {\n if (\n dependency.external ||\n !isCompilerSourceFile(dependency.resolvedId) ||\n queued.has(dependency.resolvedId)\n ) {\n continue\n }\n queued.add(dependency.resolvedId)\n queue.push(dependency.resolvedId)\n }\n } catch (error) {\n // walk-seeded files are speculative: nothing proved the bundle needs\n // them, so a compile failure is not a build diagnostic. If the bundle\n // does include one, the transformer's plan-miss warning still fires.\n if (speculativeRoots.has(path)) continue\n const diagnostic = metroDiagnostic(\n 'metro/transform-failed',\n `Failed to compile ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n\n if (\n !compilerProject.projectInfo.tamaguiConfig ||\n !compilerProject.projectInfo.components\n ) {\n throw new Error('Metro compiler project has no Tamagui config or components')\n }\n this.#tamaguiConfig = compilerProject.projectInfo.tamaguiConfig\n this.#entries.clear()\n const unplanned = await this.#restorePlans(options)\n const zero = this.config.zero\n // a scan that restores everything builds no graph, so the previous scan's\n // graph must not survive as this scan's answer\n this.#graph = null\n this.#host = null\n // Nothing left to compile and no live session to serve means the analyzer\n // graph is never read, so it is never built. Parsing and linking every\n // project source is the other half of the prepass cost.\n if (unplanned.length || retainsLiveGraph(options)) {\n this.#graph = new ProjectGraph(yukuFactory, {\n modules: [...this.#records.values()].map(({ input }) => input),\n })\n this.#host = createTamaguiCompilerHost({\n target: compilerTarget(options.platform),\n tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,\n components: compilerProject.projectInfo.components,\n componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({\n moduleName,\n resolvedId: id,\n })),\n disablePartialExtraction: compilerProject.disablePartialExtraction,\n experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,\n zeroRuntime: compilerProject.zeroRuntime,\n })\n if (zero) {\n if (zero.isEnforcing) {\n Static.assertZeroConfigDrivers(compilerProject.projectInfo.tamaguiConfig)\n }\n zero.plansRestoredFromCache = false\n zero.configCSS = compilerProject.projectInfo.tamaguiConfig.getCSS?.() ?? ''\n zero.artifact.clearGraphs()\n zero.bridges.clear()\n zero.violations.length = 0\n zero.transformed.clear()\n zero.erasedExports.clear()\n // The zero contract applies to an ENTRY GRAPH. Metro's frontend plans\n // every project source by directory walk, so a config module, a control\n // fixture, or another entry's page would otherwise be judged against a\n // contract they are not part of.\n this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId))\n }\n for (const id of unplanned) this.#refreshEntry(id)\n await this.#storePlans(unplanned)\n }\n if (zero) {\n // Written in both modes and before the failure, so `report` and `enforce`\n // emit the identical list and only their exit differs.\n Static.writeZeroViolationReport(zero.resolved.outDir, 'metro-zero', {\n integration: 'metro-web',\n mode: zero.isEnforcing ? 'enforce' : 'report',\n violations: zero.violations,\n })\n if (zero.isEnforcing && zero.violations.length) {\n throw new Error(Static.formatZeroViolations(zero.violations))\n }\n }\n const totalFound = [...this.#entries.values()].reduce(\n (sum, entry) => sum + entry.plan.stats.found,\n 0\n )\n if (this.#entries.size > 0 && totalFound === 0) {\n const componentNames = compilerProject.componentModules.map(\n ({ moduleName }) => moduleName\n )\n const cjsComponentImporters = [...this.#records.values()].filter((record) =>\n record.requireSpecifiers.some((specifier) =>\n componentNames.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`)\n )\n )\n ).length\n if (cjsComponentImporters > 0) {\n const diagnostic = metroDiagnostic(\n 'metro/no-linked-components',\n `The Tamagui compiler linked 0 components across ${this.#entries.size} modules even though ` +\n `${cjsComponentImporters} module(s) reference ${componentNames.join(', ')} through require() calls. ` +\n `Metro compiled modules to CommonJS before the compiler could analyze them, so component ` +\n `imports cannot be linked and nothing will be optimized. Enable experimentalImportSupport ` +\n `in your transformer's getTransformOptions (Expo enables it by default) to restore ` +\n `Tamagui compilation.`\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n const generation = await this.#publish(options.platform)\n const moduleIds = [...this.#records.keys()].sort(compareCodeUnits)\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#installWatchers()\n } else if (!retainsLiveGraph(options)) {\n this.#releaseGraph()\n }\n return {\n generation,\n moduleIds,\n diagnostics,\n }\n }\n\n ensureValidCache(options: MetroCompilerScanOptions): Promise<MetroCompilerGeneration> {\n return this.#enqueue(() => this.#ensureValidCache(options))\n }\n\n async #ensureValidCache(\n options: MetroCompilerScanOptions\n ): Promise<MetroCompilerGeneration> {\n const diagnostics: MetroCompilerDiagnostic[] = []\n const firstEntry = options.entryFiles[0]\n const importer = firstEntry\n ? await realpath(resolve(this.config.projectRoot, firstEntry))\n : this.config.projectRoot\n const compilerProject = await this.#loadCompilerProject(\n options,\n importer,\n diagnostics\n )\n const cache = new MetroCompilerCache(this.cacheRootFor(options.platform))\n const validation = await cache.validate()\n const projectSources = await walkProjectSources(this.config.projectRoot)\n const optionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n contentHash(JSON.stringify(projectSources))\n )\n if (\n validation.valid &&\n validation.generation &&\n validation.optionsHash === optionsHash &&\n (await this.#sourcesAreFresh(validation.sourceHashes)) &&\n ((!retainsLiveGraph(options) && !this.#graph) ||\n (this.#publishedGeneration && this.#scanOptionsHash === optionsHash)) &&\n // A zero build owns the one CSS artifact, and its contents are produced by\n // the scan. Reusing a published plan without restoring the artifact would\n // emit one missing every rule this process never collected, while still\n // deriving TAMAGUI_DID_OUTPUT_CSS from it. The sidecar carries exactly\n // those side effects; without it there is nothing safe to reuse.\n (await this.#rehydrateZeroCSS(cache, validation.generation))\n ) {\n this.#publishedGeneration = validation.generation\n this.#scanOptions = options\n this.#scanOptionsHash = optionsHash\n this.#projectGeneration = compilerProject.generation\n return {\n generation: validation.generation,\n moduleIds: validation.moduleIds,\n diagnostics,\n }\n }\n for (const diagnostic of validation.diagnostics) this.#report(diagnostic)\n await cache.discardManifest()\n return await this.#scan(options, compilerProject, projectSources)\n }\n\n async updateFile(path: string): Promise<MetroCompilerUpdate> {\n let result: MetroCompilerUpdate = {\n changed: false,\n affectedIds: [],\n generation: null,\n }\n return this.#enqueue(async () => {\n const graph = this.#graph\n const options = this.#scanOptions\n if (!graph || !options) return result\n let record: CompiledRecord\n const diagnostics: MetroCompilerDiagnostic[] = []\n try {\n record = await this.#compileRecord(path, options, diagnostics)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n const id = resolvedModuleId(resolve(path))\n const invalidation = graph.removeModule(id)\n this.#watchers.get(id)?.close()\n this.#watchers.delete(id)\n this.#records.delete(id)\n this.#entries.delete(id)\n for (const affected of invalidation.invalidatedIds) {\n if (affected !== id) this.#refreshEntry(affected)\n }\n const generation = await this.#publish(options.platform)\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation,\n }\n return result\n }\n const diagnostic = metroDiagnostic(\n 'metro/transform-failed',\n `Failed to update ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n )\n this.#report(diagnostic)\n return result\n }\n\n for (const dependency of record.input.imports) {\n if (\n dependency.external ||\n !isCompilerSourceFile(dependency.resolvedId) ||\n this.#records.has(dependency.resolvedId)\n ) {\n continue\n }\n await this.#addDependency(dependency.resolvedId, options, diagnostics)\n }\n this.#records.set(record.input.id, record)\n const invalidation = graph.updateModule(record.input)\n for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected)\n const generation = invalidation.changed\n ? await this.#publish(options.platform)\n : null\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation,\n }\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#watchModule(record.input.id)\n }\n return result\n })\n }\n\n /** A published plan only applies while every recorded module source is unchanged. */\n async #sourcesAreFresh(sourceHashes: Record<string, string>): Promise<boolean> {\n const checks = Object.entries(sourceHashes).map(async ([moduleId, sourceHash]) => {\n try {\n return contentHash(await readFile(moduleId, 'utf8')) === sourceHash\n } catch {\n return false\n }\n })\n return (await Promise.all(checks)).every(Boolean)\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const queued = this.#operationQueue.then(operation)\n this.#operationQueue = queued.then(\n () => undefined,\n () => undefined\n )\n return queued\n }\n\n close(): Promise<void> {\n return this.#enqueue(async () => {\n this.#releaseGraph()\n })\n }\n\n #releaseGraph(): void {\n for (const watcher of this.#watchers.values()) watcher.close()\n this.#watchers.clear()\n this.#entries.clear()\n this.#records.clear()\n this.#planKeys.clear()\n this.#graph = null\n this.#host = null\n this.#projectGeneration = null\n }\n\n async #loadCompilerProject(\n options: MetroCompilerScanOptions,\n importer: string,\n diagnostics: MetroCompilerDiagnostic[]\n ): Promise<MetroCompilerProject> {\n const target = compilerTarget(options.platform)\n if (this.config.loadCompilerProject) {\n return await this.config.loadCompilerProject(target, options.platform)\n }\n return Static.loadCompilerProject({\n root: this.config.projectRoot,\n target,\n options: this.config.tamaguiOptions ?? {},\n hostVersions: compilerImplementationVersions,\n missingProjectMessage: 'Unable to load the Tamagui project for Metro compilation',\n generation: (projectInfo, componentModules, normalizedOptions) => {\n return contentHash(\n JSON.stringify({\n cacheVersion: METRO_COMPILER_CACHE_VERSION,\n compilerImplementationVersions,\n componentModules,\n configCss: projectInfo.tamaguiConfig?.getCSS?.() ?? '',\n disablePartialExtraction: !!normalizedOptions.disablePartialExtraction,\n experimentalNativeFastPath:\n target === 'native' &&\n normalizedOptions.experimental?.nativeFastPath === true,\n target,\n // the host's diagnostics are mode-aware, so a plan built in one mode is\n // not a plan the other mode may reuse\n zeroRuntime: !!this.config.zero,\n })\n )\n },\n resolveComponents: async (moduleNames) => {\n const componentModules: MetroCompilerProject['componentModules'] = []\n for (const moduleName of moduleNames) {\n try {\n const resolution = this.#resolver.resolve(\n importer,\n { specifier: moduleName, isESMImport: true },\n options.platform\n )\n if (!resolution) continue\n componentModules.push({ moduleName, id: resolution.resolvedId })\n } catch (error) {\n const diagnostic = metroDiagnostic(\n 'metro/resolve-failed',\n `Failed to resolve compiler component ${moduleName}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: importer, dependency: moduleName }\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n return componentModules\n },\n })\n }\n\n async #compileRecord(\n rawPath: string,\n options: MetroCompilerScanOptions,\n diagnostics: MetroCompilerDiagnostic[]\n ): Promise<CompiledRecord> {\n const path = await realpath(resolve(rawPath))\n const source = await readFile(path, 'utf8')\n const sourceHash = contentHash(source)\n const id = resolvedModuleId(path)\n const cache = this.#recordCache\n const identity = this.#recordCacheIdentity\n const key = cache && identity ? contentHash(`${identity}\\0${sourceHash}`) : null\n if (cache && key) {\n const cached = await cache.read(key, (value) => {\n const entry = value as CachedRecord | null\n return entry?.schemaVersion === METRO_RECORD_CACHE_VERSION &&\n entry.sourceHash === sourceHash &&\n Array.isArray(entry.imports) &&\n Array.isArray(entry.requireSpecifiers) &&\n Array.isArray(entry.diagnostics)\n ? entry\n : null\n })\n if (cached) {\n for (const diagnostic of cached.diagnostics) {\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n return {\n input: { id, source, imports: cached.imports },\n sourceHash,\n requireSpecifiers: cached.requireSpecifiers,\n }\n }\n }\n\n const args = this.#babelArgs(path, source, options)\n const compiled = await compileWithUserBabel(\n this.config.originalBabelTransformerPath,\n args\n )\n const imports: HostResolvedImport[] = []\n const requireSpecifiers: string[] = []\n const recordDiagnostics: MetroCompilerDiagnostic[] = []\n for (const dependency of moduleSpecifiersFromAst(compiled.result.ast)) {\n if (!dependency.isESMImport) requireSpecifiers.push(dependency.specifier)\n try {\n const resolution = this.#resolver.resolve(path, dependency, options.platform)\n if (!resolution) continue\n imports.push({\n specifier: resolution.specifier,\n resolvedId: resolvedModuleId(resolution.resolvedId),\n external: resolution.external,\n })\n } catch (error) {\n recordDiagnostics.push(\n metroDiagnostic(\n 'metro/resolve-failed',\n `Failed to resolve ${dependency.specifier} from ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path, dependency: dependency.specifier }\n )\n )\n }\n }\n for (const diagnostic of recordDiagnostics) {\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n if (cache && key) {\n await cache.write(key, {\n schemaVersion: METRO_RECORD_CACHE_VERSION,\n sourceHash,\n imports,\n requireSpecifiers,\n diagnostics: recordDiagnostics,\n } satisfies CachedRecord)\n }\n return {\n // The graph and plans operate on raw source: workers apply plan edits to\n // the raw module before their own Babel pass, so plans never depend on\n // this process's Babel output matching the workers' byte for byte.\n input: { id, source, imports },\n sourceHash,\n requireSpecifiers,\n }\n }\n\n #babelOptions(options: MetroCompilerScanOptions): MetroBabelTransformArgs['options'] {\n const transformer = this.config.transformer ?? {}\n return {\n ...options.transform,\n dev: options.dev,\n hot: options.hot,\n platform: options.platform,\n projectRoot: this.config.projectRoot,\n enableBabelRCLookup: transformer.enableBabelRCLookup ?? true,\n enableBabelRuntime: transformer.enableBabelRuntime ?? true,\n hermesParser: transformer.hermesParser ?? false,\n publicPath: transformer.publicPath ?? '/assets',\n }\n }\n\n #babelArgs(\n filename: string,\n src: string,\n options: MetroCompilerScanOptions\n ): MetroBabelTransformArgs {\n return { filename, src, plugins: [], options: this.#babelOptions(options) }\n }\n\n async #addDependency(\n id: ResolvedModuleId,\n options: MetroCompilerScanOptions,\n diagnostics: MetroCompilerDiagnostic[],\n visiting = new Set<ResolvedModuleId>()\n ): Promise<void> {\n if (this.#records.has(id) || visiting.has(id)) return\n visiting.add(id)\n try {\n const record = await this.#compileRecord(id, options, diagnostics)\n for (const dependency of record.input.imports) {\n if (!dependency.external && isCompilerSourceFile(dependency.resolvedId)) {\n await this.#addDependency(dependency.resolvedId, options, diagnostics, visiting)\n }\n }\n this.#records.set(id, record)\n const invalidation = this.#graph?.updateModule(record.input)\n for (const affected of invalidation?.invalidatedIds ?? [id]) {\n this.#refreshEntry(affected)\n }\n if (\n this.config.watch !== false &&\n this.#scanOptions &&\n retainsLiveGraph(this.#scanOptions)\n ) {\n this.#watchModule(id)\n }\n } finally {\n visiting.delete(id)\n }\n }\n\n #refreshEntry(id: ResolvedModuleId): void {\n const graph = this.#graph\n const host = this.#host\n const record = this.#records.get(id)\n if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration)\n return\n const target = compilerTarget(this.#scanOptions.platform)\n const plan = lowerModule({\n module: materializeModule(graph, id),\n source: record.input.source,\n target,\n host,\n options: { projectGeneration: this.#projectGeneration },\n })\n // Zero-mode reference erasure rides the same plan. Metro fixes a module's\n // dependencies at resolution time and does no export-level shaking, so the\n // plan a worker applies before Babel is the only point early enough to\n // remove an import from the graph.\n const zeroPlan = this.#zeroPlanFor(id, record.input.source, plan)\n this.#entries.set(id, this.#entryFor(id, record, zeroPlan ?? plan))\n }\n\n /**\n * One plan becomes one cache entry the same way whether the plan was just\n * lowered or read back off disk, so a restored build reports exactly the\n * diagnostics a fresh one did.\n */\n #entryFor(\n id: ResolvedModuleId,\n record: CompiledRecord,\n plan: LoweredModulePlan\n ): MetroCompilerCacheEntry {\n return {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n moduleId: id,\n sourceHash: record.sourceHash,\n plan,\n diagnostics: plan.diagnostics.map(\n ({ code, message, dependencyId, span, component }) => {\n const { line, column } = Static.offsetToLineColumn(\n record.input.source,\n span.start\n )\n return metroDiagnostic(\n code.startsWith('linked/')\n ? 'metro/resolve-failed'\n : 'metro/transform-failed',\n message,\n { moduleId: id, dependency: dependencyId, span, line, column, component }\n )\n }\n ),\n }\n }\n\n /**\n * Both per-file caches for this scan. A project with no content stamp gets\n * neither: a stamp that cannot see a config change would serve styles built\n * against the old config, so the answer is no cache rather than a partial one.\n *\n * Zero builds opt out of the plan cache because a zero plan is produced\n * alongside side effects that do not travel in the plan - the CSS artifact,\n * the bridge manifest, the violation list - so replaying one module's plan\n * without them would emit an artifact missing its rules.\n */\n #installCaches(\n options: MetroCompilerScanOptions,\n project: MetroCompilerProject,\n projectSourcesHash: string\n ): void {\n const platform = options.platform ?? 'default'\n const root = defaultPlanCacheRoot(this.config.projectRoot, platform)\n this.#recordCache = new JsonFileCache(\n join(root, 'records'),\n METRO_RECORD_CACHE_VERSION\n )\n this.#recordCacheIdentity = contentHash(\n stableStringify({\n schema: METRO_RECORD_CACHE_VERSION,\n resolver: this.#resolver.version,\n babel: userBabelCacheKey(this.config.originalBabelTransformerPath),\n // resolutions depend on which files exist, so the walked source list is\n // part of a record's identity exactly as it is for the plan manifest\n projectSourcesHash,\n platform,\n transform: this.#babelOptions(options),\n })\n )\n const stamp = project.cacheStamp\n const usePlanCache = typeof stamp === 'string' && stamp !== '' && !this.config.zero\n this.#planCache = usePlanCache ? new ModulePlanCache(join(root, 'plans')) : null\n this.#planCacheStamp = usePlanCache ? stamp : null\n }\n\n /**\n * Fills `#entries` from disk for every module whose whole compile input is\n * unchanged, and returns the ids that still have to be compiled. This is the\n * per-file property: one edited module leaves every other module's entry\n * valid, where the plan manifest would have discarded all of them.\n */\n async #restorePlans(options: MetroCompilerScanOptions): Promise<ResolvedModuleId[]> {\n this.#planKeys.clear()\n const cache = this.#planCache\n const stamp = this.#planCacheStamp\n if (!cache || !stamp) return [...this.#records.keys()].sort(compareCodeUnits)\n const target = compilerTarget(options.platform)\n const identity = {\n stamp,\n target,\n structuralPassHash: `${target}-noop-v1`,\n }\n const nodes = new Map<ResolvedModuleId, ModuleClosureNode | null>()\n const lookup = (id: ResolvedModuleId): ModuleClosureNode | null => {\n let node = nodes.get(id)\n if (node === undefined) {\n const record = this.#records.get(id)\n node = record ? moduleClosureNode(record.input) : null\n nodes.set(id, node)\n }\n return node\n }\n const memo = new Map<ResolvedModuleId, string | null>()\n const unplanned: ResolvedModuleId[] = []\n for (const id of [...this.#records.keys()].sort(compareCodeUnits)) {\n const record = this.#records.get(id)!\n const digest = moduleClosureDigest(id, lookup, memo)\n const key = digest && planCacheKey(identity, id, digest)\n const entry = key && digest ? await cache.read(key, id, digest) : null\n if (entry) {\n this.#entries.set(id, this.#entryFor(id, record, entry.plan))\n continue\n }\n if (key && digest) this.#planKeys.set(id, { key, digest })\n unplanned.push(id)\n }\n return unplanned\n }\n\n async #storePlans(ids: readonly ResolvedModuleId[]): Promise<void> {\n const cache = this.#planCache\n if (!cache) return\n const pending = ids.flatMap((id) => {\n const entry = this.#entries.get(id)\n const key = this.#planKeys.get(id)\n return entry && key ? [{ id, entry, key }] : []\n })\n // a first build writes one file per module, and doing that serially costs\n // seconds on a real project\n for (let index = 0; index < pending.length; index += 32) {\n await Promise.all(\n pending.slice(index, index + 32).map(({ id, entry, key }) =>\n cache.write(key.key, {\n schemaVersion: PLAN_CACHE_SCHEMA_VERSION,\n moduleId: id,\n closureDigest: key.digest,\n plan: entry.plan,\n })\n )\n )\n }\n }\n\n /** Modules reachable from the bundle's entry, over the frontend's own graph. */\n #reachableFrom(roots: readonly ResolvedModuleId[]): Set<ResolvedModuleId> {\n const reached = new Set<ResolvedModuleId>()\n const queue = [...roots]\n while (queue.length) {\n const id = queue.pop()!\n if (reached.has(id)) continue\n reached.add(id)\n for (const dependency of this.#records.get(id)?.input.imports ?? []) {\n if (!dependency.external) queue.push(dependency.resolvedId)\n }\n }\n return reached\n }\n\n /**\n * The zero transform for one module, returning a plan whose edits also carry\n * the static Theme lowering, the island bridge, and reference erasure.\n */\n #zeroPlanFor(\n id: ResolvedModuleId,\n source: string,\n plan: ReturnType<typeof lowerModule>\n ): ReturnType<typeof lowerModule> | null {\n const zero = this.config.zero\n const config = this.#tamaguiConfig\n if (!zero || !config) return null\n\n // An island build is a full-runtime graph: it contributes its compiler\n // atomic CSS to the one artifact and is never erased or judged.\n if (zero.islandBuild) {\n zero.artifact.setIslandModuleCSS(zero.islandBuild, id, plan.css)\n return null\n }\n\n if (this.#zeroEntryGraph && !this.#zeroEntryGraph.has(id)) return null\n // only app-authored modules: a workspace dependency resolves outside\n // node_modules here, and erasing Tamagui's own re-exports would break it\n const relativePath = relative(this.config.projectRoot, id)\n if (\n relativePath === '' ||\n relativePath.startsWith('..') ||\n relativePath.split(/[\\\\/]/).includes('node_modules')\n ) {\n return null\n }\n\n const result = Static.transformZeroModule({\n mode: zero.isEnforcing ? 'enforce' : 'report',\n id,\n root: this.config.projectRoot,\n source,\n plan,\n config,\n isTamaguiSpecifier,\n resolveIslandLoader: (specifier) => {\n const islandId = zero.loaderIds.get(zeroModuleKey(resolve(id, '..', specifier)))\n return islandId ? { islandId } : null\n },\n resolveIslandModule: (specifier) =>\n zero.islandModuleIds.get(zeroModuleKey(resolve(id, '..', specifier))) ?? null,\n })\n\n zero.transformed.add(id)\n if (result.erased.exports.length) {\n zero.erasedExports.set(id, result.erased.exports)\n }\n for (const violation of result.violations) {\n const { line, column } = Static.offsetToLineColumn(source, violation.span.start)\n zero.violations.push({\n file: relativePath,\n line,\n column,\n rule: violation.rule,\n code: violation.code,\n component: violation.component,\n message: violation.message,\n })\n }\n if (result.violations.length || !zero.isEnforcing) return null\n\n Static.mergeIslandBridges(zero.bridges, result.bridges)\n for (const [identifier, rules] of result.bridgeCSS) {\n zero.artifact.setBridgeRules(identifier, rules)\n }\n zero.artifact.setZeroModuleCSS(id, plan.css)\n return { ...plan, edits: [...plan.edits, ...result.edits] }\n }\n\n async #publish(platform: string | null): Promise<string> {\n const cache = new MetroCompilerCache(this.cacheRootFor(platform))\n const generation = await cache.publish(\n platform,\n [...this.#entries.values()],\n this.#scanOptionsHash ?? ''\n )\n const zero = this.config.zero\n if (zero && !zero.islandBuild) {\n // the plans and the artifact are the same scan's output, so they are\n // published together or the warm path has nothing safe to reuse\n await cache.publishZeroCSS({\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n configCSS: zero.configCSS,\n zeroModuleCSS: Object.fromEntries(zero.artifact.zeroModuleEntries()),\n bridgeCSS: Object.fromEntries(zero.artifact.bridgeEntries()),\n bridges: Object.fromEntries(zero.bridges),\n })\n }\n this.#publishedGeneration = generation\n return generation\n }\n\n /**\n * Restores the zero build's CSS side effects from the sidecar published with\n * this plan generation. Returns false when there is nothing trustworthy to\n * restore, which sends the caller to a full scan.\n */\n async #rehydrateZeroCSS(cache: MetroCompilerCache, generation: string) {\n const zero = this.config.zero\n if (!zero || zero.islandBuild) return true\n const sidecar = await cache.readZeroCSS(generation)\n if (!sidecar) return false\n zero.artifact.clearGraphs()\n zero.bridges.clear()\n zero.violations.length = 0\n zero.configCSS = sidecar.configCSS\n for (const [moduleId, css] of Object.entries(sidecar.zeroModuleCSS)) {\n zero.artifact.setZeroModuleCSS(moduleId, css)\n }\n for (const [bridgeId, css] of Object.entries(sidecar.bridgeCSS)) {\n zero.artifact.setBridgeRules(bridgeId, css)\n }\n for (const [islandId, bridges] of Object.entries(sidecar.bridges)) {\n zero.bridges.set(islandId, bridges as IslandThemeBridge[])\n }\n zero.plansRestoredFromCache = true\n return true\n }\n\n #installWatchers(): void {\n for (const id of this.#records.keys()) this.#watchModule(id)\n }\n\n #watchModule(id: ResolvedModuleId): void {\n if (this.#watchers.has(id)) return\n try {\n const watcher = watch(id, { persistent: false }, () => {\n void this.updateFile(id)\n })\n watcher.unref()\n this.#watchers.set(id, watcher)\n } catch {\n // A concurrent delete is handled by the importer's next invalidation.\n }\n }\n\n #report(diagnostic: MetroCompilerDiagnostic): void {\n this.config.reportDiagnostic?.(diagnostic)\n }\n}\n\nexport function describeMetroCompilerRoot(projectRoot: string, moduleId: string): string {\n const path = relative(projectRoot, moduleId)\n return path.startsWith('..') ? basename(moduleId) : path\n}\n"
9
+ "import { existsSync, watch, type FSWatcher } from 'node:fs'\nimport { readFile, readdir, realpath } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path'\n\nimport ignore, { type Ignore } from 'ignore'\n\nimport {\n JsonFileCache,\n ModulePlanCache,\n PLAN_CACHE_SCHEMA_VERSION,\n ProjectGraph,\n contentHash,\n createExternalClosureLookup,\n defaultPlanCacheRoot,\n isTamaguiSpecifier,\n lowerModule,\n materializeModule,\n moduleClosureDigest,\n moduleClosureNode,\n planCacheKey,\n resolvedModuleId,\n stableStringify,\n yukuFactory,\n type CompilerLoweringHost,\n type CompilerTarget,\n type HostModuleInput,\n type HostResolvedImport,\n type LoweredModulePlan,\n type ModuleClosureNode,\n type ResolvedModuleId,\n} from '@tamagui/compiler-core'\nimport Static, {\n ComponentDiscovery,\n createComponentRegistry,\n createTamaguiCompilerHost,\n} from '@tamagui/static'\nimport type {\n IslandThemeBridge,\n TamaguiOptions,\n TamaguiProjectInfo,\n} from '@tamagui/static'\n\nimport {\n compileWithUserBabel,\n userBabelCacheKey,\n type MetroBabelTransformArgs,\n} from './babel'\nimport { zeroModuleKey, type MetroZeroController } from './zeroRuntime'\nimport {\n METRO_COMPILER_CACHE_VERSION,\n MetroCompilerCache,\n defaultMetroCompilerCacheRoot,\n type MetroCompilerCacheEntry,\n} from './compilerCache'\nimport { metroDiagnostic, type MetroCompilerDiagnostic } from './diagnostics'\nimport {\n createMetroCompilerResolver,\n isCompilerSourceFile,\n moduleSpecifiersFromAst,\n type MetroResolverConfig,\n} from './metroResolver'\n\ninterface CompiledRecord {\n input: HostModuleInput\n sourceHash: string\n /** Specifiers that reached the compiled output as require() calls instead of imports. */\n requireSpecifiers: string[]\n}\n\n/**\n * Metro runs the user's whole Babel transformer over every project source just\n * to read its import specifiers, which is the single most expensive step of the\n * prepass. The result is a pure function of the module's own bytes plus the\n * resolver and Babel identity, so it caches per file with no closure involved.\n */\nexport const METRO_RECORD_CACHE_VERSION = 2\n\ninterface CachedRecord {\n schemaVersion: typeof METRO_RECORD_CACHE_VERSION\n sourceHash: string\n imports: HostResolvedImport[]\n requireSpecifiers: string[]\n /** Resolve failures replayed on a hit, so a cached record reports what a fresh one did. */\n diagnostics: MetroCompilerDiagnostic[]\n}\n\nexport interface MetroCompilerFrontendConfig extends MetroResolverConfig {\n cacheRoot?: string\n /** Present only for an enforced zero-runtime web build. */\n zero?: MetroZeroController | null\n originalBabelTransformerPath: string\n transformer?: Record<string, any>\n tamaguiOptions?: Partial<TamaguiOptions>\n loadCompilerProject?: (\n target: CompilerTarget,\n platform: string | null\n ) => Promise<MetroCompilerProject>\n watch?: boolean\n reportDiagnostic?: (diagnostic: MetroCompilerDiagnostic) => void\n}\n\nexport interface MetroCompilerProject extends Static.CompilerProject {}\n\nexport interface MetroCompilerScanOptions {\n dev: boolean\n entryFiles: readonly string[]\n hot: boolean\n platform: string | null\n transform?: Record<string, any>\n}\n\nexport interface MetroCompilerGeneration {\n generation: string\n moduleIds: string[]\n diagnostics: MetroCompilerDiagnostic[]\n}\n\nexport interface MetroCompilerUpdate {\n changed: boolean\n affectedIds: string[]\n generation: string | null\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nconst requireFromFrontend = createRequire(\n typeof __filename === 'string' ? __filename : import.meta.url\n)\n\n// upgrading the compiler must invalidate published plans even when the Tamagui\n// config output is unchanged\nconst compilerImplementationVersions = (\n ['@tamagui/metro-plugin', '@tamagui/static', '@tamagui/compiler-core'] as const\n).map((packageName) => {\n const { version } = requireFromFrontend(`${packageName}/package.json`) as {\n version: string\n }\n return `${packageName}@${version}`\n})\n\nfunction scanOptionsHash(\n options: MetroCompilerScanOptions,\n projectGeneration: string,\n projectSourcesHash: string\n): string {\n return contentHash(JSON.stringify({ options, projectGeneration, projectSourcesHash }))\n}\n\n// Metro entries can live inside node_modules (expo-router's entry reaches app\n// source only through require.context), so reachability from the entry alone\n// discovers nothing there. Project source is walked directly and seeded into\n// the scan alongside the entry; imports then extend the graph outside the\n// project root (workspace packages) exactly as before.\n//\n// The walked list is both the seed set and the plan cache's options hash, so it\n// has to be authored source only. Build output is whatever the project already\n// declares as ignored, read with git's own rules: a directory-name list cannot\n// know that `dist-metro`, `out` or `public/assets` are output, and a sibling\n// bundler's content-hashed filenames then re-key the plan cache on every\n// unrelated rebuild, forcing Metro to rescan a project that never changed.\n// `node_modules` is skipped structurally instead, because that is the same\n// externality boundary the resolver draws and it must hold with or without a\n// declaration.\ninterface IgnoreScope {\n dir: string\n matcher: Ignore\n}\n\nconst speculativeWalkExcludedDirs = new Set([\n '__tests__',\n 'e2e',\n 'flows',\n 'plugins',\n 'screenshots',\n 'scripts',\n 'test',\n 'test-results',\n 'tests',\n])\n\nasync function walkProjectSources(root: string): Promise<string[]> {\n // git reads every .gitignore from the repository root down to the file, so an\n // app nested in a monorepo inherits the declarations made above it\n const inherited: string[] = []\n let ancestor = root\n while (!existsSync(join(ancestor, '.git'))) {\n const parent = dirname(ancestor)\n if (parent === ancestor) break\n inherited.unshift(parent)\n ancestor = parent\n }\n const rootScopes: IgnoreScope[] = []\n for (const dir of inherited) {\n const source = await readFile(join(dir, '.gitignore'), 'utf8').catch(() => null)\n if (source) rootScopes.push({ dir, matcher: ignore().add(source) })\n }\n\n const found: string[] = []\n const stack: { dir: string; scopes: IgnoreScope[] }[] = [\n { dir: root, scopes: rootScopes },\n ]\n while (stack.length) {\n const { dir, scopes } = stack.pop()!\n let entries\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n continue\n }\n let active = scopes\n if (entries.some((entry) => entry.isFile() && entry.name === '.gitignore')) {\n const source = await readFile(join(dir, '.gitignore'), 'utf8').catch(() => null)\n if (source) active = [...scopes, { dir, matcher: ignore().add(source) }]\n }\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue\n const isDirectory = entry.isDirectory()\n if (isDirectory && speculativeWalkExcludedDirs.has(entry.name)) continue\n if (!isDirectory && !(entry.isFile() && isCompilerSourceFile(entry.name))) continue\n if (\n !isDirectory &&\n (/(?:^|[-.])(?:probe|run|spec|tests?)(?:[-.]|$)/i.test(entry.name) ||\n /\\.(?:build|config|workspace)\\.[cm]?[jt]sx?$/.test(entry.name))\n ) {\n continue\n }\n const path = join(dir, entry.name)\n let ignored = false\n for (const scope of active) {\n const relativePath = relative(scope.dir, path)\n if (!relativePath || relativePath.startsWith('..')) continue\n const candidate = relativePath.split(sep).join('/') + (isDirectory ? '/' : '')\n if (scope.matcher.ignores(candidate)) {\n ignored = true\n break\n }\n }\n if (ignored) continue\n if (isDirectory) stack.push({ dir: path, scopes: active })\n else found.push(path)\n }\n }\n return found.sort(compareCodeUnits)\n}\n\nfunction compilerTarget(platform: string | null): CompilerTarget {\n return platform === 'web' ? 'web' : 'native'\n}\n\nfunction retainsLiveGraph(options: MetroCompilerScanOptions): boolean {\n return options.dev && options.hot\n}\n\nexport class MetroCompilerFrontend {\n readonly #cacheBaseRoot: string\n readonly #entries = new Map<ResolvedModuleId, MetroCompilerCacheEntry>()\n readonly #records = new Map<ResolvedModuleId, CompiledRecord>()\n readonly #watchers = new Map<ResolvedModuleId, FSWatcher>()\n readonly #resolver\n #graph: ProjectGraph | null = null\n #host: CompilerLoweringHost | null = null\n #registry: Static.CompilerComponentRegistry | null = null\n readonly #discovery = new ComponentDiscovery()\n readonly #externalClosure = createExternalClosureLookup()\n #projectGeneration: string | null = null\n #publishedGeneration: string | null = null\n #scanOptions: MetroCompilerScanOptions | null = null\n #scanOptionsHash: string | null = null\n #operationQueue: Promise<void> = Promise.resolve()\n #tamaguiConfig: TamaguiProjectInfo['tamaguiConfig'] | null = null\n #zeroEntryGraph: Set<ResolvedModuleId> | null = null\n readonly #planKeys = new Map<ResolvedModuleId, { key: string; digest: string }>()\n #recordCache: JsonFileCache | null = null\n #recordCacheIdentity: string | null = null\n #planCache: ModulePlanCache | null = null\n #planCacheStamp: string | null = null\n\n constructor(readonly config: MetroCompilerFrontendConfig) {\n this.#cacheBaseRoot =\n config.cacheRoot ?? defaultMetroCompilerCacheRoot(config.projectRoot)\n this.#resolver = createMetroCompilerResolver(config)\n }\n\n get metroResolverVersion(): string {\n return this.#resolver.version\n }\n\n /**\n * Per-file cache accounting for the last scan. The point of these caches is\n * that one edited module leaves every other module's entry valid, and this is\n * how that is observed rather than assumed.\n */\n get compileCacheStats(): {\n plans: { hits: number; misses: number; writes: number }\n records: { hits: number; misses: number; writes: number }\n } {\n const empty = { hits: 0, misses: 0, writes: 0 }\n return {\n plans: this.#planCache?.stats ?? empty,\n records: this.#recordCache?.stats ?? empty,\n }\n }\n\n cacheRootFor(platform: string | null): string {\n return join(this.#cacheBaseRoot, platform ?? 'default')\n }\n\n scan(options: MetroCompilerScanOptions): Promise<MetroCompilerGeneration> {\n return this.#enqueue(() => this.#scan(options))\n }\n\n async #scan(\n options: MetroCompilerScanOptions,\n preparedProject?: MetroCompilerProject,\n preparedProjectSources?: string[]\n ): Promise<MetroCompilerGeneration> {\n this.#scanOptions = options\n this.#publishedGeneration = null\n const diagnostics: MetroCompilerDiagnostic[] = []\n const entryRoots = (\n await Promise.all(\n options.entryFiles.map((path) => realpath(resolve(this.config.projectRoot, path)))\n )\n ).sort(compareCodeUnits)\n const compilerProject =\n preparedProject ??\n (await this.#loadCompilerProject(options, entryRoots[0], diagnostics))\n this.#projectGeneration = compilerProject.generation\n const projectSources =\n preparedProjectSources ?? (await walkProjectSources(this.config.projectRoot))\n const projectSourcesHash = contentHash(JSON.stringify(projectSources))\n this.#scanOptionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n projectSourcesHash\n )\n this.#installCaches(options, compilerProject, projectSourcesHash)\n const speculativeRoots = new Set<string>()\n for (const file of projectSources) {\n try {\n const id = await realpath(file)\n if (!entryRoots.includes(id)) speculativeRoots.add(id)\n } catch {}\n }\n const roots = [...new Set([...entryRoots, ...speculativeRoots])].sort(\n compareCodeUnits\n )\n const queue = [...roots]\n const queued = new Set(queue)\n for (const watcher of this.#watchers.values()) watcher.close()\n this.#watchers.clear()\n this.#records.clear()\n\n while (queue.length) {\n const path = queue.shift()!\n try {\n const record = await this.#compileRecord(path, options, diagnostics)\n this.#records.set(record.input.id, record)\n for (const dependency of record.input.imports) {\n if (\n dependency.external ||\n !isCompilerSourceFile(dependency.resolvedId) ||\n queued.has(dependency.resolvedId)\n ) {\n continue\n }\n queued.add(dependency.resolvedId)\n queue.push(dependency.resolvedId)\n }\n } catch (error) {\n // walk-seeded files are speculative: nothing proved the bundle needs\n // them, so a compile failure is not a build diagnostic. If the bundle\n // does include one, the transformer's plan-miss warning still fires.\n if (speculativeRoots.has(path)) continue\n const diagnostic = metroDiagnostic(\n 'metro/transform-failed',\n `Failed to compile ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n\n if (\n !compilerProject.projectInfo.tamaguiConfig ||\n !compilerProject.projectInfo.components\n ) {\n throw new Error('Metro compiler project has no Tamagui config or components')\n }\n this.#tamaguiConfig = compilerProject.projectInfo.tamaguiConfig\n this.#entries.clear()\n const unplanned = await this.#restorePlans(options)\n const zero = this.config.zero\n // a scan that restores everything builds no graph, so the previous scan's\n // graph must not survive as this scan's answer\n this.#graph = null\n this.#host = null\n // Nothing left to compile and no live session to serve means the analyzer\n // graph is never read, so it is never built. Parsing and linking every\n // project source is the other half of the prepass cost.\n if (unplanned.length || retainsLiveGraph(options)) {\n this.#graph = new ProjectGraph(yukuFactory, {\n modules: [...this.#records.values()].map(({ input }) => input),\n })\n const componentModules = compilerProject.componentModules.map(\n ({ moduleName, id }) => ({ moduleName, resolvedId: id })\n )\n // a new project generation may carry new static configs, so discovery\n // starts over with it and re-registers what it finds\n this.#discovery.clear()\n this.#registry = createComponentRegistry(\n compilerProject.projectInfo.components,\n componentModules\n )\n this.#host = createTamaguiCompilerHost({\n target: compilerTarget(options.platform),\n tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,\n components: compilerProject.projectInfo.components,\n componentModules,\n registry: this.#registry,\n disablePartialExtraction: compilerProject.disablePartialExtraction,\n experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,\n zeroRuntime: compilerProject.zeroRuntime,\n })\n if (zero) {\n if (zero.isEnforcing) {\n Static.assertZeroConfigDrivers(compilerProject.projectInfo.tamaguiConfig)\n }\n zero.plansRestoredFromCache = false\n zero.configCSS = compilerProject.projectInfo.tamaguiConfig.getCSS?.() ?? ''\n zero.artifact.clearGraphs()\n zero.bridges.clear()\n zero.violations.length = 0\n zero.transformed.clear()\n zero.erasedExports.clear()\n // The zero contract applies to an ENTRY GRAPH. Metro's frontend plans\n // every project source by directory walk, so a config module, a control\n // fixture, or another entry's page would otherwise be judged against a\n // contract they are not part of.\n this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(resolvedModuleId))\n }\n for (const id of unplanned) await this.#refreshEntry(id)\n await this.#storePlans(unplanned)\n }\n if (zero) {\n // Written in both modes and before the failure, so `report` and `enforce`\n // emit the identical list and only their exit differs.\n Static.writeZeroViolationReport(zero.resolved.outDir, 'metro-zero', {\n integration: 'metro-web',\n mode: zero.isEnforcing ? 'enforce' : 'report',\n violations: zero.violations,\n })\n if (zero.isEnforcing && zero.violations.length) {\n throw new Error(Static.formatZeroViolations(zero.violations))\n }\n }\n const totalFound = [...this.#entries.values()].reduce(\n (sum, entry) => sum + entry.plan.stats.found,\n 0\n )\n if (this.#entries.size > 0 && totalFound === 0) {\n const componentNames = compilerProject.componentModules.map(\n ({ moduleName }) => moduleName\n )\n const cjsComponentImporters = [...this.#records.values()].filter((record) =>\n record.requireSpecifiers.some((specifier) =>\n componentNames.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`)\n )\n )\n ).length\n if (cjsComponentImporters > 0) {\n const diagnostic = metroDiagnostic(\n 'metro/no-linked-components',\n `The Tamagui compiler linked 0 components across ${this.#entries.size} modules even though ` +\n `${cjsComponentImporters} module(s) reference ${componentNames.join(', ')} through require() calls. ` +\n `Metro compiled modules to CommonJS before the compiler could analyze them, so component ` +\n `imports cannot be linked and nothing will be optimized. Enable experimentalImportSupport ` +\n `in your transformer's getTransformOptions (Expo enables it by default) to restore ` +\n `Tamagui compilation.`\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n const generation = await this.#publish(options.platform)\n const moduleIds = [...this.#records.keys()].sort(compareCodeUnits)\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#installWatchers()\n } else if (!retainsLiveGraph(options)) {\n this.#releaseGraph()\n }\n return {\n generation,\n moduleIds,\n diagnostics,\n }\n }\n\n ensureValidCache(options: MetroCompilerScanOptions): Promise<MetroCompilerGeneration> {\n return this.#enqueue(() => this.#ensureValidCache(options))\n }\n\n async #ensureValidCache(\n options: MetroCompilerScanOptions\n ): Promise<MetroCompilerGeneration> {\n const diagnostics: MetroCompilerDiagnostic[] = []\n const firstEntry = options.entryFiles[0]\n const importer = firstEntry\n ? await realpath(resolve(this.config.projectRoot, firstEntry))\n : this.config.projectRoot\n const compilerProject = await this.#loadCompilerProject(\n options,\n importer,\n diagnostics\n )\n const cache = new MetroCompilerCache(this.cacheRootFor(options.platform))\n const validation = await cache.validate()\n const projectSources = await walkProjectSources(this.config.projectRoot)\n const optionsHash = scanOptionsHash(\n options,\n compilerProject.generation,\n contentHash(JSON.stringify(projectSources))\n )\n if (\n validation.valid &&\n validation.generation &&\n validation.optionsHash === optionsHash &&\n (await this.#sourcesAreFresh(validation.sourceHashes)) &&\n ((!retainsLiveGraph(options) && !this.#graph) ||\n (this.#publishedGeneration && this.#scanOptionsHash === optionsHash)) &&\n // A zero build owns the one CSS artifact, and its contents are produced by\n // the scan. Reusing a published plan without restoring the artifact would\n // emit one missing every rule this process never collected, while still\n // deriving TAMAGUI_DID_OUTPUT_CSS from it. The sidecar carries exactly\n // those side effects; without it there is nothing safe to reuse.\n (await this.#rehydrateZeroCSS(cache, validation.generation))\n ) {\n this.#publishedGeneration = validation.generation\n this.#scanOptions = options\n this.#scanOptionsHash = optionsHash\n this.#projectGeneration = compilerProject.generation\n return {\n generation: validation.generation,\n moduleIds: validation.moduleIds,\n diagnostics,\n }\n }\n for (const diagnostic of validation.diagnostics) this.#report(diagnostic)\n await cache.discardManifest()\n return await this.#scan(options, compilerProject, projectSources)\n }\n\n async updateFile(path: string): Promise<MetroCompilerUpdate> {\n let result: MetroCompilerUpdate = {\n changed: false,\n affectedIds: [],\n generation: null,\n }\n return this.#enqueue(async () => {\n const graph = this.#graph\n const options = this.#scanOptions\n if (!graph || !options) return result\n let record: CompiledRecord\n const diagnostics: MetroCompilerDiagnostic[] = []\n try {\n record = await this.#compileRecord(path, options, diagnostics)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n const id = resolvedModuleId(resolve(path))\n const invalidation = graph.removeModule(id)\n this.#watchers.get(id)?.close()\n this.#watchers.delete(id)\n this.#records.delete(id)\n this.#entries.delete(id)\n for (const affected of invalidation.invalidatedIds) {\n if (affected !== id) await this.#refreshEntry(affected)\n }\n const generation = await this.#publish(options.platform)\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation,\n }\n return result\n }\n const diagnostic = metroDiagnostic(\n 'metro/transform-failed',\n `Failed to update ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path }\n )\n this.#report(diagnostic)\n return result\n }\n\n for (const dependency of record.input.imports) {\n if (\n dependency.external ||\n !isCompilerSourceFile(dependency.resolvedId) ||\n this.#records.has(dependency.resolvedId)\n ) {\n continue\n }\n await this.#addDependency(dependency.resolvedId, options, diagnostics)\n }\n this.#records.set(record.input.id, record)\n const invalidation = graph.updateModule(record.input)\n for (const affected of invalidation.invalidatedIds)\n await this.#refreshEntry(affected)\n const generation = invalidation.changed\n ? await this.#publish(options.platform)\n : null\n result = {\n changed: invalidation.changed,\n affectedIds: invalidation.invalidatedIds,\n generation,\n }\n if (this.config.watch !== false && retainsLiveGraph(options)) {\n this.#watchModule(record.input.id)\n }\n return result\n })\n }\n\n /** A published plan only applies while every recorded module source is unchanged. */\n async #sourcesAreFresh(sourceHashes: Record<string, string>): Promise<boolean> {\n const checks = Object.entries(sourceHashes).map(async ([moduleId, sourceHash]) => {\n try {\n return contentHash(await readFile(moduleId, 'utf8')) === sourceHash\n } catch {\n return false\n }\n })\n return (await Promise.all(checks)).every(Boolean)\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const queued = this.#operationQueue.then(operation)\n this.#operationQueue = queued.then(\n () => undefined,\n () => undefined\n )\n return queued\n }\n\n close(): Promise<void> {\n return this.#enqueue(async () => {\n this.#releaseGraph()\n })\n }\n\n #releaseGraph(): void {\n for (const watcher of this.#watchers.values()) watcher.close()\n this.#watchers.clear()\n this.#entries.clear()\n this.#records.clear()\n this.#planKeys.clear()\n this.#graph = null\n this.#host = null\n this.#registry = null\n this.#projectGeneration = null\n }\n\n async #loadCompilerProject(\n options: MetroCompilerScanOptions,\n importer: string,\n diagnostics: MetroCompilerDiagnostic[]\n ): Promise<MetroCompilerProject> {\n const target = compilerTarget(options.platform)\n if (this.config.loadCompilerProject) {\n return await this.config.loadCompilerProject(target, options.platform)\n }\n return Static.loadCompilerProject({\n root: this.config.projectRoot,\n target,\n options: this.config.tamaguiOptions ?? {},\n hostVersions: compilerImplementationVersions,\n missingProjectMessage: 'Unable to load the Tamagui project for Metro compilation',\n generation: (projectInfo, componentModules, normalizedOptions) => {\n return contentHash(\n JSON.stringify({\n cacheVersion: METRO_COMPILER_CACHE_VERSION,\n compilerImplementationVersions,\n componentModules,\n configCss: projectInfo.tamaguiConfig?.getCSS?.() ?? '',\n disablePartialExtraction: !!normalizedOptions.disablePartialExtraction,\n experimentalNativeFastPath:\n target === 'native' &&\n normalizedOptions.experimental?.nativeFastPath === true,\n target,\n // the host's diagnostics are mode-aware, so a plan built in one mode is\n // not a plan the other mode may reuse\n zeroRuntime: !!this.config.zero,\n })\n )\n },\n resolveComponents: async (moduleNames) => {\n const componentModules: MetroCompilerProject['componentModules'] = []\n for (const moduleName of moduleNames) {\n try {\n const resolution = this.#resolver.resolve(\n importer,\n { specifier: moduleName, isESMImport: true },\n options.platform\n )\n if (!resolution) continue\n componentModules.push({ moduleName, id: resolution.resolvedId })\n } catch (error) {\n const diagnostic = metroDiagnostic(\n 'metro/resolve-failed',\n `Failed to resolve compiler component ${moduleName}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: importer, dependency: moduleName }\n )\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n }\n return componentModules\n },\n })\n }\n\n async #compileRecord(\n rawPath: string,\n options: MetroCompilerScanOptions,\n diagnostics: MetroCompilerDiagnostic[]\n ): Promise<CompiledRecord> {\n const path = await realpath(resolve(rawPath))\n const source = await readFile(path, 'utf8')\n const sourceHash = contentHash(source)\n const id = resolvedModuleId(path)\n const cache = this.#recordCache\n const identity = this.#recordCacheIdentity\n const key = cache && identity ? contentHash(`${identity}\\0${sourceHash}`) : null\n if (cache && key) {\n const cached = await cache.read(key, (value) => {\n const entry = value as CachedRecord | null\n return entry?.schemaVersion === METRO_RECORD_CACHE_VERSION &&\n entry.sourceHash === sourceHash &&\n Array.isArray(entry.imports) &&\n Array.isArray(entry.requireSpecifiers) &&\n Array.isArray(entry.diagnostics)\n ? entry\n : null\n })\n if (cached) {\n for (const diagnostic of cached.diagnostics) {\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n return {\n input: { id, source, imports: cached.imports },\n sourceHash,\n requireSpecifiers: cached.requireSpecifiers,\n }\n }\n }\n\n const args = this.#babelArgs(path, source, options)\n const compiled = await compileWithUserBabel(\n this.config.originalBabelTransformerPath,\n args\n )\n const imports: HostResolvedImport[] = []\n const requireSpecifiers: string[] = []\n const recordDiagnostics: MetroCompilerDiagnostic[] = []\n for (const dependency of moduleSpecifiersFromAst(compiled.result.ast)) {\n if (!dependency.isESMImport) requireSpecifiers.push(dependency.specifier)\n try {\n const resolution = this.#resolver.resolve(path, dependency, options.platform)\n if (!resolution) continue\n imports.push({\n specifier: resolution.specifier,\n resolvedId: resolvedModuleId(resolution.resolvedId),\n external: resolution.external,\n })\n } catch (error) {\n recordDiagnostics.push(\n metroDiagnostic(\n 'metro/resolve-failed',\n `Failed to resolve ${dependency.specifier} from ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { moduleId: path, dependency: dependency.specifier }\n )\n )\n }\n }\n for (const diagnostic of recordDiagnostics) {\n diagnostics.push(diagnostic)\n this.#report(diagnostic)\n }\n if (cache && key) {\n await cache.write(key, {\n schemaVersion: METRO_RECORD_CACHE_VERSION,\n sourceHash,\n imports,\n requireSpecifiers,\n diagnostics: recordDiagnostics,\n } satisfies CachedRecord)\n }\n return {\n // The graph and plans operate on raw source: workers apply plan edits to\n // the raw module before their own Babel pass, so plans never depend on\n // this process's Babel output matching the workers' byte for byte.\n input: { id, source, imports },\n sourceHash,\n requireSpecifiers,\n }\n }\n\n #babelOptions(options: MetroCompilerScanOptions): MetroBabelTransformArgs['options'] {\n const transformer = this.config.transformer ?? {}\n return {\n ...options.transform,\n dev: options.dev,\n hot: options.hot,\n platform: options.platform,\n projectRoot: this.config.projectRoot,\n enableBabelRCLookup: transformer.enableBabelRCLookup ?? true,\n enableBabelRuntime: transformer.enableBabelRuntime ?? true,\n hermesParser: transformer.hermesParser ?? false,\n publicPath: transformer.publicPath ?? '/assets',\n }\n }\n\n #babelArgs(\n filename: string,\n src: string,\n options: MetroCompilerScanOptions\n ): MetroBabelTransformArgs {\n return { filename, src, plugins: [], options: this.#babelOptions(options) }\n }\n\n async #addDependency(\n id: ResolvedModuleId,\n options: MetroCompilerScanOptions,\n diagnostics: MetroCompilerDiagnostic[],\n visiting = new Set<ResolvedModuleId>()\n ): Promise<void> {\n if (this.#records.has(id) || visiting.has(id)) return\n visiting.add(id)\n try {\n const record = await this.#compileRecord(id, options, diagnostics)\n for (const dependency of record.input.imports) {\n if (!dependency.external && isCompilerSourceFile(dependency.resolvedId)) {\n await this.#addDependency(dependency.resolvedId, options, diagnostics, visiting)\n }\n }\n this.#records.set(id, record)\n const invalidation = this.#graph?.updateModule(record.input)\n for (const affected of invalidation?.invalidatedIds ?? [id]) {\n await this.#refreshEntry(affected)\n }\n if (\n this.config.watch !== false &&\n this.#scanOptions &&\n retainsLiveGraph(this.#scanOptions)\n ) {\n this.#watchModule(id)\n }\n } finally {\n visiting.delete(id)\n }\n }\n\n async #refreshEntry(id: ResolvedModuleId): Promise<void> {\n const graph = this.#graph\n const host = this.#host\n const registry = this.#registry\n const record = this.#records.get(id)\n if (\n !graph ||\n !host ||\n !registry ||\n !record ||\n !this.#scanOptions ||\n !this.#projectGeneration\n ) {\n return\n }\n const target = compilerTarget(this.#scanOptions.platform)\n const materialized = materializeModule(graph, id)\n // Metro has no module runner; packages a file uses that are not in\n // `components` evaluate under the static-evaluation require hooks, once\n await this.#discovery.prepare(materialized, registry, ({ id: moduleId }) =>\n Static.evaluateComponentModule(\n { ...this.config.tamaguiOptions, platform: target },\n moduleId\n )\n )\n const plan = lowerModule({\n module: materialized,\n source: record.input.source,\n target,\n host,\n options: { projectGeneration: this.#projectGeneration },\n })\n // Zero-mode reference erasure rides the same plan. Metro fixes a module's\n // dependencies at resolution time and does no export-level shaking, so the\n // plan a worker applies before Babel is the only point early enough to\n // remove an import from the graph.\n const zeroPlan = this.#zeroPlanFor(id, record.input.source, plan)\n this.#entries.set(id, this.#entryFor(id, record, zeroPlan ?? plan))\n }\n\n /**\n * One plan becomes one cache entry the same way whether the plan was just\n * lowered or read back off disk, so a restored build reports exactly the\n * diagnostics a fresh one did.\n */\n #entryFor(\n id: ResolvedModuleId,\n record: CompiledRecord,\n plan: LoweredModulePlan\n ): MetroCompilerCacheEntry {\n return {\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n moduleId: id,\n sourceHash: record.sourceHash,\n plan,\n diagnostics: plan.diagnostics.map(\n ({ code, message, dependencyId, span, component }) => {\n const { line, column } = Static.offsetToLineColumn(\n record.input.source,\n span.start\n )\n return metroDiagnostic(\n code.startsWith('linked/')\n ? 'metro/resolve-failed'\n : 'metro/transform-failed',\n message,\n { moduleId: id, dependency: dependencyId, span, line, column, component }\n )\n }\n ),\n }\n }\n\n /**\n * Both per-file caches for this scan. A project with no content stamp gets\n * neither: a stamp that cannot see a config change would serve styles built\n * against the old config, so the answer is no cache rather than a partial one.\n *\n * Zero builds opt out of the plan cache because a zero plan is produced\n * alongside side effects that do not travel in the plan - the CSS artifact,\n * the bridge manifest, the violation list - so replaying one module's plan\n * without them would emit an artifact missing its rules.\n */\n #installCaches(\n options: MetroCompilerScanOptions,\n project: MetroCompilerProject,\n projectSourcesHash: string\n ): void {\n const platform = options.platform ?? 'default'\n const root = defaultPlanCacheRoot(this.config.projectRoot, platform)\n this.#recordCache = new JsonFileCache(\n join(root, 'records'),\n METRO_RECORD_CACHE_VERSION\n )\n this.#recordCacheIdentity = contentHash(\n stableStringify({\n schema: METRO_RECORD_CACHE_VERSION,\n resolver: this.#resolver.version,\n babel: userBabelCacheKey(this.config.originalBabelTransformerPath),\n // resolutions depend on which files exist, so the walked source list is\n // part of a record's identity exactly as it is for the plan manifest\n projectSourcesHash,\n platform,\n transform: this.#babelOptions(options),\n })\n )\n const stamp = project.cacheStamp\n const usePlanCache = typeof stamp === 'string' && stamp !== '' && !this.config.zero\n this.#planCache = usePlanCache ? new ModulePlanCache(join(root, 'plans')) : null\n this.#planCacheStamp = usePlanCache ? stamp : null\n }\n\n /**\n * Fills `#entries` from disk for every module whose whole compile input is\n * unchanged, and returns the ids that still have to be compiled. This is the\n * per-file property: one edited module leaves every other module's entry\n * valid, where the plan manifest would have discarded all of them.\n */\n async #restorePlans(options: MetroCompilerScanOptions): Promise<ResolvedModuleId[]> {\n this.#planKeys.clear()\n const cache = this.#planCache\n const stamp = this.#planCacheStamp\n if (!cache || !stamp) return [...this.#records.keys()].sort(compareCodeUnits)\n const target = compilerTarget(options.platform)\n const identity = {\n stamp,\n target,\n structuralPassHash: `${target}-noop-v1`,\n }\n const nodes = new Map<ResolvedModuleId, ModuleClosureNode | null>()\n const lookup = (id: ResolvedModuleId): ModuleClosureNode | null => {\n let node = nodes.get(id)\n if (node === undefined) {\n const record = this.#records.get(id)\n node = record\n ? moduleClosureNode(record.input, { includeExternal: true })\n : this.#externalClosure(id)\n nodes.set(id, node)\n }\n return node\n }\n const memo = new Map<ResolvedModuleId, string | null>()\n const unplanned: ResolvedModuleId[] = []\n for (const id of [...this.#records.keys()].sort(compareCodeUnits)) {\n const record = this.#records.get(id)!\n const digest = moduleClosureDigest(id, lookup, memo)\n const key = digest && planCacheKey(identity, id, digest)\n const entry = key && digest ? await cache.read(key, id, digest) : null\n if (entry) {\n this.#entries.set(id, this.#entryFor(id, record, entry.plan))\n continue\n }\n if (key && digest) this.#planKeys.set(id, { key, digest })\n unplanned.push(id)\n }\n return unplanned\n }\n\n async #storePlans(ids: readonly ResolvedModuleId[]): Promise<void> {\n const cache = this.#planCache\n if (!cache) return\n const pending = ids.flatMap((id) => {\n const entry = this.#entries.get(id)\n const key = this.#planKeys.get(id)\n return entry && key ? [{ id, entry, key }] : []\n })\n // a first build writes one file per module, and doing that serially costs\n // seconds on a real project\n for (let index = 0; index < pending.length; index += 32) {\n await Promise.all(\n pending.slice(index, index + 32).map(({ id, entry, key }) =>\n cache.write(key.key, {\n schemaVersion: PLAN_CACHE_SCHEMA_VERSION,\n moduleId: id,\n closureDigest: key.digest,\n plan: entry.plan,\n })\n )\n )\n }\n }\n\n /** Modules reachable from the bundle's entry, over the frontend's own graph. */\n #reachableFrom(roots: readonly ResolvedModuleId[]): Set<ResolvedModuleId> {\n const reached = new Set<ResolvedModuleId>()\n const queue = [...roots]\n while (queue.length) {\n const id = queue.pop()!\n if (reached.has(id)) continue\n reached.add(id)\n for (const dependency of this.#records.get(id)?.input.imports ?? []) {\n if (!dependency.external) queue.push(dependency.resolvedId)\n }\n }\n return reached\n }\n\n /**\n * The zero transform for one module, returning a plan whose edits also carry\n * the static Theme lowering, the island bridge, and reference erasure.\n */\n #zeroPlanFor(\n id: ResolvedModuleId,\n source: string,\n plan: ReturnType<typeof lowerModule>\n ): ReturnType<typeof lowerModule> | null {\n const zero = this.config.zero\n const config = this.#tamaguiConfig\n if (!zero || !config) return null\n\n // An island build is a full-runtime graph: it contributes its compiler\n // atomic CSS to the one artifact and is never erased or judged.\n if (zero.islandBuild) {\n zero.artifact.setIslandModuleCSS(zero.islandBuild, id, plan.css)\n return null\n }\n\n if (this.#zeroEntryGraph && !this.#zeroEntryGraph.has(id)) return null\n // only app-authored modules: a workspace dependency resolves outside\n // node_modules here, and erasing Tamagui's own re-exports would break it\n const relativePath = relative(this.config.projectRoot, id)\n if (\n relativePath === '' ||\n relativePath.startsWith('..') ||\n relativePath.split(/[\\\\/]/).includes('node_modules')\n ) {\n return null\n }\n\n const result = Static.transformZeroModule({\n mode: zero.isEnforcing ? 'enforce' : 'report',\n id,\n root: this.config.projectRoot,\n source,\n plan,\n config,\n isTamaguiSpecifier,\n resolveIslandLoader: (specifier) => {\n const islandId = zero.loaderIds.get(zeroModuleKey(resolve(id, '..', specifier)))\n return islandId ? { islandId } : null\n },\n resolveIslandModule: (specifier) =>\n zero.islandModuleIds.get(zeroModuleKey(resolve(id, '..', specifier))) ?? null,\n })\n\n zero.transformed.add(id)\n if (result.erased.exports.length) {\n zero.erasedExports.set(id, result.erased.exports)\n }\n for (const violation of result.violations) {\n const { line, column } = Static.offsetToLineColumn(source, violation.span.start)\n zero.violations.push({\n file: relativePath,\n line,\n column,\n rule: violation.rule,\n code: violation.code,\n component: violation.component,\n message: violation.message,\n })\n }\n if (result.violations.length || !zero.isEnforcing) return null\n\n Static.mergeIslandBridges(zero.bridges, result.bridges)\n for (const [identifier, rules] of result.bridgeCSS) {\n zero.artifact.setBridgeRules(identifier, rules)\n }\n zero.artifact.setZeroModuleCSS(id, plan.css)\n return { ...plan, edits: [...plan.edits, ...result.edits] }\n }\n\n async #publish(platform: string | null): Promise<string> {\n const cache = new MetroCompilerCache(this.cacheRootFor(platform))\n const generation = await cache.publish(\n platform,\n [...this.#entries.values()],\n this.#scanOptionsHash ?? ''\n )\n const zero = this.config.zero\n if (zero && !zero.islandBuild) {\n // the plans and the artifact are the same scan's output, so they are\n // published together or the warm path has nothing safe to reuse\n await cache.publishZeroCSS({\n schemaVersion: METRO_COMPILER_CACHE_VERSION,\n generation,\n configCSS: zero.configCSS,\n zeroModuleCSS: Object.fromEntries(zero.artifact.zeroModuleEntries()),\n bridgeCSS: Object.fromEntries(zero.artifact.bridgeEntries()),\n bridges: Object.fromEntries(zero.bridges),\n })\n }\n this.#publishedGeneration = generation\n return generation\n }\n\n /**\n * Restores the zero build's CSS side effects from the sidecar published with\n * this plan generation. Returns false when there is nothing trustworthy to\n * restore, which sends the caller to a full scan.\n */\n async #rehydrateZeroCSS(cache: MetroCompilerCache, generation: string) {\n const zero = this.config.zero\n if (!zero || zero.islandBuild) return true\n const sidecar = await cache.readZeroCSS(generation)\n if (!sidecar) return false\n zero.artifact.clearGraphs()\n zero.bridges.clear()\n zero.violations.length = 0\n zero.configCSS = sidecar.configCSS\n for (const [moduleId, css] of Object.entries(sidecar.zeroModuleCSS)) {\n zero.artifact.setZeroModuleCSS(moduleId, css)\n }\n for (const [bridgeId, css] of Object.entries(sidecar.bridgeCSS)) {\n zero.artifact.setBridgeRules(bridgeId, css)\n }\n for (const [islandId, bridges] of Object.entries(sidecar.bridges)) {\n zero.bridges.set(islandId, bridges as IslandThemeBridge[])\n }\n zero.plansRestoredFromCache = true\n return true\n }\n\n #installWatchers(): void {\n for (const id of this.#records.keys()) this.#watchModule(id)\n }\n\n #watchModule(id: ResolvedModuleId): void {\n if (this.#watchers.has(id)) return\n try {\n const watcher = watch(id, { persistent: false }, () => {\n void this.updateFile(id)\n })\n watcher.unref()\n this.#watchers.set(id, watcher)\n } catch {\n // A concurrent delete is handled by the importer's next invalidation.\n }\n }\n\n #report(diagnostic: MetroCompilerDiagnostic): void {\n this.config.reportDiagnostic?.(diagnostic)\n }\n}\n\nexport function describeMetroCompilerRoot(projectRoot: string, moduleId: string): string {\n const path = relative(projectRoot, moduleId)\n return path.startsWith('..') ? basename(moduleId) : path\n}\n"
10
10
  ]
11
11
  }