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