@ecrindigital/facetpack 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,15 +6,277 @@ import {
6
6
  analyzeSync,
7
7
  shakeSync
8
8
  } from "@ecrindigital/facetpack-native";
9
+
10
+ // src/stats.ts
11
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from "fs";
12
+ import { tmpdir } from "os";
13
+ import { join } from "path";
14
+ var ANSI = {
15
+ green: "\x1B[32m",
16
+ yellow: "\x1B[33m",
17
+ cyan: "\x1B[36m",
18
+ white: "\x1B[37m",
19
+ gray: "\x1B[90m",
20
+ bold: "\x1B[1m",
21
+ dim: "\x1B[2m",
22
+ reset: "\x1B[0m"
23
+ };
24
+ var STATS_DIR = join(tmpdir(), "facetpack-stats");
25
+ var STATS_FILE_PREFIX = "stats-";
26
+ var LOCK_FILE = join(STATS_DIR, ".print-lock");
27
+ var PRINT_DELAY = 1000;
28
+ function getStatsFilePath() {
29
+ const workerId = process.env.METRO_WORKER_ID || process.pid.toString();
30
+ return join(STATS_DIR, `${STATS_FILE_PREFIX}${workerId}.json`);
31
+ }
32
+ function ensureStatsDir() {
33
+ if (!existsSync(STATS_DIR)) {
34
+ mkdirSync(STATS_DIR, { recursive: true });
35
+ }
36
+ }
37
+ function acquirePrintLock() {
38
+ try {
39
+ ensureStatsDir();
40
+ if (existsSync(LOCK_FILE)) {
41
+ const lockTime = parseInt(readFileSync(LOCK_FILE, "utf-8"), 10);
42
+ if (Date.now() - lockTime < 5000) {
43
+ return false;
44
+ }
45
+ }
46
+ writeFileSync(LOCK_FILE, Date.now().toString());
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+ function releasePrintLock() {
53
+ try {
54
+ if (existsSync(LOCK_FILE)) {
55
+ unlinkSync(LOCK_FILE);
56
+ }
57
+ } catch {}
58
+ }
59
+
60
+ class GlobalStats {
61
+ stats = this.createEmptyStats();
62
+ exitHandlerRegistered = false;
63
+ hasPrinted = false;
64
+ printTimer = null;
65
+ createEmptyStats() {
66
+ return {
67
+ transformer: { oxc: 0, babel: 0 },
68
+ resolver: { facetpack: 0, metro: 0 },
69
+ minifier: { files: 0, originalSize: 0, minifiedSize: 0 },
70
+ treeShaking: { modulesAnalyzed: 0, modulesRemoved: 0, exportsRemoved: 0 },
71
+ startTime: Date.now()
72
+ };
73
+ }
74
+ recordTransform(engine) {
75
+ this.stats.transformer[engine]++;
76
+ this.persistStats();
77
+ this.schedulePrint();
78
+ }
79
+ adjustTransformFallback() {
80
+ this.stats.transformer.oxc--;
81
+ this.stats.transformer.babel++;
82
+ this.persistStats();
83
+ }
84
+ recordResolve(engine) {
85
+ this.stats.resolver[engine]++;
86
+ }
87
+ flush() {
88
+ this.persistStats();
89
+ }
90
+ schedulePrint() {
91
+ if (this.printTimer) {
92
+ clearTimeout(this.printTimer);
93
+ }
94
+ this.printTimer = setTimeout(() => {
95
+ if (acquirePrintLock()) {
96
+ this.print();
97
+ releasePrintLock();
98
+ }
99
+ }, PRINT_DELAY);
100
+ }
101
+ recordMinify(originalSize, minifiedSize) {
102
+ this.stats.minifier.files++;
103
+ this.stats.minifier.originalSize += originalSize;
104
+ this.stats.minifier.minifiedSize += minifiedSize;
105
+ }
106
+ recordTreeShaking(analyzed, removed, exports) {
107
+ this.stats.treeShaking.modulesAnalyzed += analyzed;
108
+ this.stats.treeShaking.modulesRemoved += removed;
109
+ this.stats.treeShaking.exportsRemoved += exports;
110
+ }
111
+ get() {
112
+ return JSON.parse(JSON.stringify(this.stats));
113
+ }
114
+ reset() {
115
+ this.stats = this.createEmptyStats();
116
+ this.hasPrinted = false;
117
+ this.cleanupStatsFiles();
118
+ }
119
+ persistStats() {
120
+ try {
121
+ ensureStatsDir();
122
+ writeFileSync(getStatsFilePath(), JSON.stringify(this.stats));
123
+ } catch {}
124
+ }
125
+ cleanupStatsFiles() {
126
+ try {
127
+ if (existsSync(STATS_DIR)) {
128
+ const { readdirSync } = __require("fs");
129
+ const files = readdirSync(STATS_DIR);
130
+ for (const file of files) {
131
+ if (file.startsWith(STATS_FILE_PREFIX)) {
132
+ try {
133
+ unlinkSync(join(STATS_DIR, file));
134
+ } catch {}
135
+ }
136
+ }
137
+ }
138
+ } catch {}
139
+ }
140
+ aggregateWorkerStats() {
141
+ const aggregated = this.createEmptyStats();
142
+ aggregated.startTime = this.stats.startTime;
143
+ try {
144
+ if (existsSync(STATS_DIR)) {
145
+ const { readdirSync } = __require("fs");
146
+ const files = readdirSync(STATS_DIR);
147
+ for (const file of files) {
148
+ if (file.startsWith(STATS_FILE_PREFIX)) {
149
+ try {
150
+ const content = readFileSync(join(STATS_DIR, file), "utf-8");
151
+ const workerStats = JSON.parse(content);
152
+ aggregated.transformer.oxc += workerStats.transformer.oxc;
153
+ aggregated.transformer.babel += workerStats.transformer.babel;
154
+ aggregated.resolver.facetpack += workerStats.resolver.facetpack;
155
+ aggregated.resolver.metro += workerStats.resolver.metro;
156
+ if (workerStats.startTime < aggregated.startTime) {
157
+ aggregated.startTime = workerStats.startTime;
158
+ }
159
+ } catch {}
160
+ }
161
+ }
162
+ }
163
+ } catch {}
164
+ aggregated.minifier = this.stats.minifier;
165
+ aggregated.treeShaking = this.stats.treeShaking;
166
+ return aggregated;
167
+ }
168
+ formatPercent(value, total) {
169
+ if (total === 0)
170
+ return "0.0";
171
+ return (value / total * 100).toFixed(1);
172
+ }
173
+ formatSize(bytes) {
174
+ if (bytes < 1024)
175
+ return `${bytes} B`;
176
+ if (bytes < 1024 * 1024)
177
+ return `${(bytes / 1024).toFixed(1)} KB`;
178
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
179
+ }
180
+ formatDuration(ms) {
181
+ if (ms < 1000)
182
+ return `${ms}ms`;
183
+ return `${(ms / 1000).toFixed(2)}s`;
184
+ }
185
+ print() {
186
+ if (!process.env.FACETPACK_DEBUG || this.hasPrinted)
187
+ return;
188
+ const aggregated = this.aggregateWorkerStats();
189
+ const { transformer, resolver, minifier, treeShaking, startTime } = aggregated;
190
+ const duration = Date.now() - startTime;
191
+ const transformTotal = transformer.oxc + transformer.babel;
192
+ const resolveTotal = resolver.facetpack + resolver.metro;
193
+ if (transformTotal === 0 && resolveTotal === 0 && minifier.files === 0)
194
+ return;
195
+ this.hasPrinted = true;
196
+ const { cyan, green, yellow, white, gray, bold, dim, reset } = ANSI;
197
+ console.log(`
198
+ `);
199
+ console.log(`${bold}${cyan}╔════════════════════════════════════════════════════════════════════╗${reset}`);
200
+ console.log(`${bold}${cyan}║${reset} ${bold}FACETPACK BUNDLE STATS${reset} ${cyan}║${reset}`);
201
+ console.log(`${bold}${cyan}╠════════════════════════════════════════════════════════════════════╣${reset}`);
202
+ if (transformTotal > 0) {
203
+ const oxcPct = this.formatPercent(transformer.oxc, transformTotal);
204
+ const babelPct = this.formatPercent(transformer.babel, transformTotal);
205
+ console.log(`${cyan}║${reset} ${cyan}║${reset}`);
206
+ console.log(`${cyan}║${reset} ${bold}TRANSFORMER${reset} ${cyan}║${reset}`);
207
+ console.log(`${cyan}║${reset} ${green}●${reset} OXC (native) ${bold}${green}${transformer.oxc.toString().padStart(6)}${reset} files ${green}${oxcPct.padStart(6)}%${reset} ${cyan}║${reset}`);
208
+ console.log(`${cyan}║${reset} ${yellow}●${reset} Babel ${bold}${yellow}${transformer.babel.toString().padStart(6)}${reset} files ${yellow}${babelPct.padStart(6)}%${reset} ${cyan}║${reset}`);
209
+ console.log(`${cyan}║${reset} ${dim}${white} Total ${transformTotal.toString().padStart(6)} files${reset} ${cyan}║${reset}`);
210
+ }
211
+ if (resolveTotal > 0) {
212
+ const fpPct = this.formatPercent(resolver.facetpack, resolveTotal);
213
+ const metroPct = this.formatPercent(resolver.metro, resolveTotal);
214
+ console.log(`${cyan}║${reset} ${cyan}║${reset}`);
215
+ console.log(`${cyan}║${reset} ${bold}RESOLVER${reset} ${cyan}║${reset}`);
216
+ console.log(`${cyan}║${reset} ${green}●${reset} Facetpack ${bold}${green}${resolver.facetpack.toString().padStart(6)}${reset} hits ${green}${fpPct.padStart(6)}%${reset} ${cyan}║${reset}`);
217
+ console.log(`${cyan}║${reset} ${yellow}●${reset} Metro ${bold}${yellow}${resolver.metro.toString().padStart(6)}${reset} hits ${yellow}${metroPct.padStart(6)}%${reset} ${cyan}║${reset}`);
218
+ console.log(`${cyan}║${reset} ${dim}${white} Total ${resolveTotal.toString().padStart(6)} resolutions${reset} ${cyan}║${reset}`);
219
+ }
220
+ if (minifier.files > 0) {
221
+ const savings = minifier.originalSize - minifier.minifiedSize;
222
+ const savingsPct = this.formatPercent(savings, minifier.originalSize);
223
+ console.log(`${cyan}║${reset} ${cyan}║${reset}`);
224
+ console.log(`${cyan}║${reset} ${bold}MINIFIER${reset} ${cyan}║${reset}`);
225
+ console.log(`${cyan}║${reset} ${green}●${reset} Files minified ${bold}${green}${minifier.files.toString().padStart(6)}${reset} ${cyan}║${reset}`);
226
+ console.log(`${cyan}║${reset} ${gray}●${reset} Original size ${this.formatSize(minifier.originalSize).padStart(12)} ${cyan}║${reset}`);
227
+ console.log(`${cyan}║${reset} ${green}●${reset} Minified size ${this.formatSize(minifier.minifiedSize).padStart(12)} ${green}-${savingsPct}%${reset} ${cyan}║${reset}`);
228
+ }
229
+ if (treeShaking.modulesAnalyzed > 0) {
230
+ console.log(`${cyan}║${reset} ${cyan}║${reset}`);
231
+ console.log(`${cyan}║${reset} ${bold}TREE SHAKING${reset} ${cyan}║${reset}`);
232
+ console.log(`${cyan}║${reset} ${gray}●${reset} Modules analyzed ${bold}${treeShaking.modulesAnalyzed.toString().padStart(5)}${reset} ${cyan}║${reset}`);
233
+ console.log(`${cyan}║${reset} ${green}●${reset} Modules removed ${bold}${green}${treeShaking.modulesRemoved.toString().padStart(5)}${reset} ${cyan}║${reset}`);
234
+ console.log(`${cyan}║${reset} ${green}●${reset} Exports removed ${bold}${green}${treeShaking.exportsRemoved.toString().padStart(5)}${reset} ${cyan}║${reset}`);
235
+ }
236
+ console.log(`${cyan}║${reset} ${cyan}║${reset}`);
237
+ console.log(`${cyan}║${reset} ${dim}Duration: ${this.formatDuration(duration)}${reset} ${cyan}║${reset}`);
238
+ console.log(`${bold}${cyan}╚════════════════════════════════════════════════════════════════════╝${reset}`);
239
+ console.log(`
240
+ `);
241
+ this.cleanupStatsFiles();
242
+ }
243
+ registerExitHandler() {
244
+ if (this.exitHandlerRegistered)
245
+ return;
246
+ this.exitHandlerRegistered = true;
247
+ process.on("SIGINT", () => {
248
+ this.print();
249
+ process.exit(0);
250
+ });
251
+ process.on("beforeExit", () => this.print());
252
+ }
253
+ }
254
+ var globalStats = new GlobalStats;
255
+ function printStats() {
256
+ globalStats.print();
257
+ }
258
+ function resetStats() {
259
+ globalStats.reset();
260
+ }
261
+ function getStats() {
262
+ return globalStats.get();
263
+ }
264
+
265
+ // src/serializer.ts
9
266
  function createFacetpackSerializer(existingSerializer, config = {}) {
10
267
  return async (entryPoint, preModules, graph, options) => {
11
268
  if (options.dev || config.treeShake === false) {
269
+ let result2;
12
270
  if (existingSerializer) {
13
- return existingSerializer(entryPoint, preModules, graph, options);
271
+ result2 = await existingSerializer(entryPoint, preModules, graph, options);
272
+ } else {
273
+ result2 = defaultSerialize(entryPoint, preModules, graph, options);
14
274
  }
15
- return defaultSerialize(entryPoint, preModules, graph, options);
275
+ globalStats.print();
276
+ return result2;
16
277
  }
17
278
  const analyses = new Map;
279
+ let modulesAnalyzed = 0;
18
280
  for (const [path, module] of graph.dependencies) {
19
281
  if (path.includes("node_modules")) {
20
282
  continue;
@@ -23,6 +285,7 @@ function createFacetpackSerializer(existingSerializer, config = {}) {
23
285
  const code = module.output[0]?.data?.code ?? "";
24
286
  const analysis = analyzeSync(path, code);
25
287
  analyses.set(path, analysis);
288
+ modulesAnalyzed++;
26
289
  } catch {
27
290
  analyses.set(path, {
28
291
  exports: [],
@@ -33,7 +296,8 @@ function createFacetpackSerializer(existingSerializer, config = {}) {
33
296
  }
34
297
  const usedExports = computeUsedExports(entryPoint, analyses, graph);
35
298
  const shakenModules = new Map;
36
- let totalRemoved = 0;
299
+ let modulesRemoved = 0;
300
+ let exportsRemoved = 0;
37
301
  for (const [path, module] of graph.dependencies) {
38
302
  if (path.includes("node_modules")) {
39
303
  const code = module.output[0]?.data?.code ?? "";
@@ -43,28 +307,30 @@ function createFacetpackSerializer(existingSerializer, config = {}) {
43
307
  const used = usedExports.get(path);
44
308
  const analysis = analyses.get(path);
45
309
  if ((!used || used.size === 0) && analysis && !analysis.hasSideEffects) {
46
- totalRemoved++;
310
+ modulesRemoved++;
47
311
  continue;
48
312
  }
49
313
  try {
50
314
  const code = module.output[0]?.data?.code ?? "";
51
315
  const usedArray = used ? Array.from(used) : ["*"];
52
- const result = shakeSync(path, code, usedArray);
53
- shakenModules.set(path, { code: result.code, map: result.map ?? undefined });
54
- totalRemoved += result.removedExports.length;
316
+ const result2 = shakeSync(path, code, usedArray);
317
+ shakenModules.set(path, { code: result2.code, map: result2.map ?? undefined });
318
+ exportsRemoved += result2.removedExports.length;
55
319
  } catch {
56
320
  const code = module.output[0]?.data?.code ?? "";
57
321
  shakenModules.set(path, { code });
58
322
  }
59
323
  }
60
- if (totalRemoved > 0) {
61
- console.log(`[facetpack] Tree-shaking removed ${totalRemoved} unused exports`);
62
- }
324
+ globalStats.recordTreeShaking(modulesAnalyzed, modulesRemoved, exportsRemoved);
325
+ let result;
63
326
  if (existingSerializer) {
64
327
  const shakenGraph = createShakenGraph(graph, shakenModules);
65
- return existingSerializer(entryPoint, preModules, shakenGraph, options);
328
+ result = await existingSerializer(entryPoint, preModules, shakenGraph, options);
329
+ } else {
330
+ result = defaultSerialize(entryPoint, preModules, graph, options, shakenModules);
66
331
  }
67
- return defaultSerialize(entryPoint, preModules, graph, options, shakenModules);
332
+ globalStats.print();
333
+ return result;
68
334
  };
69
335
  }
70
336
  function computeUsedExports(entryPoint, analyses, graph) {
@@ -168,7 +434,7 @@ function wrapModule(id, code) {
168
434
  var serializer_default = createFacetpackSerializer;
169
435
 
170
436
  // src/transformer.ts
171
- import { transformSync, JsxRuntime, resolveBatchSync } from "@ecrindigital/facetpack-native";
437
+ import { transformSync, JsxRuntime, resolveBatchSync, parseSync } from "@ecrindigital/facetpack-native";
172
438
  import { parse } from "@babel/parser";
173
439
 
174
440
  // src/cache.ts
@@ -207,8 +473,112 @@ function getCacheStats() {
207
473
  }
208
474
 
209
475
  // src/transformer.ts
476
+ var ANSI2 = {
477
+ green: "\x1B[32m",
478
+ yellow: "\x1B[33m",
479
+ cyan: "\x1B[36m",
480
+ bold: "\x1B[1m",
481
+ reset: "\x1B[0m"
482
+ };
210
483
  var IMPORT_REGEX = /(?:import|export)\s+(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
211
484
  var REQUIRE_REGEX = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
485
+ var BABEL_REQUIRED_PATTERNS = [
486
+ /'worklet'/,
487
+ /"worklet"/,
488
+ /require\.context\s*\(/,
489
+ /useAnimatedStyle/,
490
+ /useAnimatedProps/,
491
+ /useAnimatedScrollHandler/,
492
+ /useAnimatedGestureHandler/,
493
+ /useAnimatedReaction/,
494
+ /useDerivedValue/,
495
+ /useAnimatedSensor/,
496
+ /useFrameCallback/,
497
+ /useScrollViewOffset/,
498
+ /runOnUI/,
499
+ /runOnJS/
500
+ ];
501
+ var DEFAULT_OPTIONS = {
502
+ jsx: true,
503
+ jsxRuntime: "automatic",
504
+ jsxImportSource: "react",
505
+ jsxPragma: "React.createElement",
506
+ jsxPragmaFrag: "React.Fragment",
507
+ typescript: true,
508
+ sourceExts: ["ts", "tsx", "js", "jsx", "mjs", "cjs"],
509
+ minifier: true,
510
+ treeShake: true,
511
+ noAst: false
512
+ };
513
+
514
+ class Logger {
515
+ startupLogged = false;
516
+ logStartup() {
517
+ if (this.startupLogged || !process.env.FACETPACK_DEBUG)
518
+ return;
519
+ this.startupLogged = true;
520
+ console.log(`${ANSI2.cyan}${ANSI2.bold}[Facetpack]${ANSI2.reset} Transformer loaded`);
521
+ }
522
+ logTransform(decision, filename) {
523
+ if (!process.env.FACETPACK_DEBUG)
524
+ return;
525
+ const color = decision === "oxc" ? ANSI2.green : ANSI2.yellow;
526
+ console.log(`${color}[Facetpack]${ANSI2.reset} ${decision.toUpperCase()}: ${filename}`);
527
+ }
528
+ logFallback(filename, error) {
529
+ if (!process.env.FACETPACK_DEBUG)
530
+ return;
531
+ const message = error instanceof Error ? error.message : String(error);
532
+ console.log(`${ANSI2.yellow}[Facetpack]${ANSI2.reset} OXC failed, falling back to Babel: ${filename}`);
533
+ console.log(`${ANSI2.yellow}[Facetpack]${ANSI2.reset} Error: ${message}`);
534
+ }
535
+ }
536
+
537
+ class OptionsManager {
538
+ globalOptions = {};
539
+ setGlobal(options) {
540
+ this.globalOptions = options;
541
+ }
542
+ get() {
543
+ return { ...DEFAULT_OPTIONS, ...this.globalOptions, ...this.getFromEnv() };
544
+ }
545
+ merge(options) {
546
+ return { ...DEFAULT_OPTIONS, ...options };
547
+ }
548
+ getFromEnv() {
549
+ try {
550
+ const json = process.env.FACETPACK_OPTIONS;
551
+ return json ? JSON.parse(json) : {};
552
+ } catch {
553
+ return {};
554
+ }
555
+ }
556
+ }
557
+
558
+ class FallbackTransformerManager {
559
+ instance = null;
560
+ get() {
561
+ if (this.instance)
562
+ return this.instance;
563
+ const envPath = process.env.FACETPACK_FALLBACK_TRANSFORMER;
564
+ if (envPath) {
565
+ try {
566
+ this.instance = __require(envPath);
567
+ return this.instance;
568
+ } catch (e) {
569
+ console.warn(`[Facetpack] Failed to load fallback transformer from ${envPath}:`, e);
570
+ }
571
+ }
572
+ this.instance = {
573
+ transform: ({ src }) => ({ code: src, map: null })
574
+ };
575
+ return this.instance;
576
+ }
577
+ }
578
+ var logger = new Logger;
579
+ var options = new OptionsManager;
580
+ var fallback = new FallbackTransformerManager;
581
+ logger.logStartup();
212
582
  function extractSpecifiers(code) {
213
583
  const specifiers = new Set;
214
584
  let match;
@@ -237,168 +607,126 @@ function preResolveImports(filename, code, sourceExts) {
237
607
  const specifier = specifiers[i];
238
608
  if (specifier) {
239
609
  resolutions.set(specifier, results[i]?.path ?? null);
610
+ if (results[i]?.path) {
611
+ globalStats.recordResolve("facetpack");
612
+ }
240
613
  }
241
614
  }
242
615
  setCachedResolutions(filename, resolutions);
243
616
  }
244
- var defaultOptions = {
245
- jsx: true,
246
- jsxRuntime: "automatic",
247
- jsxImportSource: "react",
248
- jsxPragma: "React.createElement",
249
- jsxPragmaFrag: "React.Fragment",
250
- typescript: true,
251
- sourceExts: ["ts", "tsx", "js", "jsx", "mjs", "cjs"],
252
- minifier: true,
253
- treeShake: true,
254
- noAst: false
255
- };
256
- var globalOptions = {};
257
- var fallbackTransformer = null;
258
- function getFallbackTransformer() {
259
- if (fallbackTransformer) {
260
- return fallbackTransformer;
261
- }
262
- const fallbackPath = process.env.FACETPACK_FALLBACK_TRANSFORMER;
263
- if (fallbackPath) {
264
- try {
265
- fallbackTransformer = __require(fallbackPath);
266
- return fallbackTransformer;
267
- } catch (e) {
268
- console.warn(`[Facetpack] Failed to load fallback transformer from ${fallbackPath}:`, e);
269
- }
270
- }
271
- fallbackTransformer = {
272
- transform: ({ src }) => ({ code: src, map: null })
273
- };
274
- return fallbackTransformer;
275
- }
276
- function setTransformerOptions(options) {
277
- globalOptions = options;
278
- }
279
- function getStoredOptions() {
280
- try {
281
- const optionsJson = process.env.FACETPACK_OPTIONS;
282
- if (optionsJson) {
283
- return JSON.parse(optionsJson);
284
- }
285
- } catch {}
286
- return {};
287
- }
288
- function getOptions() {
289
- const storedOptions = getStoredOptions();
290
- return { ...defaultOptions, ...globalOptions, ...storedOptions };
617
+ function requiresBabelTransform(src) {
618
+ return BABEL_REQUIRED_PATTERNS.some((pattern) => pattern.test(src));
291
619
  }
292
620
  function isNodeModules(filename) {
293
621
  return filename.includes("node_modules");
294
622
  }
295
- var BABEL_REQUIRED_PATTERNS = [
296
- /'worklet'/,
297
- /"worklet"/,
298
- /useAnimatedStyle/,
299
- /useAnimatedProps/,
300
- /useDerivedValue/,
301
- /useAnimatedReaction/,
302
- /useAnimatedScrollHandler/,
303
- /useAnimatedGestureHandler/,
304
- /runOnUI/,
305
- /runOnJS/
306
- ];
307
- function requiresBabelTransform(src) {
308
- return BABEL_REQUIRED_PATTERNS.some((pattern) => pattern.test(src));
623
+ function getFileExtension(filename) {
624
+ return filename.split(".").pop()?.toLowerCase();
309
625
  }
310
- function shouldTransform(filename, src, options) {
311
- if (isNodeModules(filename)) {
312
- return false;
313
- }
314
- if (requiresBabelTransform(src)) {
315
- if (process.env.FACETPACK_DEBUG) {
316
- console.log(`[Facetpack] Babel required for worklets: ${filename}`);
626
+ function getTransformDecision(filename, src, opts) {
627
+ if (requiresBabelTransform(src))
628
+ return "babel";
629
+ if (isNodeModules(filename))
630
+ return "babel";
631
+ const ext = getFileExtension(filename);
632
+ if (!ext || !opts.sourceExts.includes(ext))
633
+ return "babel";
634
+ return "oxc";
635
+ }
636
+ function formatDiagnostics(diagnostics) {
637
+ return diagnostics.map((d) => {
638
+ if (d.formatted)
639
+ return d.formatted;
640
+ if (!d.message)
641
+ return "";
642
+ let output = `
643
+ × ${d.message}
644
+ `;
645
+ if (d.snippet) {
646
+ output += ` ╭─[${d.filename}:${d.line}:${d.column}]
647
+ `;
648
+ output += ` ${d.line} │ ${d.snippet}
649
+ `;
650
+ output += ` ╰────
651
+ `;
317
652
  }
318
- return false;
653
+ if (d.help)
654
+ output += ` help: ${d.help}
655
+ `;
656
+ return output;
657
+ }).join(`
658
+ `);
659
+ }
660
+ function transformWithOxc(filename, src, opts, isDev) {
661
+ const parseResult = parseSync(filename, src);
662
+ if (parseResult.errors.length > 0) {
663
+ const error = parseResult.diagnostics.length > 0 ? new Error(`
664
+ ${formatDiagnostics(parseResult.diagnostics)}`) : new Error(`Parse error in ${filename}:
665
+ ${parseResult.errors.join(`
666
+ `)}`);
667
+ error.isParseError = true;
668
+ throw error;
319
669
  }
320
- const ext = filename.split(".").pop()?.toLowerCase();
321
- if (!ext)
322
- return false;
323
- return options.sourceExts.includes(ext);
670
+ const isClassic = opts.jsxRuntime === "classic";
671
+ const result = transformSync(filename, src, {
672
+ jsx: opts.jsx,
673
+ jsxRuntime: isClassic ? JsxRuntime.Classic : JsxRuntime.Automatic,
674
+ ...isClassic ? { jsxPragma: opts.jsxPragma, jsxPragmaFrag: opts.jsxPragmaFrag } : { jsxImportSource: opts.jsxImportSource },
675
+ typescript: opts.typescript,
676
+ sourcemap: isDev
677
+ });
678
+ if (result.errors.length > 0) {
679
+ throw new Error(`Facetpack transform error in ${filename}:
680
+ ${result.errors.join(`
681
+ `)}`);
682
+ }
683
+ preResolveImports(filename, result.code, opts.sourceExts);
684
+ globalStats.flush();
685
+ const ast = parse(result.code, {
686
+ sourceType: "unambiguous",
687
+ plugins: ["jsx"]
688
+ });
689
+ return {
690
+ ast,
691
+ code: result.code,
692
+ map: result.map ? JSON.parse(result.map) : null
693
+ };
694
+ }
695
+ function setTransformerOptions(opts) {
696
+ options.setGlobal(opts);
324
697
  }
325
698
  function transform(params) {
326
699
  const { filename, src, options: metroOptions } = params;
327
- const opts = getOptions();
328
- if (process.env.FACETPACK_DEBUG) {
329
- console.log(`[Facetpack] Processing: ${filename}`);
330
- }
331
- if (!shouldTransform(filename, src, opts)) {
332
- if (process.env.FACETPACK_DEBUG) {
333
- console.log(`[Facetpack] Fallback: ${filename}`);
334
- }
335
- return getFallbackTransformer().transform(params);
336
- }
337
- if (process.env.FACETPACK_DEBUG) {
338
- console.log(`[Facetpack] OXC Transform: ${filename}`);
700
+ const opts = options.get();
701
+ globalStats.registerExitHandler();
702
+ const decision = getTransformDecision(filename, src, opts);
703
+ globalStats.recordTransform(decision);
704
+ logger.logTransform(decision, filename);
705
+ if (decision === "babel") {
706
+ return fallback.get().transform(params);
339
707
  }
340
708
  try {
341
- const isClassic = opts.jsxRuntime === "classic";
342
- const result = transformSync(filename, src, {
343
- jsx: opts.jsx,
344
- jsxRuntime: isClassic ? JsxRuntime.Classic : JsxRuntime.Automatic,
345
- ...isClassic ? { jsxPragma: opts.jsxPragma, jsxPragmaFrag: opts.jsxPragmaFrag } : { jsxImportSource: opts.jsxImportSource },
346
- typescript: opts.typescript,
347
- sourcemap: metroOptions.dev
348
- });
349
- if (result.errors.length > 0) {
350
- const errorMessage = result.errors.join(`
351
- `);
352
- throw new Error(`Facetpack transform error in ${filename}:
353
- ${errorMessage}`);
354
- }
355
- preResolveImports(filename, result.code, opts.sourceExts);
356
- const ast = opts.noAst ? undefined : parse(result.code, {
357
- sourceType: "unambiguous",
358
- plugins: ["jsx"]
359
- });
360
- const output = {
361
- ast,
362
- code: result.code,
363
- map: result.map ? JSON.parse(result.map) : null
364
- };
365
- if (process.env.FACETPACK_DEBUG) {
366
- console.log(`[Facetpack] Output for ${filename}:`);
367
- console.log(result.code.slice(0, 500));
368
- }
369
- return output;
709
+ return transformWithOxc(filename, src, opts, metroOptions.dev);
370
710
  } catch (error) {
371
- if (error instanceof Error) {
372
- error.message = `[Facetpack] ${error.message}`;
711
+ if (error.isParseError) {
712
+ throw error;
373
713
  }
374
- throw error;
714
+ logger.logFallback(filename, error);
715
+ globalStats.adjustTransformFallback();
716
+ return fallback.get().transform(params);
375
717
  }
376
718
  }
377
- function createTransformer(options = {}) {
378
- const opts = { ...defaultOptions, ...options };
719
+ function createTransformer(customOptions = {}) {
720
+ const opts = options.merge(customOptions);
379
721
  return {
380
722
  transform(params) {
381
723
  const { filename, src, options: metroOptions } = params;
382
- if (!shouldTransform(filename, src, opts)) {
383
- return getFallbackTransformer().transform(params);
724
+ const decision = getTransformDecision(filename, src, opts);
725
+ globalStats.recordTransform(decision);
726
+ if (decision === "babel") {
727
+ return fallback.get().transform(params);
384
728
  }
385
- const isClassic = opts.jsxRuntime === "classic";
386
- const result = transformSync(filename, src, {
387
- jsx: opts.jsx,
388
- jsxRuntime: isClassic ? JsxRuntime.Classic : JsxRuntime.Automatic,
389
- ...isClassic ? { jsxPragma: opts.jsxPragma, jsxPragmaFrag: opts.jsxPragmaFrag } : { jsxImportSource: opts.jsxImportSource },
390
- typescript: opts.typescript,
391
- sourcemap: metroOptions.dev
392
- });
393
- if (result.errors.length > 0) {
394
- throw new Error(`Facetpack transform error in ${filename}:
395
- ${result.errors.join(`
396
- `)}`);
397
- }
398
- return {
399
- code: result.code,
400
- map: result.map ? JSON.parse(result.map) : null
401
- };
729
+ return transformWithOxc(filename, src, opts, metroOptions.dev);
402
730
  }
403
731
  };
404
732
  }
@@ -406,7 +734,7 @@ ${result.errors.join(`
406
734
  // src/minifier.ts
407
735
  import { minifySync } from "@ecrindigital/facetpack-native";
408
736
  function minify(input) {
409
- const options = {
737
+ const options2 = {
410
738
  compress: input.config.compress ?? true,
411
739
  mangle: input.config.mangle ?? true,
412
740
  keepFnames: input.config.keep_fnames ?? false,
@@ -414,22 +742,28 @@ function minify(input) {
414
742
  dropDebugger: input.config.drop_debugger ?? true,
415
743
  sourcemap: input.map !== undefined
416
744
  };
417
- const result = minifySync(input.code, input.filename, options);
745
+ const originalSize = Buffer.byteLength(input.code, "utf8");
746
+ const result = minifySync(input.code, input.filename, options2);
747
+ const minifiedSize = Buffer.byteLength(result.code, "utf8");
748
+ globalStats.recordMinify(originalSize, minifiedSize);
418
749
  return {
419
750
  code: result.code,
420
751
  map: result.map ?? undefined
421
752
  };
422
753
  }
423
- function minifyCode(code, filename, options) {
754
+ function minifyCode(code, filename, options2) {
424
755
  const nativeOptions = {
425
- compress: options?.compress ?? true,
426
- mangle: options?.mangle ?? true,
427
- keepFnames: options?.keep_fnames ?? false,
428
- dropConsole: options?.drop_console ?? false,
429
- dropDebugger: options?.drop_debugger ?? true,
756
+ compress: options2?.compress ?? true,
757
+ mangle: options2?.mangle ?? true,
758
+ keepFnames: options2?.keep_fnames ?? false,
759
+ dropConsole: options2?.drop_console ?? false,
760
+ dropDebugger: options2?.drop_debugger ?? true,
430
761
  sourcemap: false
431
762
  };
763
+ const originalSize = Buffer.byteLength(code, "utf8");
432
764
  const result = minifySync(code, filename, nativeOptions);
765
+ const minifiedSize = Buffer.byteLength(result.code, "utf8");
766
+ globalStats.recordMinify(originalSize, minifiedSize);
433
767
  return {
434
768
  code: result.code,
435
769
  map: result.map ?? undefined
@@ -440,15 +774,15 @@ var minifier_default = minify;
440
774
  // src/withFacetpack.ts
441
775
  import { resolveSync } from "@ecrindigital/facetpack-native";
442
776
  import { createRequire as createRequire2 } from "module";
443
- import { join, dirname } from "path";
777
+ import { join as join2, dirname } from "path";
444
778
  var DEFAULT_SOURCE_EXTS = ["ts", "tsx", "js", "jsx", "mjs", "cjs"];
445
779
  function getPackageDir() {
446
- const projectRequire = createRequire2(join(process.cwd(), "package.json"));
780
+ const projectRequire = createRequire2(join2(process.cwd(), "package.json"));
447
781
  const packageJsonPath = projectRequire.resolve("@ecrindigital/facetpack/package.json");
448
782
  return dirname(packageJsonPath);
449
783
  }
450
784
  function findFallbackTransformer(projectRoot) {
451
- const projectRequire = createRequire2(join(projectRoot, "package.json"));
785
+ const projectRequire = createRequire2(join2(projectRoot, "package.json"));
452
786
  const transformerPaths = [
453
787
  "@expo/metro-config/babel-transformer",
454
788
  "@react-native/metro-babel-transformer",
@@ -461,20 +795,21 @@ function findFallbackTransformer(projectRoot) {
461
795
  }
462
796
  return;
463
797
  }
464
- function withFacetpack(config, options = {}) {
465
- const sourceExts = options.sourceExts ?? DEFAULT_SOURCE_EXTS;
798
+ function withFacetpack(config, options2 = {}) {
799
+ const sourceExts = options2.sourceExts ?? DEFAULT_SOURCE_EXTS;
466
800
  const packageDir = getPackageDir();
467
- const transformerPath = join(packageDir, "dist", "transformer.js");
468
- const useMinifier = options.minifier !== false;
469
- const minifierPath = useMinifier ? join(packageDir, "dist", "minifier.js") : config.transformer?.minifierPath;
470
- const minifierConfig = typeof options.minifier === "object" ? options.minifier : {};
471
- const useTreeShake = options.treeShake !== false;
801
+ const transformerPath = join2(packageDir, "dist", "transformer.js");
802
+ const useMinifier = options2.minifier !== false;
803
+ const minifierPath = useMinifier ? join2(packageDir, "dist", "minifier.js") : config.transformer?.minifierPath;
804
+ const minifierConfig = typeof options2.minifier === "object" ? options2.minifier : {};
805
+ const useTreeShake = options2.treeShake === true;
472
806
  const existingSerializer = config.serializer?.customSerializer;
473
807
  const customSerializer = useTreeShake ? createFacetpackSerializer(existingSerializer, { treeShake: true }) : existingSerializer;
474
808
  const projectRoot = config.projectRoot || process.cwd();
475
809
  const originalTransformerPath = config.transformer?.babelTransformerPath;
476
810
  const fallbackTransformerPath = originalTransformerPath || findFallbackTransformer(projectRoot);
477
- storeTransformerOptions(options, fallbackTransformerPath);
811
+ storeTransformerOptions(options2, fallbackTransformerPath);
812
+ const existingResolver = config.resolver?.resolveRequest;
478
813
  return {
479
814
  ...config,
480
815
  transformer: {
@@ -503,15 +838,16 @@ function withFacetpack(config, options = {}) {
503
838
  ])
504
839
  ],
505
840
  resolveRequest: (context, moduleName, platform) => {
841
+ const fallbackResolver = existingResolver ?? context.resolveRequest;
506
842
  if (context.originModulePath.includes("node_modules")) {
507
- return context.resolveRequest(context, moduleName, platform);
843
+ return fallbackResolver(context, moduleName, platform);
508
844
  }
509
845
  const cached = getCachedResolution(context.originModulePath, moduleName);
510
846
  if (cached !== undefined) {
511
847
  if (cached) {
512
848
  return { type: "sourceFile", filePath: cached };
513
849
  }
514
- return context.resolveRequest(context, moduleName, platform);
850
+ return fallbackResolver(context, moduleName, platform);
515
851
  }
516
852
  const directory = context.originModulePath.substring(0, context.originModulePath.lastIndexOf("/"));
517
853
  const result = resolveSync(directory, moduleName, {
@@ -522,7 +858,7 @@ function withFacetpack(config, options = {}) {
522
858
  if (result.path) {
523
859
  return { type: "sourceFile", filePath: result.path };
524
860
  }
525
- return context.resolveRequest(context, moduleName, platform);
861
+ return fallbackResolver(context, moduleName, platform);
526
862
  }
527
863
  },
528
864
  serializer: {
@@ -531,13 +867,13 @@ function withFacetpack(config, options = {}) {
531
867
  }
532
868
  };
533
869
  }
534
- function storeTransformerOptions(options, fallbackTransformerPath) {
535
- process.env.FACETPACK_OPTIONS = JSON.stringify(options);
870
+ function storeTransformerOptions(options2, fallbackTransformerPath) {
871
+ process.env.FACETPACK_OPTIONS = JSON.stringify(options2);
536
872
  if (fallbackTransformerPath) {
537
873
  process.env.FACETPACK_FALLBACK_TRANSFORMER = fallbackTransformerPath;
538
874
  }
539
875
  }
540
- function getStoredOptions2() {
876
+ function getStoredOptions() {
541
877
  try {
542
878
  const optionsJson = process.env.FACETPACK_OPTIONS;
543
879
  if (optionsJson) {
@@ -548,11 +884,16 @@ function getStoredOptions2() {
548
884
  }
549
885
  // src/resolver.ts
550
886
  import { resolveSync as resolveSync2 } from "@ecrindigital/facetpack-native";
551
- function createResolver(options) {
887
+ function createResolver(options2) {
552
888
  return {
553
889
  resolve(originModulePath, moduleName) {
554
890
  const directory = originModulePath.substring(0, originModulePath.lastIndexOf("/"));
555
- const result = resolveSync2(directory, moduleName, options);
891
+ const result = resolveSync2(directory, moduleName, options2);
892
+ if (result.path) {
893
+ globalStats.recordResolve("facetpack");
894
+ } else {
895
+ globalStats.recordResolve("metro");
896
+ }
556
897
  return result.path ?? null;
557
898
  }
558
899
  };
@@ -562,9 +903,12 @@ export {
562
903
  transform,
563
904
  setTransformerOptions,
564
905
  resolveSync2 as resolveSync,
906
+ resetStats,
907
+ printStats,
565
908
  minifyCode,
566
909
  minify,
567
- getStoredOptions2 as getStoredOptions,
910
+ getStoredOptions,
911
+ getStats,
568
912
  getCacheStats,
569
913
  createTransformer,
570
914
  createResolver,