@tamagui/metro-plugin 2.7.7 → 3.0.0-beta.643.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cjs/babel.cjs +77 -0
- package/dist/cjs/compilerCache.cjs +237 -0
- package/dist/cjs/diagnostics.cjs +41 -0
- package/dist/cjs/frontend.cjs +870 -0
- package/dist/cjs/index.cjs +102 -0
- package/dist/cjs/lowering.cjs +109 -0
- package/dist/cjs/metroResolver.cjs +197 -0
- package/dist/cjs/transformOptions.cjs +35 -0
- package/dist/cjs/transformer.cjs +142 -0
- package/dist/cjs/zeroRuntime.cjs +140 -0
- package/dist/cjs/zeroSerializer.cjs +150 -0
- package/dist/esm/babel.mjs +52 -0
- package/dist/esm/babel.mjs.map +1 -0
- package/dist/esm/compilerCache.mjs +212 -0
- package/dist/esm/compilerCache.mjs.map +1 -0
- package/dist/esm/diagnostics.mjs +18 -0
- package/dist/esm/diagnostics.mjs.map +1 -0
- package/dist/esm/frontend.mjs +839 -0
- package/dist/esm/frontend.mjs.map +1 -0
- package/dist/esm/index.mjs +64 -22
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/lowering.mjs +89 -0
- package/dist/esm/lowering.mjs.map +1 -0
- package/dist/esm/metroResolver.mjs +173 -0
- package/dist/esm/metroResolver.mjs.map +1 -0
- package/dist/esm/transformOptions.mjs +14 -0
- package/dist/esm/transformOptions.mjs.map +1 -0
- package/dist/esm/transformer.mjs +119 -0
- package/dist/esm/transformer.mjs.map +1 -0
- package/dist/esm/zeroRuntime.mjs +105 -0
- package/dist/esm/zeroRuntime.mjs.map +1 -0
- package/dist/esm/zeroSerializer.mjs +123 -0
- package/dist/esm/zeroSerializer.mjs.map +1 -0
- package/package.json +33 -5
- package/src/babel.ts +87 -0
- package/src/compilerCache.ts +346 -0
- package/src/diagnostics.ts +47 -0
- package/src/frontend.ts +1178 -0
- package/src/index.ts +117 -14
- package/src/lowering.ts +136 -0
- package/src/metroResolver.ts +209 -0
- package/src/transformOptions.ts +36 -0
- package/src/transformer.ts +210 -0
- package/src/zeroRuntime.ts +212 -0
- package/src/zeroSerializer.ts +175 -0
- package/types/babel.d.ts +28 -0
- package/types/babel.d.ts.map +11 -0
- package/types/compilerCache.d.ts +63 -0
- package/types/compilerCache.d.ts.map +11 -0
- package/types/diagnostics.d.ts +16 -0
- package/types/diagnostics.d.ts.map +11 -0
- package/types/frontend.d.ts +73 -0
- package/types/frontend.d.ts.map +11 -0
- package/types/index.d.ts +49 -32
- package/types/index.d.ts.map +11 -1
- package/types/lowering.d.ts +20 -0
- package/types/lowering.d.ts.map +11 -0
- package/types/metroResolver.d.ts +21 -0
- package/types/metroResolver.d.ts.map +11 -0
- package/types/transformOptions.d.ts +13 -0
- package/types/transformOptions.d.ts.map +11 -0
- package/types/transformer.d.ts +26 -0
- package/types/transformer.d.ts.map +11 -0
- package/types/zeroRuntime.d.ts +75 -0
- package/types/zeroRuntime.d.ts.map +11 -0
- package/types/zeroSerializer.d.ts +6 -0
- package/types/zeroSerializer.d.ts.map +11 -0
- package/dist/cjs/index.js +0 -45
- package/dist/cjs/index.js.map +0 -6
- package/dist/esm/index.js +0 -25
- package/dist/esm/index.js.map +0 -1
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all) __defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
17
|
+
get: () => from[key],
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
+
value: mod,
|
|
25
|
+
enumerable: true
|
|
26
|
+
}) : target, mod));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var frontend_exports = {};
|
|
29
|
+
__export(frontend_exports, {
|
|
30
|
+
METRO_RECORD_CACHE_VERSION: () => METRO_RECORD_CACHE_VERSION,
|
|
31
|
+
MetroCompilerFrontend: () => MetroCompilerFrontend,
|
|
32
|
+
describeMetroCompilerRoot: () => describeMetroCompilerRoot
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(frontend_exports);
|
|
35
|
+
var import_node_fs = require("node:fs");
|
|
36
|
+
var import_promises = require("node:fs/promises");
|
|
37
|
+
var import_node_module = require("node:module");
|
|
38
|
+
var import_node_path = require("node:path");
|
|
39
|
+
var import_ignore = __toESM(require("ignore"), 1);
|
|
40
|
+
var import_compiler_core = require("@tamagui/compiler-core");
|
|
41
|
+
var import_static = __toESM(require("@tamagui/static"), 1);
|
|
42
|
+
var import_babel = require("./babel.cjs");
|
|
43
|
+
var import_zeroRuntime = require("./zeroRuntime.cjs");
|
|
44
|
+
var import_compilerCache = require("./compilerCache.cjs");
|
|
45
|
+
var import_diagnostics = require("./diagnostics.cjs");
|
|
46
|
+
var import_metroResolver = require("./metroResolver.cjs");
|
|
47
|
+
const import_meta = {};
|
|
48
|
+
const METRO_RECORD_CACHE_VERSION = 1;
|
|
49
|
+
function compareCodeUnits(left, right) {
|
|
50
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
51
|
+
}
|
|
52
|
+
const requireFromFrontend = (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : import_meta.url);
|
|
53
|
+
const compilerImplementationVersions = [
|
|
54
|
+
"@tamagui/metro-plugin",
|
|
55
|
+
"@tamagui/static",
|
|
56
|
+
"@tamagui/compiler-core"
|
|
57
|
+
].map((packageName) => {
|
|
58
|
+
const { version } = requireFromFrontend(`${packageName}/package.json`);
|
|
59
|
+
return `${packageName}@${version}`;
|
|
60
|
+
});
|
|
61
|
+
function scanOptionsHash(options, projectGeneration, projectSourcesHash) {
|
|
62
|
+
return (0, import_compiler_core.contentHash)(JSON.stringify({
|
|
63
|
+
options,
|
|
64
|
+
projectGeneration,
|
|
65
|
+
projectSourcesHash
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
const speculativeWalkExcludedDirs = /* @__PURE__ */ new Set([
|
|
69
|
+
"__tests__",
|
|
70
|
+
"e2e",
|
|
71
|
+
"flows",
|
|
72
|
+
"plugins",
|
|
73
|
+
"screenshots",
|
|
74
|
+
"scripts",
|
|
75
|
+
"test",
|
|
76
|
+
"test-results",
|
|
77
|
+
"tests"
|
|
78
|
+
]);
|
|
79
|
+
async function walkProjectSources(root) {
|
|
80
|
+
const inherited = [];
|
|
81
|
+
let ancestor = root;
|
|
82
|
+
while (!(0, import_node_fs.existsSync)((0, import_node_path.join)(ancestor, ".git"))) {
|
|
83
|
+
const parent = (0, import_node_path.dirname)(ancestor);
|
|
84
|
+
if (parent === ancestor) break;
|
|
85
|
+
inherited.unshift(parent);
|
|
86
|
+
ancestor = parent;
|
|
87
|
+
}
|
|
88
|
+
const rootScopes = [];
|
|
89
|
+
for (const dir of inherited) {
|
|
90
|
+
const source = await (0, import_promises.readFile)((0, import_node_path.join)(dir, ".gitignore"), "utf8").catch(() => null);
|
|
91
|
+
if (source) rootScopes.push({
|
|
92
|
+
dir,
|
|
93
|
+
matcher: (0, import_ignore.default)().add(source)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const found = [];
|
|
97
|
+
const stack = [{
|
|
98
|
+
dir: root,
|
|
99
|
+
scopes: rootScopes
|
|
100
|
+
}];
|
|
101
|
+
while (stack.length) {
|
|
102
|
+
const { dir, scopes } = stack.pop();
|
|
103
|
+
let entries;
|
|
104
|
+
try {
|
|
105
|
+
entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
|
|
106
|
+
} catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let active = scopes;
|
|
110
|
+
if (entries.some((entry) => entry.isFile() && entry.name === ".gitignore")) {
|
|
111
|
+
const source = await (0, import_promises.readFile)((0, import_node_path.join)(dir, ".gitignore"), "utf8").catch(() => null);
|
|
112
|
+
if (source) active = [...scopes, {
|
|
113
|
+
dir,
|
|
114
|
+
matcher: (0, import_ignore.default)().add(source)
|
|
115
|
+
}];
|
|
116
|
+
}
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
119
|
+
const isDirectory = entry.isDirectory();
|
|
120
|
+
if (isDirectory && speculativeWalkExcludedDirs.has(entry.name)) continue;
|
|
121
|
+
if (!isDirectory && !(entry.isFile() && (0, import_metroResolver.isCompilerSourceFile)(entry.name))) continue;
|
|
122
|
+
if (!isDirectory && (/(?:^|[-.])(?:probe|run|spec|tests?)(?:[-.]|$)/i.test(entry.name) || /\.(?:build|config|workspace)\.[cm]?[jt]sx?$/.test(entry.name))) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const path = (0, import_node_path.join)(dir, entry.name);
|
|
126
|
+
let ignored = false;
|
|
127
|
+
for (const scope of active) {
|
|
128
|
+
const relativePath = (0, import_node_path.relative)(scope.dir, path);
|
|
129
|
+
if (!relativePath || relativePath.startsWith("..")) continue;
|
|
130
|
+
const candidate = relativePath.split(import_node_path.sep).join("/") + (isDirectory ? "/" : "");
|
|
131
|
+
if (scope.matcher.ignores(candidate)) {
|
|
132
|
+
ignored = true;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (ignored) continue;
|
|
137
|
+
if (isDirectory) stack.push({
|
|
138
|
+
dir: path,
|
|
139
|
+
scopes: active
|
|
140
|
+
});
|
|
141
|
+
else found.push(path);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return found.sort(compareCodeUnits);
|
|
145
|
+
}
|
|
146
|
+
function compilerTarget(platform) {
|
|
147
|
+
return platform === "web" ? "web" : "native";
|
|
148
|
+
}
|
|
149
|
+
function retainsLiveGraph(options) {
|
|
150
|
+
return options.dev && options.hot;
|
|
151
|
+
}
|
|
152
|
+
var MetroCompilerFrontend = class {
|
|
153
|
+
constructor(config) {
|
|
154
|
+
this.config = config;
|
|
155
|
+
this.#cacheBaseRoot = config.cacheRoot ?? (0, import_compilerCache.defaultMetroCompilerCacheRoot)(config.projectRoot);
|
|
156
|
+
this.#resolver = (0, import_metroResolver.createMetroCompilerResolver)(config);
|
|
157
|
+
}
|
|
158
|
+
config;
|
|
159
|
+
#cacheBaseRoot;
|
|
160
|
+
#entries = /* @__PURE__ */ new Map();
|
|
161
|
+
#records = /* @__PURE__ */ new Map();
|
|
162
|
+
#watchers = /* @__PURE__ */ new Map();
|
|
163
|
+
#resolver;
|
|
164
|
+
#graph = null;
|
|
165
|
+
#host = null;
|
|
166
|
+
#projectGeneration = null;
|
|
167
|
+
#publishedGeneration = null;
|
|
168
|
+
#scanOptions = null;
|
|
169
|
+
#scanOptionsHash = null;
|
|
170
|
+
#operationQueue = Promise.resolve();
|
|
171
|
+
#tamaguiConfig = null;
|
|
172
|
+
#zeroEntryGraph = null;
|
|
173
|
+
#planKeys = /* @__PURE__ */ new Map();
|
|
174
|
+
#recordCache = null;
|
|
175
|
+
#recordCacheIdentity = null;
|
|
176
|
+
#planCache = null;
|
|
177
|
+
#planCacheStamp = null;
|
|
178
|
+
get metroResolverVersion() {
|
|
179
|
+
return this.#resolver.version;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Per-file cache accounting for the last scan. The point of these caches is
|
|
183
|
+
* that one edited module leaves every other module's entry valid, and this is
|
|
184
|
+
* how that is observed rather than assumed.
|
|
185
|
+
*/
|
|
186
|
+
get compileCacheStats() {
|
|
187
|
+
const empty = {
|
|
188
|
+
hits: 0,
|
|
189
|
+
misses: 0,
|
|
190
|
+
writes: 0
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
plans: this.#planCache?.stats ?? empty,
|
|
194
|
+
records: this.#recordCache?.stats ?? empty
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
cacheRootFor(platform) {
|
|
198
|
+
return (0, import_node_path.join)(this.#cacheBaseRoot, platform ?? "default");
|
|
199
|
+
}
|
|
200
|
+
scan(options) {
|
|
201
|
+
return this.#enqueue(() => this.#scan(options));
|
|
202
|
+
}
|
|
203
|
+
async #scan(options, preparedProject, preparedProjectSources) {
|
|
204
|
+
this.#scanOptions = options;
|
|
205
|
+
this.#publishedGeneration = null;
|
|
206
|
+
const diagnostics = [];
|
|
207
|
+
const entryRoots = (await Promise.all(options.entryFiles.map((path) => (0, import_promises.realpath)((0, import_node_path.resolve)(this.config.projectRoot, path))))).sort(compareCodeUnits);
|
|
208
|
+
const compilerProject = preparedProject ?? await this.#loadCompilerProject(options, entryRoots[0], diagnostics);
|
|
209
|
+
this.#projectGeneration = compilerProject.generation;
|
|
210
|
+
const projectSources = preparedProjectSources ?? await walkProjectSources(this.config.projectRoot);
|
|
211
|
+
const projectSourcesHash = (0, import_compiler_core.contentHash)(JSON.stringify(projectSources));
|
|
212
|
+
this.#scanOptionsHash = scanOptionsHash(options, compilerProject.generation, projectSourcesHash);
|
|
213
|
+
this.#installCaches(options, compilerProject, projectSourcesHash);
|
|
214
|
+
const speculativeRoots = /* @__PURE__ */ new Set();
|
|
215
|
+
for (const file of projectSources) {
|
|
216
|
+
try {
|
|
217
|
+
const id = await (0, import_promises.realpath)(file);
|
|
218
|
+
if (!entryRoots.includes(id)) speculativeRoots.add(id);
|
|
219
|
+
} catch {}
|
|
220
|
+
}
|
|
221
|
+
const roots = [.../* @__PURE__ */ new Set([...entryRoots, ...speculativeRoots])].sort(compareCodeUnits);
|
|
222
|
+
const queue = [...roots];
|
|
223
|
+
const queued = new Set(queue);
|
|
224
|
+
for (const watcher of this.#watchers.values()) watcher.close();
|
|
225
|
+
this.#watchers.clear();
|
|
226
|
+
this.#records.clear();
|
|
227
|
+
while (queue.length) {
|
|
228
|
+
const path = queue.shift();
|
|
229
|
+
try {
|
|
230
|
+
const record = await this.#compileRecord(path, options, diagnostics);
|
|
231
|
+
this.#records.set(record.input.id, record);
|
|
232
|
+
for (const dependency of record.input.imports) {
|
|
233
|
+
if (dependency.external || !(0, import_metroResolver.isCompilerSourceFile)(dependency.resolvedId) || queued.has(dependency.resolvedId)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
queued.add(dependency.resolvedId);
|
|
237
|
+
queue.push(dependency.resolvedId);
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (speculativeRoots.has(path)) continue;
|
|
241
|
+
const diagnostic = (0, import_diagnostics.metroDiagnostic)("metro/transform-failed", `Failed to compile ${path}: ${error instanceof Error ? error.message : String(error)}`, { moduleId: path });
|
|
242
|
+
diagnostics.push(diagnostic);
|
|
243
|
+
this.#report(diagnostic);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!compilerProject.projectInfo.tamaguiConfig || !compilerProject.projectInfo.components) {
|
|
247
|
+
throw new Error("Metro compiler project has no Tamagui config or components");
|
|
248
|
+
}
|
|
249
|
+
this.#tamaguiConfig = compilerProject.projectInfo.tamaguiConfig;
|
|
250
|
+
this.#entries.clear();
|
|
251
|
+
const unplanned = await this.#restorePlans(options);
|
|
252
|
+
const zero = this.config.zero;
|
|
253
|
+
this.#graph = null;
|
|
254
|
+
this.#host = null;
|
|
255
|
+
if (unplanned.length || retainsLiveGraph(options)) {
|
|
256
|
+
this.#graph = new import_compiler_core.ProjectGraph(import_compiler_core.yukuFactory, { modules: [...this.#records.values()].map(({ input }) => input) });
|
|
257
|
+
this.#host = (0, import_static.createTamaguiCompilerHost)({
|
|
258
|
+
target: compilerTarget(options.platform),
|
|
259
|
+
tamaguiConfig: compilerProject.projectInfo.tamaguiConfig,
|
|
260
|
+
components: compilerProject.projectInfo.components,
|
|
261
|
+
componentModules: compilerProject.componentModules.map(({ moduleName, id }) => ({
|
|
262
|
+
moduleName,
|
|
263
|
+
resolvedId: id
|
|
264
|
+
})),
|
|
265
|
+
disablePartialExtraction: compilerProject.disablePartialExtraction,
|
|
266
|
+
experimentalNativeFastPath: compilerProject.experimentalNativeFastPath,
|
|
267
|
+
zeroRuntime: compilerProject.zeroRuntime
|
|
268
|
+
});
|
|
269
|
+
if (zero) {
|
|
270
|
+
if (zero.isEnforcing) {
|
|
271
|
+
import_static.default.assertZeroConfigDrivers(compilerProject.projectInfo.tamaguiConfig);
|
|
272
|
+
}
|
|
273
|
+
zero.plansRestoredFromCache = false;
|
|
274
|
+
zero.configCSS = compilerProject.projectInfo.tamaguiConfig.getCSS?.() ?? "";
|
|
275
|
+
zero.artifact.clearGraphs();
|
|
276
|
+
zero.bridges.clear();
|
|
277
|
+
zero.violations.length = 0;
|
|
278
|
+
zero.transformed.clear();
|
|
279
|
+
zero.erasedExports.clear();
|
|
280
|
+
this.#zeroEntryGraph = this.#reachableFrom(entryRoots.map(import_compiler_core.resolvedModuleId));
|
|
281
|
+
}
|
|
282
|
+
for (const id of unplanned) this.#refreshEntry(id);
|
|
283
|
+
await this.#storePlans(unplanned);
|
|
284
|
+
}
|
|
285
|
+
if (zero) {
|
|
286
|
+
import_static.default.writeZeroViolationReport(zero.resolved.outDir, "metro-zero", {
|
|
287
|
+
integration: "metro-web",
|
|
288
|
+
mode: zero.isEnforcing ? "enforce" : "report",
|
|
289
|
+
violations: zero.violations
|
|
290
|
+
});
|
|
291
|
+
if (zero.isEnforcing && zero.violations.length) {
|
|
292
|
+
throw new Error(import_static.default.formatZeroViolations(zero.violations));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const totalFound = [...this.#entries.values()].reduce((sum, entry) => sum + entry.plan.stats.found, 0);
|
|
296
|
+
if (this.#entries.size > 0 && totalFound === 0) {
|
|
297
|
+
const componentNames = compilerProject.componentModules.map(({ moduleName }) => moduleName);
|
|
298
|
+
const cjsComponentImporters = [...this.#records.values()].filter((record) => record.requireSpecifiers.some((specifier) => componentNames.some((name) => specifier === name || specifier.startsWith(`${name}/`)))).length;
|
|
299
|
+
if (cjsComponentImporters > 0) {
|
|
300
|
+
const diagnostic = (0, import_diagnostics.metroDiagnostic)("metro/no-linked-components", `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.`);
|
|
301
|
+
diagnostics.push(diagnostic);
|
|
302
|
+
this.#report(diagnostic);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const generation = await this.#publish(options.platform);
|
|
306
|
+
const moduleIds = [...this.#records.keys()].sort(compareCodeUnits);
|
|
307
|
+
if (this.config.watch !== false && retainsLiveGraph(options)) {
|
|
308
|
+
this.#installWatchers();
|
|
309
|
+
} else if (!retainsLiveGraph(options)) {
|
|
310
|
+
this.#releaseGraph();
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
generation,
|
|
314
|
+
moduleIds,
|
|
315
|
+
diagnostics
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
ensureValidCache(options) {
|
|
319
|
+
return this.#enqueue(() => this.#ensureValidCache(options));
|
|
320
|
+
}
|
|
321
|
+
async #ensureValidCache(options) {
|
|
322
|
+
const diagnostics = [];
|
|
323
|
+
const firstEntry = options.entryFiles[0];
|
|
324
|
+
const importer = firstEntry ? await (0, import_promises.realpath)((0, import_node_path.resolve)(this.config.projectRoot, firstEntry)) : this.config.projectRoot;
|
|
325
|
+
const compilerProject = await this.#loadCompilerProject(options, importer, diagnostics);
|
|
326
|
+
const cache = new import_compilerCache.MetroCompilerCache(this.cacheRootFor(options.platform));
|
|
327
|
+
const validation = await cache.validate();
|
|
328
|
+
const projectSources = await walkProjectSources(this.config.projectRoot);
|
|
329
|
+
const optionsHash = scanOptionsHash(options, compilerProject.generation, (0, import_compiler_core.contentHash)(JSON.stringify(projectSources)));
|
|
330
|
+
if (validation.valid && validation.generation && validation.optionsHash === optionsHash && await this.#sourcesAreFresh(validation.sourceHashes) && (!retainsLiveGraph(options) && !this.#graph || this.#publishedGeneration && this.#scanOptionsHash === optionsHash) && await this.#rehydrateZeroCSS(cache, validation.generation)) {
|
|
331
|
+
this.#publishedGeneration = validation.generation;
|
|
332
|
+
this.#scanOptions = options;
|
|
333
|
+
this.#scanOptionsHash = optionsHash;
|
|
334
|
+
this.#projectGeneration = compilerProject.generation;
|
|
335
|
+
return {
|
|
336
|
+
generation: validation.generation,
|
|
337
|
+
moduleIds: validation.moduleIds,
|
|
338
|
+
diagnostics
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
for (const diagnostic of validation.diagnostics) this.#report(diagnostic);
|
|
342
|
+
await cache.discardManifest();
|
|
343
|
+
return await this.#scan(options, compilerProject, projectSources);
|
|
344
|
+
}
|
|
345
|
+
async updateFile(path) {
|
|
346
|
+
let result = {
|
|
347
|
+
changed: false,
|
|
348
|
+
affectedIds: [],
|
|
349
|
+
generation: null
|
|
350
|
+
};
|
|
351
|
+
return this.#enqueue(async () => {
|
|
352
|
+
const graph = this.#graph;
|
|
353
|
+
const options = this.#scanOptions;
|
|
354
|
+
if (!graph || !options) return result;
|
|
355
|
+
let record;
|
|
356
|
+
const diagnostics = [];
|
|
357
|
+
try {
|
|
358
|
+
record = await this.#compileRecord(path, options, diagnostics);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (error.code === "ENOENT") {
|
|
361
|
+
const id = (0, import_compiler_core.resolvedModuleId)((0, import_node_path.resolve)(path));
|
|
362
|
+
const invalidation2 = graph.removeModule(id);
|
|
363
|
+
this.#watchers.get(id)?.close();
|
|
364
|
+
this.#watchers.delete(id);
|
|
365
|
+
this.#records.delete(id);
|
|
366
|
+
this.#entries.delete(id);
|
|
367
|
+
for (const affected of invalidation2.invalidatedIds) {
|
|
368
|
+
if (affected !== id) this.#refreshEntry(affected);
|
|
369
|
+
}
|
|
370
|
+
const generation2 = await this.#publish(options.platform);
|
|
371
|
+
result = {
|
|
372
|
+
changed: invalidation2.changed,
|
|
373
|
+
affectedIds: invalidation2.invalidatedIds,
|
|
374
|
+
generation: generation2
|
|
375
|
+
};
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
const diagnostic = (0, import_diagnostics.metroDiagnostic)("metro/transform-failed", `Failed to update ${path}: ${error instanceof Error ? error.message : String(error)}`, { moduleId: path });
|
|
379
|
+
this.#report(diagnostic);
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
for (const dependency of record.input.imports) {
|
|
383
|
+
if (dependency.external || !(0, import_metroResolver.isCompilerSourceFile)(dependency.resolvedId) || this.#records.has(dependency.resolvedId)) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
await this.#addDependency(dependency.resolvedId, options, diagnostics);
|
|
387
|
+
}
|
|
388
|
+
this.#records.set(record.input.id, record);
|
|
389
|
+
const invalidation = graph.updateModule(record.input);
|
|
390
|
+
for (const affected of invalidation.invalidatedIds) this.#refreshEntry(affected);
|
|
391
|
+
const generation = invalidation.changed ? await this.#publish(options.platform) : null;
|
|
392
|
+
result = {
|
|
393
|
+
changed: invalidation.changed,
|
|
394
|
+
affectedIds: invalidation.invalidatedIds,
|
|
395
|
+
generation
|
|
396
|
+
};
|
|
397
|
+
if (this.config.watch !== false && retainsLiveGraph(options)) {
|
|
398
|
+
this.#watchModule(record.input.id);
|
|
399
|
+
}
|
|
400
|
+
return result;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/** A published plan only applies while every recorded module source is unchanged. */
|
|
404
|
+
async #sourcesAreFresh(sourceHashes) {
|
|
405
|
+
const checks = Object.entries(sourceHashes).map(async ([moduleId, sourceHash]) => {
|
|
406
|
+
try {
|
|
407
|
+
return (0, import_compiler_core.contentHash)(await (0, import_promises.readFile)(moduleId, "utf8")) === sourceHash;
|
|
408
|
+
} catch {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
return (await Promise.all(checks)).every(Boolean);
|
|
413
|
+
}
|
|
414
|
+
#enqueue(operation) {
|
|
415
|
+
const queued = this.#operationQueue.then(operation);
|
|
416
|
+
this.#operationQueue = queued.then(() => void 0, () => void 0);
|
|
417
|
+
return queued;
|
|
418
|
+
}
|
|
419
|
+
close() {
|
|
420
|
+
return this.#enqueue(async () => {
|
|
421
|
+
this.#releaseGraph();
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
#releaseGraph() {
|
|
425
|
+
for (const watcher of this.#watchers.values()) watcher.close();
|
|
426
|
+
this.#watchers.clear();
|
|
427
|
+
this.#entries.clear();
|
|
428
|
+
this.#records.clear();
|
|
429
|
+
this.#planKeys.clear();
|
|
430
|
+
this.#graph = null;
|
|
431
|
+
this.#host = null;
|
|
432
|
+
this.#projectGeneration = null;
|
|
433
|
+
}
|
|
434
|
+
async #loadCompilerProject(options, importer, diagnostics) {
|
|
435
|
+
const target = compilerTarget(options.platform);
|
|
436
|
+
if (this.config.loadCompilerProject) {
|
|
437
|
+
return await this.config.loadCompilerProject(target, options.platform);
|
|
438
|
+
}
|
|
439
|
+
return import_static.default.loadCompilerProject({
|
|
440
|
+
root: this.config.projectRoot,
|
|
441
|
+
target,
|
|
442
|
+
options: this.config.tamaguiOptions ?? {},
|
|
443
|
+
hostVersions: compilerImplementationVersions,
|
|
444
|
+
missingProjectMessage: "Unable to load the Tamagui project for Metro compilation",
|
|
445
|
+
generation: (projectInfo, componentModules, normalizedOptions) => {
|
|
446
|
+
return (0, import_compiler_core.contentHash)(JSON.stringify({
|
|
447
|
+
cacheVersion: import_compilerCache.METRO_COMPILER_CACHE_VERSION,
|
|
448
|
+
compilerImplementationVersions,
|
|
449
|
+
componentModules,
|
|
450
|
+
configCss: projectInfo.tamaguiConfig?.getCSS?.() ?? "",
|
|
451
|
+
disablePartialExtraction: !!normalizedOptions.disablePartialExtraction,
|
|
452
|
+
experimentalNativeFastPath: target === "native" && normalizedOptions.experimental?.nativeFastPath === true,
|
|
453
|
+
target,
|
|
454
|
+
zeroRuntime: !!this.config.zero
|
|
455
|
+
}));
|
|
456
|
+
},
|
|
457
|
+
resolveComponents: async (moduleNames) => {
|
|
458
|
+
const componentModules = [];
|
|
459
|
+
for (const moduleName of moduleNames) {
|
|
460
|
+
try {
|
|
461
|
+
const resolution = this.#resolver.resolve(importer, {
|
|
462
|
+
specifier: moduleName,
|
|
463
|
+
isESMImport: true
|
|
464
|
+
}, options.platform);
|
|
465
|
+
if (!resolution) continue;
|
|
466
|
+
componentModules.push({
|
|
467
|
+
moduleName,
|
|
468
|
+
id: resolution.resolvedId
|
|
469
|
+
});
|
|
470
|
+
} catch (error) {
|
|
471
|
+
const diagnostic = (0, import_diagnostics.metroDiagnostic)("metro/resolve-failed", `Failed to resolve compiler component ${moduleName}: ${error instanceof Error ? error.message : String(error)}`, {
|
|
472
|
+
moduleId: importer,
|
|
473
|
+
dependency: moduleName
|
|
474
|
+
});
|
|
475
|
+
diagnostics.push(diagnostic);
|
|
476
|
+
this.#report(diagnostic);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return componentModules;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
async #compileRecord(rawPath, options, diagnostics) {
|
|
484
|
+
const path = await (0, import_promises.realpath)((0, import_node_path.resolve)(rawPath));
|
|
485
|
+
const source = await (0, import_promises.readFile)(path, "utf8");
|
|
486
|
+
const sourceHash = (0, import_compiler_core.contentHash)(source);
|
|
487
|
+
const id = (0, import_compiler_core.resolvedModuleId)(path);
|
|
488
|
+
const cache = this.#recordCache;
|
|
489
|
+
const identity = this.#recordCacheIdentity;
|
|
490
|
+
const key = cache && identity ? (0, import_compiler_core.contentHash)(`${identity}\0${sourceHash}`) : null;
|
|
491
|
+
if (cache && key) {
|
|
492
|
+
const cached = await cache.read(key, (value) => {
|
|
493
|
+
const entry = value;
|
|
494
|
+
return entry?.schemaVersion === METRO_RECORD_CACHE_VERSION && entry.sourceHash === sourceHash && Array.isArray(entry.imports) && Array.isArray(entry.requireSpecifiers) && Array.isArray(entry.diagnostics) ? entry : null;
|
|
495
|
+
});
|
|
496
|
+
if (cached) {
|
|
497
|
+
for (const diagnostic of cached.diagnostics) {
|
|
498
|
+
diagnostics.push(diagnostic);
|
|
499
|
+
this.#report(diagnostic);
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
input: {
|
|
503
|
+
id,
|
|
504
|
+
source,
|
|
505
|
+
imports: cached.imports
|
|
506
|
+
},
|
|
507
|
+
sourceHash,
|
|
508
|
+
requireSpecifiers: cached.requireSpecifiers
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const args = this.#babelArgs(path, source, options);
|
|
513
|
+
const compiled = await (0, import_babel.compileWithUserBabel)(this.config.originalBabelTransformerPath, args);
|
|
514
|
+
const imports = [];
|
|
515
|
+
const requireSpecifiers = [];
|
|
516
|
+
const recordDiagnostics = [];
|
|
517
|
+
for (const dependency of (0, import_metroResolver.moduleSpecifiersFromAst)(compiled.result.ast)) {
|
|
518
|
+
if (!dependency.isESMImport) requireSpecifiers.push(dependency.specifier);
|
|
519
|
+
try {
|
|
520
|
+
const resolution = this.#resolver.resolve(path, dependency, options.platform);
|
|
521
|
+
if (!resolution) continue;
|
|
522
|
+
imports.push({
|
|
523
|
+
specifier: resolution.specifier,
|
|
524
|
+
resolvedId: (0, import_compiler_core.resolvedModuleId)(resolution.resolvedId),
|
|
525
|
+
external: resolution.external
|
|
526
|
+
});
|
|
527
|
+
} catch (error) {
|
|
528
|
+
recordDiagnostics.push((0, import_diagnostics.metroDiagnostic)("metro/resolve-failed", `Failed to resolve ${dependency.specifier} from ${path}: ${error instanceof Error ? error.message : String(error)}`, {
|
|
529
|
+
moduleId: path,
|
|
530
|
+
dependency: dependency.specifier
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
for (const diagnostic of recordDiagnostics) {
|
|
535
|
+
diagnostics.push(diagnostic);
|
|
536
|
+
this.#report(diagnostic);
|
|
537
|
+
}
|
|
538
|
+
if (cache && key) {
|
|
539
|
+
await cache.write(key, {
|
|
540
|
+
schemaVersion: METRO_RECORD_CACHE_VERSION,
|
|
541
|
+
sourceHash,
|
|
542
|
+
imports,
|
|
543
|
+
requireSpecifiers,
|
|
544
|
+
diagnostics: recordDiagnostics
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
input: {
|
|
549
|
+
id,
|
|
550
|
+
source,
|
|
551
|
+
imports
|
|
552
|
+
},
|
|
553
|
+
sourceHash,
|
|
554
|
+
requireSpecifiers
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
#babelOptions(options) {
|
|
558
|
+
const transformer = this.config.transformer ?? {};
|
|
559
|
+
return {
|
|
560
|
+
...options.transform,
|
|
561
|
+
dev: options.dev,
|
|
562
|
+
hot: options.hot,
|
|
563
|
+
platform: options.platform,
|
|
564
|
+
projectRoot: this.config.projectRoot,
|
|
565
|
+
enableBabelRCLookup: transformer.enableBabelRCLookup ?? true,
|
|
566
|
+
enableBabelRuntime: transformer.enableBabelRuntime ?? true,
|
|
567
|
+
hermesParser: transformer.hermesParser ?? false,
|
|
568
|
+
publicPath: transformer.publicPath ?? "/assets"
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
#babelArgs(filename, src, options) {
|
|
572
|
+
return {
|
|
573
|
+
filename,
|
|
574
|
+
src,
|
|
575
|
+
plugins: [],
|
|
576
|
+
options: this.#babelOptions(options)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
async #addDependency(id, options, diagnostics, visiting = /* @__PURE__ */ new Set()) {
|
|
580
|
+
if (this.#records.has(id) || visiting.has(id)) return;
|
|
581
|
+
visiting.add(id);
|
|
582
|
+
try {
|
|
583
|
+
const record = await this.#compileRecord(id, options, diagnostics);
|
|
584
|
+
for (const dependency of record.input.imports) {
|
|
585
|
+
if (!dependency.external && (0, import_metroResolver.isCompilerSourceFile)(dependency.resolvedId)) {
|
|
586
|
+
await this.#addDependency(dependency.resolvedId, options, diagnostics, visiting);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
this.#records.set(id, record);
|
|
590
|
+
const invalidation = this.#graph?.updateModule(record.input);
|
|
591
|
+
for (const affected of invalidation?.invalidatedIds ?? [id]) {
|
|
592
|
+
this.#refreshEntry(affected);
|
|
593
|
+
}
|
|
594
|
+
if (this.config.watch !== false && this.#scanOptions && retainsLiveGraph(this.#scanOptions)) {
|
|
595
|
+
this.#watchModule(id);
|
|
596
|
+
}
|
|
597
|
+
} finally {
|
|
598
|
+
visiting.delete(id);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
#refreshEntry(id) {
|
|
602
|
+
const graph = this.#graph;
|
|
603
|
+
const host = this.#host;
|
|
604
|
+
const record = this.#records.get(id);
|
|
605
|
+
if (!graph || !host || !record || !this.#scanOptions || !this.#projectGeneration) return;
|
|
606
|
+
const target = compilerTarget(this.#scanOptions.platform);
|
|
607
|
+
const plan = (0, import_compiler_core.lowerModule)({
|
|
608
|
+
module: (0, import_compiler_core.materializeModule)(graph, id),
|
|
609
|
+
source: record.input.source,
|
|
610
|
+
target,
|
|
611
|
+
host,
|
|
612
|
+
options: { projectGeneration: this.#projectGeneration }
|
|
613
|
+
});
|
|
614
|
+
const zeroPlan = this.#zeroPlanFor(id, record.input.source, plan);
|
|
615
|
+
this.#entries.set(id, this.#entryFor(id, record, zeroPlan ?? plan));
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* One plan becomes one cache entry the same way whether the plan was just
|
|
619
|
+
* lowered or read back off disk, so a restored build reports exactly the
|
|
620
|
+
* diagnostics a fresh one did.
|
|
621
|
+
*/
|
|
622
|
+
#entryFor(id, record, plan) {
|
|
623
|
+
return {
|
|
624
|
+
schemaVersion: import_compilerCache.METRO_COMPILER_CACHE_VERSION,
|
|
625
|
+
moduleId: id,
|
|
626
|
+
sourceHash: record.sourceHash,
|
|
627
|
+
plan,
|
|
628
|
+
diagnostics: plan.diagnostics.map(({ code, message, dependencyId, span, component }) => {
|
|
629
|
+
const { line, column } = import_static.default.offsetToLineColumn(record.input.source, span.start);
|
|
630
|
+
return (0, import_diagnostics.metroDiagnostic)(code.startsWith("linked/") ? "metro/resolve-failed" : "metro/transform-failed", message, {
|
|
631
|
+
moduleId: id,
|
|
632
|
+
dependency: dependencyId,
|
|
633
|
+
span,
|
|
634
|
+
line,
|
|
635
|
+
column,
|
|
636
|
+
component
|
|
637
|
+
});
|
|
638
|
+
})
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Both per-file caches for this scan. A project with no content stamp gets
|
|
643
|
+
* neither: a stamp that cannot see a config change would serve styles built
|
|
644
|
+
* against the old config, so the answer is no cache rather than a partial one.
|
|
645
|
+
*
|
|
646
|
+
* Zero builds opt out of the plan cache because a zero plan is produced
|
|
647
|
+
* alongside side effects that do not travel in the plan - the CSS artifact,
|
|
648
|
+
* the bridge manifest, the violation list - so replaying one module's plan
|
|
649
|
+
* without them would emit an artifact missing its rules.
|
|
650
|
+
*/
|
|
651
|
+
#installCaches(options, project, projectSourcesHash) {
|
|
652
|
+
const platform = options.platform ?? "default";
|
|
653
|
+
const root = (0, import_compiler_core.defaultPlanCacheRoot)(this.config.projectRoot, platform);
|
|
654
|
+
this.#recordCache = new import_compiler_core.JsonFileCache((0, import_node_path.join)(root, "records"), METRO_RECORD_CACHE_VERSION);
|
|
655
|
+
this.#recordCacheIdentity = (0, import_compiler_core.contentHash)((0, import_compiler_core.stableStringify)({
|
|
656
|
+
schema: METRO_RECORD_CACHE_VERSION,
|
|
657
|
+
resolver: this.#resolver.version,
|
|
658
|
+
babel: (0, import_babel.userBabelCacheKey)(this.config.originalBabelTransformerPath),
|
|
659
|
+
projectSourcesHash,
|
|
660
|
+
platform,
|
|
661
|
+
transform: this.#babelOptions(options)
|
|
662
|
+
}));
|
|
663
|
+
const stamp = project.cacheStamp;
|
|
664
|
+
const usePlanCache = typeof stamp === "string" && stamp !== "" && !this.config.zero;
|
|
665
|
+
this.#planCache = usePlanCache ? new import_compiler_core.ModulePlanCache((0, import_node_path.join)(root, "plans")) : null;
|
|
666
|
+
this.#planCacheStamp = usePlanCache ? stamp : null;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Fills `#entries` from disk for every module whose whole compile input is
|
|
670
|
+
* unchanged, and returns the ids that still have to be compiled. This is the
|
|
671
|
+
* per-file property: one edited module leaves every other module's entry
|
|
672
|
+
* valid, where the plan manifest would have discarded all of them.
|
|
673
|
+
*/
|
|
674
|
+
async #restorePlans(options) {
|
|
675
|
+
this.#planKeys.clear();
|
|
676
|
+
const cache = this.#planCache;
|
|
677
|
+
const stamp = this.#planCacheStamp;
|
|
678
|
+
if (!cache || !stamp) return [...this.#records.keys()].sort(compareCodeUnits);
|
|
679
|
+
const target = compilerTarget(options.platform);
|
|
680
|
+
const identity = {
|
|
681
|
+
stamp,
|
|
682
|
+
target,
|
|
683
|
+
structuralPassHash: `${target}-noop-v1`
|
|
684
|
+
};
|
|
685
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
686
|
+
const lookup = (id) => {
|
|
687
|
+
let node = nodes.get(id);
|
|
688
|
+
if (node === void 0) {
|
|
689
|
+
const record = this.#records.get(id);
|
|
690
|
+
node = record ? (0, import_compiler_core.moduleClosureNode)(record.input) : null;
|
|
691
|
+
nodes.set(id, node);
|
|
692
|
+
}
|
|
693
|
+
return node;
|
|
694
|
+
};
|
|
695
|
+
const memo = /* @__PURE__ */ new Map();
|
|
696
|
+
const unplanned = [];
|
|
697
|
+
for (const id of [...this.#records.keys()].sort(compareCodeUnits)) {
|
|
698
|
+
const record = this.#records.get(id);
|
|
699
|
+
const digest = (0, import_compiler_core.moduleClosureDigest)(id, lookup, memo);
|
|
700
|
+
const key = digest && (0, import_compiler_core.planCacheKey)(identity, id, digest);
|
|
701
|
+
const entry = key && digest ? await cache.read(key, id, digest) : null;
|
|
702
|
+
if (entry) {
|
|
703
|
+
this.#entries.set(id, this.#entryFor(id, record, entry.plan));
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (key && digest) this.#planKeys.set(id, {
|
|
707
|
+
key,
|
|
708
|
+
digest
|
|
709
|
+
});
|
|
710
|
+
unplanned.push(id);
|
|
711
|
+
}
|
|
712
|
+
return unplanned;
|
|
713
|
+
}
|
|
714
|
+
async #storePlans(ids) {
|
|
715
|
+
const cache = this.#planCache;
|
|
716
|
+
if (!cache) return;
|
|
717
|
+
const pending = ids.flatMap((id) => {
|
|
718
|
+
const entry = this.#entries.get(id);
|
|
719
|
+
const key = this.#planKeys.get(id);
|
|
720
|
+
return entry && key ? [{
|
|
721
|
+
id,
|
|
722
|
+
entry,
|
|
723
|
+
key
|
|
724
|
+
}] : [];
|
|
725
|
+
});
|
|
726
|
+
for (let index = 0; index < pending.length; index += 32) {
|
|
727
|
+
await Promise.all(pending.slice(index, index + 32).map(({ id, entry, key }) => cache.write(key.key, {
|
|
728
|
+
schemaVersion: import_compiler_core.PLAN_CACHE_SCHEMA_VERSION,
|
|
729
|
+
moduleId: id,
|
|
730
|
+
closureDigest: key.digest,
|
|
731
|
+
plan: entry.plan
|
|
732
|
+
})));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/** Modules reachable from the bundle's entry, over the frontend's own graph. */
|
|
736
|
+
#reachableFrom(roots) {
|
|
737
|
+
const reached = /* @__PURE__ */ new Set();
|
|
738
|
+
const queue = [...roots];
|
|
739
|
+
while (queue.length) {
|
|
740
|
+
const id = queue.pop();
|
|
741
|
+
if (reached.has(id)) continue;
|
|
742
|
+
reached.add(id);
|
|
743
|
+
for (const dependency of this.#records.get(id)?.input.imports ?? []) {
|
|
744
|
+
if (!dependency.external) queue.push(dependency.resolvedId);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return reached;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* The zero transform for one module, returning a plan whose edits also carry
|
|
751
|
+
* the static Theme lowering, the island bridge, and reference erasure.
|
|
752
|
+
*/
|
|
753
|
+
#zeroPlanFor(id, source, plan) {
|
|
754
|
+
const zero = this.config.zero;
|
|
755
|
+
const config = this.#tamaguiConfig;
|
|
756
|
+
if (!zero || !config) return null;
|
|
757
|
+
if (zero.islandBuild) {
|
|
758
|
+
zero.artifact.setIslandModuleCSS(zero.islandBuild, id, plan.css);
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
if (this.#zeroEntryGraph && !this.#zeroEntryGraph.has(id)) return null;
|
|
762
|
+
const relativePath = (0, import_node_path.relative)(this.config.projectRoot, id);
|
|
763
|
+
if (relativePath === "" || relativePath.startsWith("..") || relativePath.split(/[\\/]/).includes("node_modules")) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
const result = import_static.default.transformZeroModule({
|
|
767
|
+
mode: zero.isEnforcing ? "enforce" : "report",
|
|
768
|
+
id,
|
|
769
|
+
root: this.config.projectRoot,
|
|
770
|
+
source,
|
|
771
|
+
plan,
|
|
772
|
+
config,
|
|
773
|
+
isTamaguiSpecifier: (specifier) => specifier === "tamagui" || specifier.startsWith("@tamagui/"),
|
|
774
|
+
resolveIslandLoader: (specifier) => {
|
|
775
|
+
const islandId = zero.loaderIds.get((0, import_zeroRuntime.zeroModuleKey)((0, import_node_path.resolve)(id, "..", specifier)));
|
|
776
|
+
return islandId ? { islandId } : null;
|
|
777
|
+
},
|
|
778
|
+
resolveIslandModule: (specifier) => zero.islandModuleIds.get((0, import_zeroRuntime.zeroModuleKey)((0, import_node_path.resolve)(id, "..", specifier))) ?? null
|
|
779
|
+
});
|
|
780
|
+
zero.transformed.add(id);
|
|
781
|
+
if (result.erased.exports.length) {
|
|
782
|
+
zero.erasedExports.set(id, result.erased.exports);
|
|
783
|
+
}
|
|
784
|
+
for (const violation of result.violations) {
|
|
785
|
+
const { line, column } = import_static.default.offsetToLineColumn(source, violation.span.start);
|
|
786
|
+
zero.violations.push({
|
|
787
|
+
file: relativePath,
|
|
788
|
+
line,
|
|
789
|
+
column,
|
|
790
|
+
rule: violation.rule,
|
|
791
|
+
code: violation.code,
|
|
792
|
+
component: violation.component,
|
|
793
|
+
message: violation.message
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
if (result.violations.length || !zero.isEnforcing) return null;
|
|
797
|
+
import_static.default.mergeIslandBridges(zero.bridges, result.bridges);
|
|
798
|
+
for (const [identifier, rules] of result.bridgeCSS) {
|
|
799
|
+
zero.artifact.setBridgeRules(identifier, rules);
|
|
800
|
+
}
|
|
801
|
+
zero.artifact.setZeroModuleCSS(id, plan.css);
|
|
802
|
+
return {
|
|
803
|
+
...plan,
|
|
804
|
+
edits: [...plan.edits, ...result.edits]
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
async #publish(platform) {
|
|
808
|
+
const cache = new import_compilerCache.MetroCompilerCache(this.cacheRootFor(platform));
|
|
809
|
+
const generation = await cache.publish(platform, [...this.#entries.values()], this.#scanOptionsHash ?? "");
|
|
810
|
+
const zero = this.config.zero;
|
|
811
|
+
if (zero && !zero.islandBuild) {
|
|
812
|
+
await cache.publishZeroCSS({
|
|
813
|
+
schemaVersion: import_compilerCache.METRO_COMPILER_CACHE_VERSION,
|
|
814
|
+
generation,
|
|
815
|
+
configCSS: zero.configCSS,
|
|
816
|
+
zeroModuleCSS: Object.fromEntries(zero.artifact.zeroModuleEntries()),
|
|
817
|
+
bridgeCSS: Object.fromEntries(zero.artifact.bridgeEntries()),
|
|
818
|
+
bridges: Object.fromEntries(zero.bridges)
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
this.#publishedGeneration = generation;
|
|
822
|
+
return generation;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Restores the zero build's CSS side effects from the sidecar published with
|
|
826
|
+
* this plan generation. Returns false when there is nothing trustworthy to
|
|
827
|
+
* restore, which sends the caller to a full scan.
|
|
828
|
+
*/
|
|
829
|
+
async #rehydrateZeroCSS(cache, generation) {
|
|
830
|
+
const zero = this.config.zero;
|
|
831
|
+
if (!zero || zero.islandBuild) return true;
|
|
832
|
+
const sidecar = await cache.readZeroCSS(generation);
|
|
833
|
+
if (!sidecar) return false;
|
|
834
|
+
zero.artifact.clearGraphs();
|
|
835
|
+
zero.bridges.clear();
|
|
836
|
+
zero.violations.length = 0;
|
|
837
|
+
zero.configCSS = sidecar.configCSS;
|
|
838
|
+
for (const [moduleId, css] of Object.entries(sidecar.zeroModuleCSS)) {
|
|
839
|
+
zero.artifact.setZeroModuleCSS(moduleId, css);
|
|
840
|
+
}
|
|
841
|
+
for (const [bridgeId, css] of Object.entries(sidecar.bridgeCSS)) {
|
|
842
|
+
zero.artifact.setBridgeRules(bridgeId, css);
|
|
843
|
+
}
|
|
844
|
+
for (const [islandId, bridges] of Object.entries(sidecar.bridges)) {
|
|
845
|
+
zero.bridges.set(islandId, bridges);
|
|
846
|
+
}
|
|
847
|
+
zero.plansRestoredFromCache = true;
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
#installWatchers() {
|
|
851
|
+
for (const id of this.#records.keys()) this.#watchModule(id);
|
|
852
|
+
}
|
|
853
|
+
#watchModule(id) {
|
|
854
|
+
if (this.#watchers.has(id)) return;
|
|
855
|
+
try {
|
|
856
|
+
const watcher = (0, import_node_fs.watch)(id, { persistent: false }, () => {
|
|
857
|
+
void this.updateFile(id);
|
|
858
|
+
});
|
|
859
|
+
watcher.unref();
|
|
860
|
+
this.#watchers.set(id, watcher);
|
|
861
|
+
} catch {}
|
|
862
|
+
}
|
|
863
|
+
#report(diagnostic) {
|
|
864
|
+
this.config.reportDiagnostic?.(diagnostic);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
function describeMetroCompilerRoot(projectRoot, moduleId) {
|
|
868
|
+
const path = (0, import_node_path.relative)(projectRoot, moduleId);
|
|
869
|
+
return path.startsWith("..") ? (0, import_node_path.basename)(moduleId) : path;
|
|
870
|
+
}
|