@ecrindigital/facetpack 0.1.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.
@@ -0,0 +1,211 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/transformer.ts
5
+ import { transformSync, JsxRuntime, resolveBatchSync } from "@ecrindigital/facetpack-native";
6
+ import { parse } from "@babel/parser";
7
+
8
+ // src/cache.ts
9
+ var resolutionCache = new Map;
10
+ var CACHE_TTL = 30000;
11
+ function setCachedResolutions(originModulePath, resolutions) {
12
+ const now = Date.now();
13
+ const cached = new Map;
14
+ for (const [specifier, path] of resolutions) {
15
+ cached.set(specifier, { path, timestamp: now });
16
+ }
17
+ resolutionCache.set(originModulePath, cached);
18
+ }
19
+ function getCachedResolution(originModulePath, specifier) {
20
+ const fileCache = resolutionCache.get(originModulePath);
21
+ if (!fileCache)
22
+ return;
23
+ const cached = fileCache.get(specifier);
24
+ if (!cached)
25
+ return;
26
+ if (Date.now() - cached.timestamp > CACHE_TTL) {
27
+ fileCache.delete(specifier);
28
+ return;
29
+ }
30
+ return cached.path;
31
+ }
32
+ function clearCache() {
33
+ resolutionCache.clear();
34
+ }
35
+ function getCacheStats() {
36
+ let resolutions = 0;
37
+ for (const fileCache of resolutionCache.values()) {
38
+ resolutions += fileCache.size;
39
+ }
40
+ return { files: resolutionCache.size, resolutions };
41
+ }
42
+
43
+ // src/transformer.ts
44
+ var IMPORT_REGEX = /(?:import|export)\s+(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
45
+ var REQUIRE_REGEX = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
46
+ function extractSpecifiers(code) {
47
+ const specifiers = new Set;
48
+ let match;
49
+ while ((match = IMPORT_REGEX.exec(code)) !== null) {
50
+ if (match[1])
51
+ specifiers.add(match[1]);
52
+ }
53
+ while ((match = REQUIRE_REGEX.exec(code)) !== null) {
54
+ if (match[1])
55
+ specifiers.add(match[1]);
56
+ }
57
+ return Array.from(specifiers);
58
+ }
59
+ function preResolveImports(filename, code, sourceExts) {
60
+ const specifiers = extractSpecifiers(code);
61
+ if (specifiers.length === 0)
62
+ return;
63
+ const directory = filename.substring(0, filename.lastIndexOf("/"));
64
+ const results = resolveBatchSync(directory, specifiers, {
65
+ extensions: [...sourceExts.map((ext) => `.${ext}`), ".json"],
66
+ mainFields: ["react-native", "browser", "main"],
67
+ conditionNames: ["react-native", "import", "require"]
68
+ });
69
+ const resolutions = new Map;
70
+ for (let i = 0;i < specifiers.length; i++) {
71
+ const specifier = specifiers[i];
72
+ if (specifier) {
73
+ resolutions.set(specifier, results[i]?.path ?? null);
74
+ }
75
+ }
76
+ setCachedResolutions(filename, resolutions);
77
+ }
78
+ var defaultOptions = {
79
+ jsx: true,
80
+ jsxRuntime: "automatic",
81
+ jsxImportSource: "react",
82
+ jsxPragma: "React.createElement",
83
+ jsxPragmaFrag: "React.Fragment",
84
+ typescript: true,
85
+ sourceExts: ["ts", "tsx", "js", "jsx", "mjs", "cjs"]
86
+ };
87
+ var globalOptions = {};
88
+ var fallbackTransformer = null;
89
+ function getFallbackTransformer() {
90
+ if (fallbackTransformer) {
91
+ return fallbackTransformer;
92
+ }
93
+ const transformerPaths = [
94
+ "@expo/metro-config/babel-transformer",
95
+ "@react-native/metro-babel-transformer",
96
+ "metro-react-native-babel-transformer"
97
+ ];
98
+ for (const transformerPath of transformerPaths) {
99
+ try {
100
+ fallbackTransformer = __require(transformerPath);
101
+ return fallbackTransformer;
102
+ } catch {}
103
+ }
104
+ fallbackTransformer = {
105
+ transform: ({ src }) => ({ code: src, map: null })
106
+ };
107
+ return fallbackTransformer;
108
+ }
109
+ function setTransformerOptions(options) {
110
+ globalOptions = options;
111
+ }
112
+ function getOptions() {
113
+ return { ...defaultOptions, ...globalOptions };
114
+ }
115
+ function isNodeModules(filename) {
116
+ return filename.includes("node_modules");
117
+ }
118
+ function shouldTransform(filename, options) {
119
+ if (isNodeModules(filename)) {
120
+ return false;
121
+ }
122
+ const ext = filename.split(".").pop()?.toLowerCase();
123
+ if (!ext)
124
+ return false;
125
+ return options.sourceExts.includes(ext);
126
+ }
127
+ function transform(params) {
128
+ const { filename, src, options: metroOptions } = params;
129
+ const opts = getOptions();
130
+ if (process.env.FACETPACK_DEBUG) {
131
+ console.log(`[Facetpack] Processing: ${filename}`);
132
+ }
133
+ if (!shouldTransform(filename, opts)) {
134
+ if (process.env.FACETPACK_DEBUG) {
135
+ console.log(`[Facetpack] Fallback: ${filename}`);
136
+ }
137
+ return getFallbackTransformer().transform(params);
138
+ }
139
+ if (process.env.FACETPACK_DEBUG) {
140
+ console.log(`[Facetpack] OXC Transform: ${filename}`);
141
+ }
142
+ try {
143
+ const isClassic = opts.jsxRuntime === "classic";
144
+ const result = transformSync(filename, src, {
145
+ jsx: opts.jsx,
146
+ jsxRuntime: isClassic ? JsxRuntime.Classic : JsxRuntime.Automatic,
147
+ ...isClassic ? { jsxPragma: opts.jsxPragma, jsxPragmaFrag: opts.jsxPragmaFrag } : { jsxImportSource: opts.jsxImportSource },
148
+ typescript: opts.typescript,
149
+ sourcemap: metroOptions.dev
150
+ });
151
+ if (result.errors.length > 0) {
152
+ const errorMessage = result.errors.join(`
153
+ `);
154
+ throw new Error(`Facetpack transform error in ${filename}:
155
+ ${errorMessage}`);
156
+ }
157
+ preResolveImports(filename, result.code, opts.sourceExts);
158
+ const ast = parse(result.code, {
159
+ sourceType: "unambiguous",
160
+ plugins: ["jsx"]
161
+ });
162
+ const output = {
163
+ ast,
164
+ code: result.code,
165
+ map: result.map ? JSON.parse(result.map) : null
166
+ };
167
+ if (process.env.FACETPACK_DEBUG) {
168
+ console.log(`[Facetpack] Output for ${filename}:`);
169
+ console.log(result.code.slice(0, 500));
170
+ }
171
+ return output;
172
+ } catch (error) {
173
+ if (error instanceof Error) {
174
+ error.message = `[Facetpack] ${error.message}`;
175
+ }
176
+ throw error;
177
+ }
178
+ }
179
+ function createTransformer(options = {}) {
180
+ const opts = { ...defaultOptions, ...options };
181
+ return {
182
+ transform(params) {
183
+ const { filename, src, options: metroOptions } = params;
184
+ if (!shouldTransform(filename, opts)) {
185
+ return getFallbackTransformer().transform(params);
186
+ }
187
+ const isClassic = opts.jsxRuntime === "classic";
188
+ const result = transformSync(filename, src, {
189
+ jsx: opts.jsx,
190
+ jsxRuntime: isClassic ? JsxRuntime.Classic : JsxRuntime.Automatic,
191
+ ...isClassic ? { jsxPragma: opts.jsxPragma, jsxPragmaFrag: opts.jsxPragmaFrag } : { jsxImportSource: opts.jsxImportSource },
192
+ typescript: opts.typescript,
193
+ sourcemap: metroOptions.dev
194
+ });
195
+ if (result.errors.length > 0) {
196
+ throw new Error(`Facetpack transform error in ${filename}:
197
+ ${result.errors.join(`
198
+ `)}`);
199
+ }
200
+ return {
201
+ code: result.code,
202
+ map: result.map ? JSON.parse(result.map) : null
203
+ };
204
+ }
205
+ };
206
+ }
207
+ export {
208
+ transform,
209
+ setTransformerOptions,
210
+ createTransformer
211
+ };
@@ -0,0 +1,59 @@
1
+ export interface FacetpackOptions {
2
+ jsx?: boolean;
3
+ jsxRuntime?: 'automatic' | 'classic';
4
+ jsxImportSource?: string;
5
+ jsxPragma?: string;
6
+ jsxPragmaFrag?: string;
7
+ typescript?: boolean;
8
+ sourceExts?: string[];
9
+ }
10
+ export interface MetroTransformerConfig {
11
+ babelTransformerPath?: string;
12
+ getTransformOptions?: (entryPoints: readonly string[], options: {
13
+ dev: boolean;
14
+ hot: boolean;
15
+ platform?: string;
16
+ }, getDependenciesOf: (path: string) => Promise<string[]>) => Promise<{
17
+ transform?: {
18
+ experimentalImportSupport?: boolean;
19
+ inlineRequires?: boolean | {
20
+ blockList?: Record<string, string[]>;
21
+ };
22
+ unstable_disableES6Transforms?: boolean;
23
+ [key: string]: unknown;
24
+ };
25
+ preloadedModules?: Record<string, true>;
26
+ ramGroups?: string[];
27
+ }>;
28
+ [key: string]: unknown;
29
+ }
30
+ export interface MetroResolverConfig {
31
+ sourceExts?: string[];
32
+ [key: string]: unknown;
33
+ }
34
+ export interface MetroConfig {
35
+ transformer?: MetroTransformerConfig;
36
+ resolver?: MetroResolverConfig;
37
+ [key: string]: unknown;
38
+ }
39
+ export interface TransformParams {
40
+ filename: string;
41
+ src: string;
42
+ options: TransformOptions;
43
+ }
44
+ export interface TransformOptions {
45
+ dev: boolean;
46
+ hot: boolean;
47
+ minify: boolean;
48
+ platform?: string;
49
+ projectRoot: string;
50
+ publicPath: string;
51
+ customTransformOptions?: Record<string, unknown>;
52
+ [key: string]: unknown;
53
+ }
54
+ export interface TransformResult {
55
+ ast?: object;
56
+ code: string;
57
+ map?: object | null;
58
+ }
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IACpC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mBAAmB,CAAC,EAAE,CACpB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,OAAO,EAAE;QAAE,GAAG,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAC1D,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,KACnD,OAAO,CAAC;QACX,SAAS,CAAC,EAAE;YACV,yBAAyB,CAAC,EAAE,OAAO,CAAA;YACnC,cAAc,CAAC,EAAE,OAAO,GAAG;gBAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;aAAE,CAAA;YACnE,6BAA6B,CAAC,EAAE,OAAO,CAAA;YACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SACvB,CAAA;QACD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACvC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;KACrB,CAAC,CAAA;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAA;IACZ,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB"}
@@ -0,0 +1,4 @@
1
+ import type { MetroConfig, FacetpackOptions } from './types';
2
+ export declare function withFacetpack(config: MetroConfig, options?: FacetpackOptions): MetroConfig;
3
+ export declare function getStoredOptions(): FacetpackOptions;
4
+ //# sourceMappingURL=withFacetpack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withFacetpack.d.ts","sourceRoot":"","sources":["../src/withFacetpack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAW5D,wBAAgB,aAAa,CAC3B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,gBAAqB,GAC7B,WAAW,CAwEb;AAMD,wBAAgB,gBAAgB,IAAI,gBAAgB,CASnD"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@ecrindigital/facetpack",
3
+ "version": "0.1.1",
4
+ "description": "High-performance Metro transformer powered by OXC",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ },
14
+ "./transformer": {
15
+ "types": "./dist/transformer.d.ts",
16
+ "import": "./dist/transformer.js",
17
+ "require": "./dist/transformer.cjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "author": {
24
+ "name": "Ecrin Digital",
25
+ "email": "contact@ecrin.digital",
26
+ "url": "https://ecrin.digital"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/ecrindigital/facetpack.git",
31
+ "directory": "packages/facetpack"
32
+ },
33
+ "homepage": "https://github.com/ecrindigital/facetpack#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/ecrindigital/facetpack/issues"
36
+ },
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "metro",
40
+ "react-native",
41
+ "transformer",
42
+ "oxc",
43
+ "bundler",
44
+ "compiler",
45
+ "typescript",
46
+ "jsx"
47
+ ],
48
+ "peerDependencies": {
49
+ "metro": ">=0.80.0",
50
+ "metro-config": ">=0.80.0"
51
+ },
52
+ "dependencies": {
53
+ "@babel/parser": "^7.24.0",
54
+ "@ecrindigital/facetpack-native": "workspace:*"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^22",
58
+ "metro": "^0.81.0",
59
+ "metro-config": "^0.81.0",
60
+ "typescript": "^5.7.0"
61
+ },
62
+ "scripts": {
63
+ "build": "bun build ./src/index.ts ./src/transformer.ts --outdir ./dist --target node --format esm --external @ecrindigital/facetpack-native --external @babel/parser && bun run build:cjs && bun run build:types",
64
+ "build:cjs": "bun build ./src/index.ts --outfile ./dist/index.cjs --target node --format cjs --external @ecrindigital/facetpack-native --external @babel/parser && bun build ./src/transformer.ts --outfile ./dist/transformer.cjs --target node --format cjs --external @ecrindigital/facetpack-native --external @babel/parser",
65
+ "build:types": "bunx tsc",
66
+ "dev": "bun build ./src/index.ts --outdir ./dist --target node --watch",
67
+ "typecheck": "tsc --noEmit",
68
+ "test": "bun test",
69
+ "clean": "rm -rf dist"
70
+ },
71
+ "engines": {
72
+ "node": ">=18"
73
+ }
74
+ }