@soda-gql/metro-plugin 0.11.2 → 0.11.3

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 CHANGED
@@ -108,6 +108,14 @@ npx react-native start --reset-cache
108
108
 
109
109
  When using other Metro transformers (e.g., react-native-svg-transformer), soda-gql automatically detects and chains with them.
110
110
 
111
+ ### How Chaining Works
112
+
113
+ When an upstream transformer is detected, soda-gql generates a wrapper transformer file in `node_modules/.cache/soda-gql/`. This wrapper:
114
+
115
+ 1. Hardcodes the upstream transformer path at build time
116
+ 2. Enables reliable chaining in Metro worker processes
117
+ 3. Is automatically regenerated when the upstream path changes
118
+
111
119
  ### Automatic Chaining (Recommended)
112
120
 
113
121
  If you set `babelTransformerPath` before calling `withSodaGql`, it will be automatically preserved and chained:
package/dist/index.cjs CHANGED
@@ -1,6 +1,67 @@
1
1
  const require_transformer = require('./transformer.cjs');
2
2
  let __soda_gql_builder_plugin_support = require("@soda-gql/builder/plugin-support");
3
+ let node_crypto = require("node:crypto");
4
+ let node_fs = require("node:fs");
5
+ let node_path = require("node:path");
3
6
 
7
+ //#region packages/metro-plugin/src/wrapper-generator.ts
8
+ /** Cache directory location relative to project root */
9
+ const CACHE_DIR = "node_modules/.cache/soda-gql";
10
+ /** Wrapper file name suffix */
11
+ const WRAPPER_FILE = "metro-transformer-wrapper.js";
12
+ /**
13
+ * Generate a deterministic hash for cache key.
14
+ * Includes version prefix for cache invalidation on plugin updates.
15
+ */
16
+ function generateCacheKey(upstreamPath) {
17
+ const hash = (0, node_crypto.createHash)("md5");
18
+ hash.update("@soda-gql/metro-plugin:wrapper:v1");
19
+ hash.update(upstreamPath);
20
+ return hash.digest("hex").slice(0, 12);
21
+ }
22
+ /**
23
+ * Generate wrapper transformer content.
24
+ * The generated file imports soda-gql transformer factory and creates
25
+ * a transformer instance with the hardcoded upstream path.
26
+ */
27
+ function generateWrapperContent(upstreamPath) {
28
+ return `// Generated by @soda-gql/metro-plugin
29
+ // DO NOT EDIT - This file is auto-generated
30
+ // Upstream: ${upstreamPath}
31
+
32
+ const { createTransformerWithUpstream } = require("@soda-gql/metro-plugin/transformer");
33
+
34
+ const transformer = createTransformerWithUpstream(${JSON.stringify(upstreamPath)});
35
+
36
+ module.exports = transformer;
37
+ module.exports.transform = transformer.transform;
38
+ module.exports.getCacheKey = transformer.getCacheKey;
39
+ `;
40
+ }
41
+ /**
42
+ * Get or create wrapper transformer file.
43
+ * Returns the absolute path to the wrapper file.
44
+ *
45
+ * The wrapper file is cached based on the upstream transformer path.
46
+ * If the file already exists with the same content, it is reused.
47
+ *
48
+ * @param options - Wrapper generator options
49
+ * @returns Absolute path to the wrapper transformer file
50
+ */
51
+ function ensureWrapperTransformer(options) {
52
+ const { upstreamTransformerPath, projectRoot } = options;
53
+ const cacheDir = (0, node_path.join)(projectRoot, CACHE_DIR);
54
+ const wrapperPath = (0, node_path.join)(cacheDir, `${generateCacheKey(upstreamTransformerPath)}-${WRAPPER_FILE}`);
55
+ const expectedContent = generateWrapperContent(upstreamTransformerPath);
56
+ if ((0, node_fs.existsSync)(wrapperPath)) try {
57
+ if ((0, node_fs.readFileSync)(wrapperPath, "utf-8") === expectedContent) return wrapperPath;
58
+ } catch {}
59
+ (0, node_fs.mkdirSync)(cacheDir, { recursive: true });
60
+ (0, node_fs.writeFileSync)(wrapperPath, expectedContent, "utf-8");
61
+ return wrapperPath;
62
+ }
63
+
64
+ //#endregion
4
65
  //#region packages/metro-plugin/src/index.ts
5
66
  /**
6
67
  * Wrap Metro configuration with soda-gql support.
@@ -57,21 +118,27 @@ let __soda_gql_builder_plugin_support = require("@soda-gql/builder/plugin-suppor
57
118
  * @returns Modified Metro configuration with soda-gql transformer
58
119
  */
59
120
  function withSodaGql(config, options = {}) {
60
- const transformerPath = require.resolve("@soda-gql/metro-plugin/transformer");
61
121
  const stateKey = (0, __soda_gql_builder_plugin_support.getStateKey)(options.configPath);
62
122
  if (options.transformer) (0, __soda_gql_builder_plugin_support.setSharedTransformerType)(stateKey, options.transformer);
63
123
  const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;
64
- const sodaGqlTransformerOptions = {
65
- ...upstreamTransformer && { upstreamTransformer },
66
- ...options.configPath && { configPath: options.configPath },
67
- ...options.transformer && { transformerType: options.transformer }
68
- };
124
+ let transformerPath;
125
+ if (upstreamTransformer) {
126
+ let resolvedUpstream;
127
+ try {
128
+ resolvedUpstream = require.resolve(upstreamTransformer, { paths: [process.cwd()] });
129
+ } catch {
130
+ resolvedUpstream = upstreamTransformer;
131
+ }
132
+ transformerPath = ensureWrapperTransformer({
133
+ upstreamTransformerPath: resolvedUpstream,
134
+ projectRoot: process.cwd()
135
+ });
136
+ } else transformerPath = require.resolve("@soda-gql/metro-plugin/transformer");
69
137
  return {
70
138
  ...config,
71
139
  transformer: {
72
140
  ...config.transformer,
73
- babelTransformerPath: transformerPath,
74
- ...Object.keys(sodaGqlTransformerOptions).length > 0 && { sodaGqlTransformerOptions }
141
+ babelTransformerPath: transformerPath
75
142
  }
76
143
  };
77
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["sodaGqlTransformerOptions: SodaGqlTransformerOptions"],"sources":["../src/index.ts"],"sourcesContent":["import { getStateKey, setSharedTransformerType } from \"@soda-gql/builder/plugin-support\";\nimport type { MetroConfig, MetroPluginOptions, SodaGqlTransformerOptions } from \"./types\";\n\n// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/builder/plugin-support\";\nexport type {\n MetroConfig,\n MetroPluginOptions,\n MetroTransformer,\n MetroTransformParams,\n MetroTransformResult,\n SodaGqlTransformerOptions,\n TransformerType,\n} from \"./types\";\n\n/**\n * Wrap Metro configuration with soda-gql support.\n *\n * This function modifies the Metro configuration to use the soda-gql\n * transformer, which applies GraphQL code transformations at build time.\n *\n * If the config already has a custom `babelTransformerPath` set (e.g., from\n * react-native-svg-transformer), soda-gql will automatically chain with it.\n *\n * @example\n * ```typescript\n * // Expo project (metro.config.js)\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // React Native bare project (metro.config.js)\n * const { getDefaultConfig, mergeConfig } = require(\"@react-native/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Chaining with another transformer (e.g., react-native-svg-transformer)\n * // The existing babelTransformerPath is automatically detected and chained.\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * config.transformer.babelTransformerPath = require.resolve(\"react-native-svg-transformer\");\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Explicitly specifying upstream transformer\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config, {\n * upstreamTransformer: require.resolve(\"react-native-svg-transformer\"),\n * });\n * ```\n *\n * @param config - The Metro configuration to wrap\n * @param options - Optional plugin configuration\n * @returns Modified Metro configuration with soda-gql transformer\n */\nexport function withSodaGql<T extends MetroConfig>(config: T, options: MetroPluginOptions = {}): T {\n // Use package export path to ensure correct resolution from any location\n const transformerPath = require.resolve(\"@soda-gql/metro-plugin/transformer\");\n\n // Store transformer type in shared state for the transformer module to read\n const stateKey = getStateKey(options.configPath);\n if (options.transformer) {\n setSharedTransformerType(stateKey, options.transformer);\n }\n\n // Determine upstream transformer path:\n // 1. Explicit option takes precedence\n // 2. Fall back to existing babelTransformerPath from config (auto-detect)\n const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;\n\n // Build sodaGqlTransformerOptions to pass to the transformer\n const sodaGqlTransformerOptions: SodaGqlTransformerOptions = {\n ...(upstreamTransformer && { upstreamTransformer }),\n ...(options.configPath && { configPath: options.configPath }),\n ...(options.transformer && { transformerType: options.transformer }),\n };\n\n return {\n ...config,\n transformer: {\n ...config.transformer,\n babelTransformerPath: transformerPath,\n // Pass options to transformer via Metro config\n ...(Object.keys(sodaGqlTransformerOptions).length > 0 && { sodaGqlTransformerOptions }),\n },\n } as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAgB,YAAmC,QAAW,UAA8B,EAAE,EAAK;CAEjG,MAAM,kBAAkB,QAAQ,QAAQ,qCAAqC;CAG7E,MAAM,8DAAuB,QAAQ,WAAW;AAChD,KAAI,QAAQ,YACV,iEAAyB,UAAU,QAAQ,YAAY;CAMzD,MAAM,sBAAsB,QAAQ,uBAAuB,OAAO,aAAa;CAG/E,MAAMA,4BAAuD;EAC3D,GAAI,uBAAuB,EAAE,qBAAqB;EAClD,GAAI,QAAQ,cAAc,EAAE,YAAY,QAAQ,YAAY;EAC5D,GAAI,QAAQ,eAAe,EAAE,iBAAiB,QAAQ,aAAa;EACpE;AAED,QAAO;EACL,GAAG;EACH,aAAa;GACX,GAAG,OAAO;GACV,sBAAsB;GAEtB,GAAI,OAAO,KAAK,0BAA0B,CAAC,SAAS,KAAK,EAAE,2BAA2B;GACvF;EACF"}
1
+ {"version":3,"file":"index.cjs","names":["transformerPath: string","resolvedUpstream: string"],"sources":["../src/wrapper-generator.ts","../src/index.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** Cache directory location relative to project root */\nconst CACHE_DIR = \"node_modules/.cache/soda-gql\";\n\n/** Wrapper file name suffix */\nconst WRAPPER_FILE = \"metro-transformer-wrapper.js\";\n\n/**\n * Options for generating wrapper transformer.\n */\nexport type WrapperGeneratorOptions = {\n /** Absolute path to the upstream transformer to chain */\n readonly upstreamTransformerPath: string;\n /** Absolute path to the project root */\n readonly projectRoot: string;\n};\n\n/**\n * Generate a deterministic hash for cache key.\n * Includes version prefix for cache invalidation on plugin updates.\n */\nexport function generateCacheKey(upstreamPath: string): string {\n const hash = createHash(\"md5\");\n hash.update(\"@soda-gql/metro-plugin:wrapper:v1\");\n hash.update(upstreamPath);\n return hash.digest(\"hex\").slice(0, 12);\n}\n\n/**\n * Generate wrapper transformer content.\n * The generated file imports soda-gql transformer factory and creates\n * a transformer instance with the hardcoded upstream path.\n */\nexport function generateWrapperContent(upstreamPath: string): string {\n return `// Generated by @soda-gql/metro-plugin\n// DO NOT EDIT - This file is auto-generated\n// Upstream: ${upstreamPath}\n\nconst { createTransformerWithUpstream } = require(\"@soda-gql/metro-plugin/transformer\");\n\nconst transformer = createTransformerWithUpstream(${JSON.stringify(upstreamPath)});\n\nmodule.exports = transformer;\nmodule.exports.transform = transformer.transform;\nmodule.exports.getCacheKey = transformer.getCacheKey;\n`;\n}\n\n/**\n * Get or create wrapper transformer file.\n * Returns the absolute path to the wrapper file.\n *\n * The wrapper file is cached based on the upstream transformer path.\n * If the file already exists with the same content, it is reused.\n *\n * @param options - Wrapper generator options\n * @returns Absolute path to the wrapper transformer file\n */\nexport function ensureWrapperTransformer(options: WrapperGeneratorOptions): string {\n const { upstreamTransformerPath, projectRoot } = options;\n\n const cacheDir = join(projectRoot, CACHE_DIR);\n const cacheKey = generateCacheKey(upstreamTransformerPath);\n const wrapperPath = join(cacheDir, `${cacheKey}-${WRAPPER_FILE}`);\n\n // Generate expected content\n const expectedContent = generateWrapperContent(upstreamTransformerPath);\n\n // Check if wrapper needs regeneration\n if (existsSync(wrapperPath)) {\n try {\n const existingContent = readFileSync(wrapperPath, \"utf-8\");\n if (existingContent === expectedContent) {\n return wrapperPath;\n }\n } catch {\n // File exists but couldn't be read, regenerate\n }\n }\n\n // Create cache directory if it doesn't exist\n mkdirSync(cacheDir, { recursive: true });\n\n // Write wrapper file\n writeFileSync(wrapperPath, expectedContent, \"utf-8\");\n\n return wrapperPath;\n}\n","import { getStateKey, setSharedTransformerType } from \"@soda-gql/builder/plugin-support\";\nimport type { MetroConfig, MetroPluginOptions } from \"./types\";\nimport { ensureWrapperTransformer } from \"./wrapper-generator\";\n\n// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/builder/plugin-support\";\nexport type {\n MetroConfig,\n MetroPluginOptions,\n MetroTransformer,\n MetroTransformParams,\n MetroTransformResult,\n TransformerType,\n} from \"./types\";\n\n/**\n * Wrap Metro configuration with soda-gql support.\n *\n * This function modifies the Metro configuration to use the soda-gql\n * transformer, which applies GraphQL code transformations at build time.\n *\n * If the config already has a custom `babelTransformerPath` set (e.g., from\n * react-native-svg-transformer), soda-gql will automatically chain with it.\n *\n * @example\n * ```typescript\n * // Expo project (metro.config.js)\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // React Native bare project (metro.config.js)\n * const { getDefaultConfig, mergeConfig } = require(\"@react-native/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Chaining with another transformer (e.g., react-native-svg-transformer)\n * // The existing babelTransformerPath is automatically detected and chained.\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * config.transformer.babelTransformerPath = require.resolve(\"react-native-svg-transformer\");\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Explicitly specifying upstream transformer\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config, {\n * upstreamTransformer: require.resolve(\"react-native-svg-transformer\"),\n * });\n * ```\n *\n * @param config - The Metro configuration to wrap\n * @param options - Optional plugin configuration\n * @returns Modified Metro configuration with soda-gql transformer\n */\nexport function withSodaGql<T extends MetroConfig>(config: T, options: MetroPluginOptions = {}): T {\n // Store transformer type in shared state for the transformer module to read\n const stateKey = getStateKey(options.configPath);\n if (options.transformer) {\n setSharedTransformerType(stateKey, options.transformer);\n }\n\n // Determine upstream transformer path:\n // 1. Explicit option takes precedence\n // 2. Fall back to existing babelTransformerPath from config (auto-detect)\n const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;\n\n let transformerPath: string;\n\n if (upstreamTransformer) {\n // Resolve upstream to absolute path for reliable worker loading\n let resolvedUpstream: string;\n try {\n resolvedUpstream = require.resolve(upstreamTransformer, {\n paths: [process.cwd()],\n });\n } catch {\n // If resolution fails, use the path as-is (might be an absolute path already)\n resolvedUpstream = upstreamTransformer;\n }\n\n // Generate wrapper transformer with hardcoded upstream\n transformerPath = ensureWrapperTransformer({\n upstreamTransformerPath: resolvedUpstream,\n projectRoot: process.cwd(),\n });\n } else {\n // No upstream - use main transformer directly\n transformerPath = require.resolve(\"@soda-gql/metro-plugin/transformer\");\n }\n\n return {\n ...config,\n transformer: {\n ...config.transformer,\n babelTransformerPath: transformerPath,\n },\n } as T;\n}\n"],"mappings":";;;;;;;;AAKA,MAAM,YAAY;;AAGlB,MAAM,eAAe;;;;;AAgBrB,SAAgB,iBAAiB,cAA8B;CAC7D,MAAM,mCAAkB,MAAM;AAC9B,MAAK,OAAO,oCAAoC;AAChD,MAAK,OAAO,aAAa;AACzB,QAAO,KAAK,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;;;AAQxC,SAAgB,uBAAuB,cAA8B;AACnE,QAAO;;eAEM,aAAa;;;;oDAIwB,KAAK,UAAU,aAAa,CAAC;;;;;;;;;;;;;;;;;AAkBjF,SAAgB,yBAAyB,SAA0C;CACjF,MAAM,EAAE,yBAAyB,gBAAgB;CAEjD,MAAM,+BAAgB,aAAa,UAAU;CAE7C,MAAM,kCAAmB,UAAU,GADlB,iBAAiB,wBAAwB,CACX,GAAG,eAAe;CAGjE,MAAM,kBAAkB,uBAAuB,wBAAwB;AAGvE,6BAAe,YAAY,CACzB,KAAI;AAEF,gCADqC,aAAa,QAAQ,KAClC,gBACtB,QAAO;SAEH;AAMV,wBAAU,UAAU,EAAE,WAAW,MAAM,CAAC;AAGxC,4BAAc,aAAa,iBAAiB,QAAQ;AAEpD,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpBT,SAAgB,YAAmC,QAAW,UAA8B,EAAE,EAAK;CAEjG,MAAM,8DAAuB,QAAQ,WAAW;AAChD,KAAI,QAAQ,YACV,iEAAyB,UAAU,QAAQ,YAAY;CAMzD,MAAM,sBAAsB,QAAQ,uBAAuB,OAAO,aAAa;CAE/E,IAAIA;AAEJ,KAAI,qBAAqB;EAEvB,IAAIC;AACJ,MAAI;AACF,sBAAmB,QAAQ,QAAQ,qBAAqB,EACtD,OAAO,CAAC,QAAQ,KAAK,CAAC,EACvB,CAAC;UACI;AAEN,sBAAmB;;AAIrB,oBAAkB,yBAAyB;GACzC,yBAAyB;GACzB,aAAa,QAAQ,KAAK;GAC3B,CAAC;OAGF,mBAAkB,QAAQ,QAAQ,qCAAqC;AAGzE,QAAO;EACL,GAAG;EACH,aAAa;GACX,GAAG,OAAO;GACV,sBAAsB;GACvB;EACF"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MetroTransformer, i as MetroTransformResult, n as MetroPluginOptions, o as SodaGqlTransformerOptions, r as MetroTransformParams, s as TransformerType, t as MetroConfig } from "./types-DEWrNhj2.cjs";
1
+ import { a as MetroTransformer, i as MetroTransformResult, n as MetroPluginOptions, o as TransformerType, r as MetroTransformParams, t as MetroConfig } from "./types-DPugisG3.cjs";
2
2
  import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/builder/plugin-support";
3
3
 
4
4
  //#region packages/metro-plugin/src/index.d.ts
@@ -59,5 +59,5 @@ import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/builde
59
59
  */
60
60
  declare function withSodaGql<T extends MetroConfig>(config: T, options?: MetroPluginOptions): T;
61
61
  //#endregion
62
- export { type MetroConfig, type MetroPluginOptions, type MetroTransformParams, type MetroTransformResult, type MetroTransformer, type SodaGqlTransformerOptions, type TransformerType, getSharedArtifact, getSharedState, getStateKey, withSodaGql };
62
+ export { type MetroConfig, type MetroPluginOptions, type MetroTransformParams, type MetroTransformResult, type MetroTransformer, type TransformerType, getSharedArtifact, getSharedState, getStateKey, withSodaGql };
63
63
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MetroTransformer, i as MetroTransformResult, n as MetroPluginOptions, o as SodaGqlTransformerOptions, r as MetroTransformParams, s as TransformerType, t as MetroConfig } from "./types-D-K1oBE_.mjs";
1
+ import { a as MetroTransformer, i as MetroTransformResult, n as MetroPluginOptions, o as TransformerType, r as MetroTransformParams, t as MetroConfig } from "./types-BjrzxgOZ.mjs";
2
2
  import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/builder/plugin-support";
3
3
 
4
4
  //#region packages/metro-plugin/src/index.d.ts
@@ -59,5 +59,5 @@ import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/builde
59
59
  */
60
60
  declare function withSodaGql<T extends MetroConfig>(config: T, options?: MetroPluginOptions): T;
61
61
  //#endregion
62
- export { type MetroConfig, type MetroPluginOptions, type MetroTransformParams, type MetroTransformResult, type MetroTransformer, type SodaGqlTransformerOptions, type TransformerType, getSharedArtifact, getSharedState, getStateKey, withSodaGql };
62
+ export { type MetroConfig, type MetroPluginOptions, type MetroTransformParams, type MetroTransformResult, type MetroTransformer, type TransformerType, getSharedArtifact, getSharedState, getStateKey, withSodaGql };
63
63
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,6 +1,67 @@
1
1
  import { t as __require } from "./chunk--GtjC1aJ.mjs";
2
2
  import { getSharedArtifact, getSharedState, getStateKey, getStateKey as getStateKey$1, setSharedTransformerType } from "@soda-gql/builder/plugin-support";
3
+ import { createHash } from "node:crypto";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
3
6
 
7
+ //#region packages/metro-plugin/src/wrapper-generator.ts
8
+ /** Cache directory location relative to project root */
9
+ const CACHE_DIR = "node_modules/.cache/soda-gql";
10
+ /** Wrapper file name suffix */
11
+ const WRAPPER_FILE = "metro-transformer-wrapper.js";
12
+ /**
13
+ * Generate a deterministic hash for cache key.
14
+ * Includes version prefix for cache invalidation on plugin updates.
15
+ */
16
+ function generateCacheKey(upstreamPath) {
17
+ const hash = createHash("md5");
18
+ hash.update("@soda-gql/metro-plugin:wrapper:v1");
19
+ hash.update(upstreamPath);
20
+ return hash.digest("hex").slice(0, 12);
21
+ }
22
+ /**
23
+ * Generate wrapper transformer content.
24
+ * The generated file imports soda-gql transformer factory and creates
25
+ * a transformer instance with the hardcoded upstream path.
26
+ */
27
+ function generateWrapperContent(upstreamPath) {
28
+ return `// Generated by @soda-gql/metro-plugin
29
+ // DO NOT EDIT - This file is auto-generated
30
+ // Upstream: ${upstreamPath}
31
+
32
+ const { createTransformerWithUpstream } = require("@soda-gql/metro-plugin/transformer");
33
+
34
+ const transformer = createTransformerWithUpstream(${JSON.stringify(upstreamPath)});
35
+
36
+ module.exports = transformer;
37
+ module.exports.transform = transformer.transform;
38
+ module.exports.getCacheKey = transformer.getCacheKey;
39
+ `;
40
+ }
41
+ /**
42
+ * Get or create wrapper transformer file.
43
+ * Returns the absolute path to the wrapper file.
44
+ *
45
+ * The wrapper file is cached based on the upstream transformer path.
46
+ * If the file already exists with the same content, it is reused.
47
+ *
48
+ * @param options - Wrapper generator options
49
+ * @returns Absolute path to the wrapper transformer file
50
+ */
51
+ function ensureWrapperTransformer(options) {
52
+ const { upstreamTransformerPath, projectRoot } = options;
53
+ const cacheDir = join(projectRoot, CACHE_DIR);
54
+ const wrapperPath = join(cacheDir, `${generateCacheKey(upstreamTransformerPath)}-${WRAPPER_FILE}`);
55
+ const expectedContent = generateWrapperContent(upstreamTransformerPath);
56
+ if (existsSync(wrapperPath)) try {
57
+ if (readFileSync(wrapperPath, "utf-8") === expectedContent) return wrapperPath;
58
+ } catch {}
59
+ mkdirSync(cacheDir, { recursive: true });
60
+ writeFileSync(wrapperPath, expectedContent, "utf-8");
61
+ return wrapperPath;
62
+ }
63
+
64
+ //#endregion
4
65
  //#region packages/metro-plugin/src/index.ts
5
66
  /**
6
67
  * Wrap Metro configuration with soda-gql support.
@@ -57,21 +118,27 @@ import { getSharedArtifact, getSharedState, getStateKey, getStateKey as getState
57
118
  * @returns Modified Metro configuration with soda-gql transformer
58
119
  */
59
120
  function withSodaGql(config, options = {}) {
60
- const transformerPath = __require.resolve("@soda-gql/metro-plugin/transformer");
61
121
  const stateKey = getStateKey$1(options.configPath);
62
122
  if (options.transformer) setSharedTransformerType(stateKey, options.transformer);
63
123
  const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;
64
- const sodaGqlTransformerOptions = {
65
- ...upstreamTransformer && { upstreamTransformer },
66
- ...options.configPath && { configPath: options.configPath },
67
- ...options.transformer && { transformerType: options.transformer }
68
- };
124
+ let transformerPath;
125
+ if (upstreamTransformer) {
126
+ let resolvedUpstream;
127
+ try {
128
+ resolvedUpstream = __require.resolve(upstreamTransformer, { paths: [process.cwd()] });
129
+ } catch {
130
+ resolvedUpstream = upstreamTransformer;
131
+ }
132
+ transformerPath = ensureWrapperTransformer({
133
+ upstreamTransformerPath: resolvedUpstream,
134
+ projectRoot: process.cwd()
135
+ });
136
+ } else transformerPath = __require.resolve("@soda-gql/metro-plugin/transformer");
69
137
  return {
70
138
  ...config,
71
139
  transformer: {
72
140
  ...config.transformer,
73
- babelTransformerPath: transformerPath,
74
- ...Object.keys(sodaGqlTransformerOptions).length > 0 && { sodaGqlTransformerOptions }
141
+ babelTransformerPath: transformerPath
75
142
  }
76
143
  };
77
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["getStateKey","sodaGqlTransformerOptions: SodaGqlTransformerOptions"],"sources":["../src/index.ts"],"sourcesContent":["import { getStateKey, setSharedTransformerType } from \"@soda-gql/builder/plugin-support\";\nimport type { MetroConfig, MetroPluginOptions, SodaGqlTransformerOptions } from \"./types\";\n\n// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/builder/plugin-support\";\nexport type {\n MetroConfig,\n MetroPluginOptions,\n MetroTransformer,\n MetroTransformParams,\n MetroTransformResult,\n SodaGqlTransformerOptions,\n TransformerType,\n} from \"./types\";\n\n/**\n * Wrap Metro configuration with soda-gql support.\n *\n * This function modifies the Metro configuration to use the soda-gql\n * transformer, which applies GraphQL code transformations at build time.\n *\n * If the config already has a custom `babelTransformerPath` set (e.g., from\n * react-native-svg-transformer), soda-gql will automatically chain with it.\n *\n * @example\n * ```typescript\n * // Expo project (metro.config.js)\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // React Native bare project (metro.config.js)\n * const { getDefaultConfig, mergeConfig } = require(\"@react-native/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Chaining with another transformer (e.g., react-native-svg-transformer)\n * // The existing babelTransformerPath is automatically detected and chained.\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * config.transformer.babelTransformerPath = require.resolve(\"react-native-svg-transformer\");\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Explicitly specifying upstream transformer\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config, {\n * upstreamTransformer: require.resolve(\"react-native-svg-transformer\"),\n * });\n * ```\n *\n * @param config - The Metro configuration to wrap\n * @param options - Optional plugin configuration\n * @returns Modified Metro configuration with soda-gql transformer\n */\nexport function withSodaGql<T extends MetroConfig>(config: T, options: MetroPluginOptions = {}): T {\n // Use package export path to ensure correct resolution from any location\n const transformerPath = require.resolve(\"@soda-gql/metro-plugin/transformer\");\n\n // Store transformer type in shared state for the transformer module to read\n const stateKey = getStateKey(options.configPath);\n if (options.transformer) {\n setSharedTransformerType(stateKey, options.transformer);\n }\n\n // Determine upstream transformer path:\n // 1. Explicit option takes precedence\n // 2. Fall back to existing babelTransformerPath from config (auto-detect)\n const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;\n\n // Build sodaGqlTransformerOptions to pass to the transformer\n const sodaGqlTransformerOptions: SodaGqlTransformerOptions = {\n ...(upstreamTransformer && { upstreamTransformer }),\n ...(options.configPath && { configPath: options.configPath }),\n ...(options.transformer && { transformerType: options.transformer }),\n };\n\n return {\n ...config,\n transformer: {\n ...config.transformer,\n babelTransformerPath: transformerPath,\n // Pass options to transformer via Metro config\n ...(Object.keys(sodaGqlTransformerOptions).length > 0 && { sodaGqlTransformerOptions }),\n },\n } as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAgB,YAAmC,QAAW,UAA8B,EAAE,EAAK;CAEjG,MAAM,4BAA0B,QAAQ,qCAAqC;CAG7E,MAAM,WAAWA,cAAY,QAAQ,WAAW;AAChD,KAAI,QAAQ,YACV,0BAAyB,UAAU,QAAQ,YAAY;CAMzD,MAAM,sBAAsB,QAAQ,uBAAuB,OAAO,aAAa;CAG/E,MAAMC,4BAAuD;EAC3D,GAAI,uBAAuB,EAAE,qBAAqB;EAClD,GAAI,QAAQ,cAAc,EAAE,YAAY,QAAQ,YAAY;EAC5D,GAAI,QAAQ,eAAe,EAAE,iBAAiB,QAAQ,aAAa;EACpE;AAED,QAAO;EACL,GAAG;EACH,aAAa;GACX,GAAG,OAAO;GACV,sBAAsB;GAEtB,GAAI,OAAO,KAAK,0BAA0B,CAAC,SAAS,KAAK,EAAE,2BAA2B;GACvF;EACF"}
1
+ {"version":3,"file":"index.mjs","names":["getStateKey","transformerPath: string","resolvedUpstream: string"],"sources":["../src/wrapper-generator.ts","../src/index.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** Cache directory location relative to project root */\nconst CACHE_DIR = \"node_modules/.cache/soda-gql\";\n\n/** Wrapper file name suffix */\nconst WRAPPER_FILE = \"metro-transformer-wrapper.js\";\n\n/**\n * Options for generating wrapper transformer.\n */\nexport type WrapperGeneratorOptions = {\n /** Absolute path to the upstream transformer to chain */\n readonly upstreamTransformerPath: string;\n /** Absolute path to the project root */\n readonly projectRoot: string;\n};\n\n/**\n * Generate a deterministic hash for cache key.\n * Includes version prefix for cache invalidation on plugin updates.\n */\nexport function generateCacheKey(upstreamPath: string): string {\n const hash = createHash(\"md5\");\n hash.update(\"@soda-gql/metro-plugin:wrapper:v1\");\n hash.update(upstreamPath);\n return hash.digest(\"hex\").slice(0, 12);\n}\n\n/**\n * Generate wrapper transformer content.\n * The generated file imports soda-gql transformer factory and creates\n * a transformer instance with the hardcoded upstream path.\n */\nexport function generateWrapperContent(upstreamPath: string): string {\n return `// Generated by @soda-gql/metro-plugin\n// DO NOT EDIT - This file is auto-generated\n// Upstream: ${upstreamPath}\n\nconst { createTransformerWithUpstream } = require(\"@soda-gql/metro-plugin/transformer\");\n\nconst transformer = createTransformerWithUpstream(${JSON.stringify(upstreamPath)});\n\nmodule.exports = transformer;\nmodule.exports.transform = transformer.transform;\nmodule.exports.getCacheKey = transformer.getCacheKey;\n`;\n}\n\n/**\n * Get or create wrapper transformer file.\n * Returns the absolute path to the wrapper file.\n *\n * The wrapper file is cached based on the upstream transformer path.\n * If the file already exists with the same content, it is reused.\n *\n * @param options - Wrapper generator options\n * @returns Absolute path to the wrapper transformer file\n */\nexport function ensureWrapperTransformer(options: WrapperGeneratorOptions): string {\n const { upstreamTransformerPath, projectRoot } = options;\n\n const cacheDir = join(projectRoot, CACHE_DIR);\n const cacheKey = generateCacheKey(upstreamTransformerPath);\n const wrapperPath = join(cacheDir, `${cacheKey}-${WRAPPER_FILE}`);\n\n // Generate expected content\n const expectedContent = generateWrapperContent(upstreamTransformerPath);\n\n // Check if wrapper needs regeneration\n if (existsSync(wrapperPath)) {\n try {\n const existingContent = readFileSync(wrapperPath, \"utf-8\");\n if (existingContent === expectedContent) {\n return wrapperPath;\n }\n } catch {\n // File exists but couldn't be read, regenerate\n }\n }\n\n // Create cache directory if it doesn't exist\n mkdirSync(cacheDir, { recursive: true });\n\n // Write wrapper file\n writeFileSync(wrapperPath, expectedContent, \"utf-8\");\n\n return wrapperPath;\n}\n","import { getStateKey, setSharedTransformerType } from \"@soda-gql/builder/plugin-support\";\nimport type { MetroConfig, MetroPluginOptions } from \"./types\";\nimport { ensureWrapperTransformer } from \"./wrapper-generator\";\n\n// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/builder/plugin-support\";\nexport type {\n MetroConfig,\n MetroPluginOptions,\n MetroTransformer,\n MetroTransformParams,\n MetroTransformResult,\n TransformerType,\n} from \"./types\";\n\n/**\n * Wrap Metro configuration with soda-gql support.\n *\n * This function modifies the Metro configuration to use the soda-gql\n * transformer, which applies GraphQL code transformations at build time.\n *\n * If the config already has a custom `babelTransformerPath` set (e.g., from\n * react-native-svg-transformer), soda-gql will automatically chain with it.\n *\n * @example\n * ```typescript\n * // Expo project (metro.config.js)\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // React Native bare project (metro.config.js)\n * const { getDefaultConfig, mergeConfig } = require(\"@react-native/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Chaining with another transformer (e.g., react-native-svg-transformer)\n * // The existing babelTransformerPath is automatically detected and chained.\n * const { getDefaultConfig } = require(\"expo/metro-config\");\n * const { withSodaGql } = require(\"@soda-gql/metro-plugin\");\n *\n * const config = getDefaultConfig(__dirname);\n * config.transformer.babelTransformerPath = require.resolve(\"react-native-svg-transformer\");\n * module.exports = withSodaGql(config);\n * ```\n *\n * @example\n * ```typescript\n * // Explicitly specifying upstream transformer\n * const config = getDefaultConfig(__dirname);\n * module.exports = withSodaGql(config, {\n * upstreamTransformer: require.resolve(\"react-native-svg-transformer\"),\n * });\n * ```\n *\n * @param config - The Metro configuration to wrap\n * @param options - Optional plugin configuration\n * @returns Modified Metro configuration with soda-gql transformer\n */\nexport function withSodaGql<T extends MetroConfig>(config: T, options: MetroPluginOptions = {}): T {\n // Store transformer type in shared state for the transformer module to read\n const stateKey = getStateKey(options.configPath);\n if (options.transformer) {\n setSharedTransformerType(stateKey, options.transformer);\n }\n\n // Determine upstream transformer path:\n // 1. Explicit option takes precedence\n // 2. Fall back to existing babelTransformerPath from config (auto-detect)\n const upstreamTransformer = options.upstreamTransformer ?? config.transformer?.babelTransformerPath;\n\n let transformerPath: string;\n\n if (upstreamTransformer) {\n // Resolve upstream to absolute path for reliable worker loading\n let resolvedUpstream: string;\n try {\n resolvedUpstream = require.resolve(upstreamTransformer, {\n paths: [process.cwd()],\n });\n } catch {\n // If resolution fails, use the path as-is (might be an absolute path already)\n resolvedUpstream = upstreamTransformer;\n }\n\n // Generate wrapper transformer with hardcoded upstream\n transformerPath = ensureWrapperTransformer({\n upstreamTransformerPath: resolvedUpstream,\n projectRoot: process.cwd(),\n });\n } else {\n // No upstream - use main transformer directly\n transformerPath = require.resolve(\"@soda-gql/metro-plugin/transformer\");\n }\n\n return {\n ...config,\n transformer: {\n ...config.transformer,\n babelTransformerPath: transformerPath,\n },\n } as T;\n}\n"],"mappings":";;;;;;;;AAKA,MAAM,YAAY;;AAGlB,MAAM,eAAe;;;;;AAgBrB,SAAgB,iBAAiB,cAA8B;CAC7D,MAAM,OAAO,WAAW,MAAM;AAC9B,MAAK,OAAO,oCAAoC;AAChD,MAAK,OAAO,aAAa;AACzB,QAAO,KAAK,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;;;AAQxC,SAAgB,uBAAuB,cAA8B;AACnE,QAAO;;eAEM,aAAa;;;;oDAIwB,KAAK,UAAU,aAAa,CAAC;;;;;;;;;;;;;;;;;AAkBjF,SAAgB,yBAAyB,SAA0C;CACjF,MAAM,EAAE,yBAAyB,gBAAgB;CAEjD,MAAM,WAAW,KAAK,aAAa,UAAU;CAE7C,MAAM,cAAc,KAAK,UAAU,GADlB,iBAAiB,wBAAwB,CACX,GAAG,eAAe;CAGjE,MAAM,kBAAkB,uBAAuB,wBAAwB;AAGvE,KAAI,WAAW,YAAY,CACzB,KAAI;AAEF,MADwB,aAAa,aAAa,QAAQ,KAClC,gBACtB,QAAO;SAEH;AAMV,WAAU,UAAU,EAAE,WAAW,MAAM,CAAC;AAGxC,eAAc,aAAa,iBAAiB,QAAQ;AAEpD,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpBT,SAAgB,YAAmC,QAAW,UAA8B,EAAE,EAAK;CAEjG,MAAM,WAAWA,cAAY,QAAQ,WAAW;AAChD,KAAI,QAAQ,YACV,0BAAyB,UAAU,QAAQ,YAAY;CAMzD,MAAM,sBAAsB,QAAQ,uBAAuB,OAAO,aAAa;CAE/E,IAAIC;AAEJ,KAAI,qBAAqB;EAEvB,IAAIC;AACJ,MAAI;AACF,gCAA2B,QAAQ,qBAAqB,EACtD,OAAO,CAAC,QAAQ,KAAK,CAAC,EACvB,CAAC;UACI;AAEN,sBAAmB;;AAIrB,oBAAkB,yBAAyB;GACzC,yBAAyB;GACzB,aAAa,QAAQ,KAAK;GAC3B,CAAC;OAGF,6BAA0B,QAAQ,qCAAqC;AAGzE,QAAO;EACL,GAAG;EACH,aAAa;GACX,GAAG,OAAO;GACV,sBAAsB;GACvB;EACF"}
@@ -819,10 +819,6 @@ const UPSTREAM_TRANSFORMER_CANDIDATES = [
819
819
  */
820
820
  let upstreamTransformer = null;
821
821
  /**
822
- * Cached custom upstream transformer path (from sodaGqlTransformerOptions).
823
- */
824
- let customUpstreamPath = null;
825
- /**
826
822
  * Try to resolve a module from multiple locations.
827
823
  * Falls back through various resolution strategies.
828
824
  */
@@ -836,29 +832,11 @@ const tryResolve = (moduleName) => {
836
832
  return null;
837
833
  };
838
834
  /**
839
- * Set custom upstream transformer path from options.
840
- * Called once during the first transform to capture the path from Metro options.
841
- */
842
- const setCustomUpstreamPath = (sodaGqlOptions) => {
843
- if (customUpstreamPath !== null) return;
844
- customUpstreamPath = sodaGqlOptions?.upstreamTransformer ?? null;
845
- };
846
- /**
847
835
  * Detect and load the upstream Metro Babel transformer.
848
- * Priority:
849
- * 1. Custom upstream path from sodaGqlTransformerOptions (set via setCustomUpstreamPath)
850
- * 2. Default candidates in order of preference
836
+ * Tries candidates in order of preference.
851
837
  */
852
838
  const getUpstreamTransformer = () => {
853
839
  if (upstreamTransformer) return upstreamTransformer;
854
- if (customUpstreamPath) {
855
- const resolved = tryResolve(customUpstreamPath);
856
- if (resolved) {
857
- upstreamTransformer = require(resolved);
858
- return upstreamTransformer;
859
- }
860
- console.warn(`[@soda-gql/metro-plugin] Custom upstream transformer not found: ${customUpstreamPath}. Falling back to default transformers.`);
861
- }
862
840
  for (const candidate of UPSTREAM_TRANSFORMER_CANDIDATES) {
863
841
  const resolved = tryResolve(candidate);
864
842
  if (resolved) {
@@ -920,10 +898,16 @@ const initializeSwcTransformer = async (artifact, config) => {
920
898
  * Wraps the upstream Metro Babel transformer.
921
899
  */
922
900
  async function transform(params) {
901
+ return transformCore(params, getUpstreamTransformer);
902
+ }
903
+ /**
904
+ * Core transformation logic.
905
+ * @internal
906
+ */
907
+ async function transformCore(params, getUpstream) {
923
908
  const { src, filename, options } = params;
924
909
  const stateKey = (0, __soda_gql_builder_plugin_support.getStateKey)();
925
- setCustomUpstreamPath(options.sodaGqlTransformerOptions);
926
- const upstream = getUpstreamTransformer();
910
+ const upstream = getUpstream();
927
911
  const session = ensurePluginSession();
928
912
  if (!session) return upstream.transform(params);
929
913
  let artifact = (0, __soda_gql_builder_plugin_support.getSharedArtifact)(stateKey);
@@ -995,11 +979,19 @@ async function transform(params) {
995
979
  * Includes artifact generation to ensure cache invalidation when models change.
996
980
  */
997
981
  function getCacheKey() {
982
+ return getCacheKeyCore(getUpstreamTransformer);
983
+ }
984
+ /**
985
+ * Core cache key generation logic.
986
+ * @internal
987
+ */
988
+ function getCacheKeyCore(getUpstream, upstreamPath) {
998
989
  const state = (0, __soda_gql_builder_plugin_support.getSharedState)((0, __soda_gql_builder_plugin_support.getStateKey)());
999
990
  const artifact = state.currentArtifact;
1000
- const upstream = getUpstreamTransformer();
991
+ const upstream = getUpstream();
1001
992
  const hash = node_crypto.default.createHash("md5");
1002
- hash.update("@soda-gql/metro-plugin:v2");
993
+ hash.update("@soda-gql/metro-plugin:v3");
994
+ if (upstreamPath) hash.update(upstreamPath);
1003
995
  if (upstream.getCacheKey) hash.update(upstream.getCacheKey());
1004
996
  hash.update(String(state.generation));
1005
997
  if (artifact) {
@@ -1008,9 +1000,39 @@ function getCacheKey() {
1008
1000
  }
1009
1001
  return hash.digest("hex");
1010
1002
  }
1003
+ /**
1004
+ * Create a transformer with a specific upstream transformer path.
1005
+ * Used by generated wrapper files to inject the upstream path at build time.
1006
+ *
1007
+ * @param upstreamPath - Absolute path to the upstream transformer module
1008
+ * @returns MetroTransformer instance configured with the specified upstream
1009
+ */
1010
+ function createTransformerWithUpstream(upstreamPath) {
1011
+ let cachedUpstream = null;
1012
+ const getUpstream = () => {
1013
+ if (cachedUpstream) return cachedUpstream;
1014
+ const resolved = tryResolve(upstreamPath);
1015
+ if (!resolved) throw new Error(`[@soda-gql/metro-plugin] Upstream transformer not found: ${upstreamPath}`);
1016
+ cachedUpstream = require(resolved);
1017
+ return cachedUpstream;
1018
+ };
1019
+ return {
1020
+ transform: (params) => transformCore(params, getUpstream),
1021
+ getCacheKey: () => getCacheKeyCore(getUpstream, upstreamPath)
1022
+ };
1023
+ }
1024
+ /**
1025
+ * Reset upstream transformer cache for testing.
1026
+ * @internal
1027
+ */
1028
+ function __resetUpstreamTransformer() {
1029
+ upstreamTransformer = null;
1030
+ }
1011
1031
 
1012
1032
  //#endregion
1033
+ exports.__resetUpstreamTransformer = __resetUpstreamTransformer;
1013
1034
  exports.__toESM = __toESM;
1035
+ exports.createTransformerWithUpstream = createTransformerWithUpstream;
1014
1036
  exports.getCacheKey = getCacheKey;
1015
1037
  exports.transform = transform;
1016
1038
  //# sourceMappingURL=transformer.cjs.map