@tamagui/metro-plugin 2.7.7 → 3.0.0-beta.643.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +12 -0
  2. package/dist/cjs/babel.cjs +77 -0
  3. package/dist/cjs/compilerCache.cjs +237 -0
  4. package/dist/cjs/diagnostics.cjs +41 -0
  5. package/dist/cjs/frontend.cjs +870 -0
  6. package/dist/cjs/index.cjs +102 -0
  7. package/dist/cjs/lowering.cjs +109 -0
  8. package/dist/cjs/metroResolver.cjs +197 -0
  9. package/dist/cjs/transformOptions.cjs +35 -0
  10. package/dist/cjs/transformer.cjs +142 -0
  11. package/dist/cjs/zeroRuntime.cjs +140 -0
  12. package/dist/cjs/zeroSerializer.cjs +150 -0
  13. package/dist/esm/babel.mjs +52 -0
  14. package/dist/esm/babel.mjs.map +1 -0
  15. package/dist/esm/compilerCache.mjs +212 -0
  16. package/dist/esm/compilerCache.mjs.map +1 -0
  17. package/dist/esm/diagnostics.mjs +18 -0
  18. package/dist/esm/diagnostics.mjs.map +1 -0
  19. package/dist/esm/frontend.mjs +839 -0
  20. package/dist/esm/frontend.mjs.map +1 -0
  21. package/dist/esm/index.mjs +64 -22
  22. package/dist/esm/index.mjs.map +1 -1
  23. package/dist/esm/lowering.mjs +89 -0
  24. package/dist/esm/lowering.mjs.map +1 -0
  25. package/dist/esm/metroResolver.mjs +173 -0
  26. package/dist/esm/metroResolver.mjs.map +1 -0
  27. package/dist/esm/transformOptions.mjs +14 -0
  28. package/dist/esm/transformOptions.mjs.map +1 -0
  29. package/dist/esm/transformer.mjs +119 -0
  30. package/dist/esm/transformer.mjs.map +1 -0
  31. package/dist/esm/zeroRuntime.mjs +105 -0
  32. package/dist/esm/zeroRuntime.mjs.map +1 -0
  33. package/dist/esm/zeroSerializer.mjs +123 -0
  34. package/dist/esm/zeroSerializer.mjs.map +1 -0
  35. package/package.json +33 -5
  36. package/src/babel.ts +87 -0
  37. package/src/compilerCache.ts +346 -0
  38. package/src/diagnostics.ts +47 -0
  39. package/src/frontend.ts +1178 -0
  40. package/src/index.ts +117 -14
  41. package/src/lowering.ts +136 -0
  42. package/src/metroResolver.ts +209 -0
  43. package/src/transformOptions.ts +36 -0
  44. package/src/transformer.ts +210 -0
  45. package/src/zeroRuntime.ts +212 -0
  46. package/src/zeroSerializer.ts +175 -0
  47. package/types/babel.d.ts +28 -0
  48. package/types/babel.d.ts.map +11 -0
  49. package/types/compilerCache.d.ts +63 -0
  50. package/types/compilerCache.d.ts.map +11 -0
  51. package/types/diagnostics.d.ts +16 -0
  52. package/types/diagnostics.d.ts.map +11 -0
  53. package/types/frontend.d.ts +73 -0
  54. package/types/frontend.d.ts.map +11 -0
  55. package/types/index.d.ts +49 -32
  56. package/types/index.d.ts.map +11 -1
  57. package/types/lowering.d.ts +20 -0
  58. package/types/lowering.d.ts.map +11 -0
  59. package/types/metroResolver.d.ts +21 -0
  60. package/types/metroResolver.d.ts.map +11 -0
  61. package/types/transformOptions.d.ts +13 -0
  62. package/types/transformOptions.d.ts.map +11 -0
  63. package/types/transformer.d.ts +26 -0
  64. package/types/transformer.d.ts.map +11 -0
  65. package/types/zeroRuntime.d.ts +75 -0
  66. package/types/zeroRuntime.d.ts.map +11 -0
  67. package/types/zeroSerializer.d.ts +6 -0
  68. package/types/zeroSerializer.d.ts.map +11 -0
  69. package/dist/cjs/index.js +0 -45
  70. package/dist/cjs/index.js.map +0 -6
  71. package/dist/esm/index.js +0 -25
  72. package/dist/esm/index.js.map +0 -1
package/src/index.ts CHANGED
@@ -1,20 +1,56 @@
1
- import { loadTamaguiBuildConfigSync, type TamaguiOptions } from '@tamagui/static'
1
+ import { createRequire } from 'node:module'
2
+ import { isAbsolute, join } from 'node:path'
3
+
4
+ import Static from '@tamagui/static'
5
+ import type { TamaguiOptions } from '@tamagui/static'
6
+
7
+ import { defaultMetroCompilerCacheRoot } from './compilerCache'
8
+ import { applyMetroZeroRuntime } from './zeroSerializer'
9
+ import { createMetroZeroController } from './zeroRuntime'
10
+ import { formatMetroCompilerDiagnostic } from './diagnostics'
11
+ import { MetroCompilerFrontend } from './frontend'
12
+ import { writeMetroCompilerTransformerBridge } from './transformer'
13
+ import { composeMetroGetTransformOptions } from './transformOptions'
2
14
 
3
15
  export type MetroTamaguiOptions = TamaguiOptions & {
16
+ /** Override the ignored on-disk handoff used by Metro transform workers. */
17
+ compilerCacheRoot?: string
4
18
  /**
5
- * @deprecated CSS interop is no longer supported. Use `tamagui generate` instead.
19
+ * Set by the zero-runtime island bundle request. An island is a second Metro
20
+ * bundle with `TAMAGUI_RUNTIME='full'` and its own entry, so this invocation
21
+ * keeps the full runtime and only contributes its CSS fragment.
6
22
  */
7
- cssInterop?: boolean
23
+ zeroIslandBuild?: string
24
+ /**
25
+ * Directory the zero CSS artifact and island bundles are published from,
26
+ * relative to the project root.
27
+ *
28
+ * @default 'public'
29
+ */
30
+ zeroPublicDir?: string
8
31
  }
9
32
 
10
33
  // Use a loose type for metro config to avoid version-specific type incompatibilities
11
34
  type MetroConfigInput = {
35
+ projectRoot?: string
12
36
  resolver?: any
13
37
  transformer?: any
14
38
  transformerPath?: string
15
39
  [key: string]: any
16
40
  }
17
41
 
42
+ const frontends = new WeakMap<object, MetroCompilerFrontend>()
43
+ const { loadTamaguiBuildConfigSync } = Static
44
+ const requireFromPlugin = createRequire(
45
+ typeof __filename === 'string' ? __filename : import.meta.url
46
+ )
47
+
48
+ export function getMetroCompilerFrontend(
49
+ metroConfig: MetroConfigInput
50
+ ): MetroCompilerFrontend | null {
51
+ return frontends.get(metroConfig) ?? null
52
+ }
53
+
18
54
  /**
19
55
  * Configure Metro for Tamagui.
20
56
  *
@@ -42,18 +78,14 @@ export function withTamagui(
42
78
  metroConfig: MetroConfigInput,
43
79
  optionsIn?: MetroTamaguiOptions
44
80
  ): MetroConfigInput {
45
- const { cssInterop, ...tamaguiOptionsIn } = optionsIn || {}
46
-
47
- if (cssInterop) {
48
- console.warn(
49
- '[@tamagui/metro-plugin] cssInterop option is deprecated. Use `tamagui generate` to pre-generate CSS instead.'
50
- )
51
- }
81
+ const {
82
+ compilerCacheRoot,
83
+ zeroIslandBuild,
84
+ zeroPublicDir = 'public',
85
+ ...tamaguiOptionsIn
86
+ } = optionsIn || {}
52
87
 
53
- const options = {
54
- ...tamaguiOptionsIn,
55
- ...loadTamaguiBuildConfigSync(tamaguiOptionsIn),
56
- }
88
+ const options = loadTamaguiBuildConfigSync(tamaguiOptionsIn)
57
89
 
58
90
  // Ensure CSS files can be resolved
59
91
  metroConfig.resolver = {
@@ -67,5 +99,76 @@ export function withTamagui(
67
99
  tamagui: options,
68
100
  }
69
101
 
102
+ const zeroProjectRoot = metroConfig.projectRoot ?? process.cwd()
103
+ const zero = createMetroZeroController(
104
+ options,
105
+ zeroProjectRoot,
106
+ zeroIslandBuild ?? null,
107
+ zeroPublicDir
108
+ )
109
+
110
+ // `report` runs the analysis through the frontend and changes nothing else,
111
+ // so it never installs the serializer that owns the artifact and the gate.
112
+ if (zero?.isEnforcing) {
113
+ applyMetroZeroRuntime(metroConfig, zero)
114
+ }
115
+
116
+ if (!options.disable) {
117
+ const projectRoot = metroConfig.projectRoot ?? process.cwd()
118
+ const requireFromProject = createRequire(join(projectRoot, 'package.json'))
119
+ // getDefaultConfig sets this to the bare specifier 'metro-babel-transformer',
120
+ // and createRequire needs an absolute path, so resolve either shape here
121
+ const configuredBabelTransformerPath =
122
+ metroConfig.transformer.babelTransformerPath ?? 'metro-babel-transformer'
123
+ const originalBabelTransformerPath = isAbsolute(configuredBabelTransformerPath)
124
+ ? configuredBabelTransformerPath
125
+ : requireFromProject.resolve(configuredBabelTransformerPath)
126
+ const cacheBaseRoot = compilerCacheRoot ?? defaultMetroCompilerCacheRoot(projectRoot)
127
+ const frontend = new MetroCompilerFrontend({
128
+ projectRoot,
129
+ resolver: metroConfig.resolver,
130
+ transformer: metroConfig.transformer,
131
+ tamaguiOptions: options,
132
+ originalBabelTransformerPath,
133
+ cacheRoot: cacheBaseRoot,
134
+ zero,
135
+ reportDiagnostic(diagnostic) {
136
+ console.warn(formatMetroCompilerDiagnostic(diagnostic, projectRoot))
137
+ },
138
+ })
139
+ const transformerFactoryPath = requireFromPlugin.resolve(
140
+ '@tamagui/metro-plugin/transformer'
141
+ )
142
+ metroConfig.transformer.babelTransformerPath = writeMetroCompilerTransformerBridge(
143
+ transformerFactoryPath,
144
+ {
145
+ cacheBaseRoot,
146
+ originalBabelTransformerPath,
147
+ projectRoot,
148
+ // an integration-owned literal, never an ambient shell value
149
+ runtimeLiteral: zero?.isEnforcing && !zero.islandBuild ? 'zero' : 'full',
150
+ }
151
+ )
152
+ const userGetTransformOptions = metroConfig.transformer.getTransformOptions
153
+ metroConfig.transformer.getTransformOptions = composeMetroGetTransformOptions(
154
+ frontend,
155
+ userGetTransformOptions
156
+ )
157
+ frontends.set(metroConfig, frontend)
158
+ }
159
+
70
160
  return metroConfig
71
161
  }
162
+
163
+ export {
164
+ METRO_COMPILER_CACHE_VERSION,
165
+ MetroCompilerCache,
166
+ MetroCompilerCacheError,
167
+ defaultMetroCompilerCacheRoot,
168
+ } from './compilerCache'
169
+ export type { MetroCompilerDiagnostic } from './diagnostics'
170
+ export type {
171
+ MetroCompilerGeneration,
172
+ MetroCompilerScanOptions,
173
+ MetroCompilerUpdate,
174
+ } from './frontend'
@@ -0,0 +1,136 @@
1
+ import {
2
+ applyLoweredModule,
3
+ resolvedModuleId,
4
+ type LoweredModulePlan,
5
+ type LoweredModuleStats,
6
+ } from '@tamagui/compiler-core'
7
+ import {
8
+ GREATEST_LOWER_BOUND,
9
+ TraceMap,
10
+ originalPositionFor,
11
+ } from '@jridgewell/trace-mapping'
12
+
13
+ import {
14
+ compileWithUserBabel,
15
+ type CompiledMetroModule,
16
+ type MetroBabelTransformArgs,
17
+ } from './babel'
18
+
19
+ export interface MetroCompilerLoweringResult {
20
+ applied: boolean
21
+ diagnostics: LoweredModulePlan['diagnostics']
22
+ sourceMapComposed: boolean
23
+ stats: LoweredModuleStats
24
+ }
25
+
26
+ interface Position {
27
+ line: number
28
+ column: number
29
+ }
30
+
31
+ function lineStarts(source: string): number[] {
32
+ const starts = [0]
33
+ for (let index = 0; index < source.length; index++) {
34
+ if (source.charCodeAt(index) === 10) starts.push(index + 1)
35
+ }
36
+ return starts
37
+ }
38
+
39
+ function sourceIndex(starts: readonly number[], position: Position): number {
40
+ return (starts[position.line - 1] ?? starts.at(-1) ?? 0) + position.column
41
+ }
42
+
43
+ function tracePosition(loweredMap: TraceMap, position: Position): Position | null {
44
+ const original = originalPositionFor(loweredMap, {
45
+ line: position.line,
46
+ column: position.column,
47
+ bias: GREATEST_LOWER_BOUND,
48
+ })
49
+ return original.line == null || original.column == null
50
+ ? null
51
+ : { line: original.line, column: original.column }
52
+ }
53
+
54
+ function remapAstLocations(
55
+ ast: Record<string, any>,
56
+ loweredMap: TraceMap,
57
+ source: string,
58
+ filename: string
59
+ ): void {
60
+ const starts = lineStarts(source)
61
+ const seen = new Set<object>()
62
+ const visit = (value: unknown) => {
63
+ if (!value || typeof value !== 'object' || seen.has(value as object)) return
64
+ seen.add(value as object)
65
+ if (Array.isArray(value)) {
66
+ for (const child of value) visit(child)
67
+ return
68
+ }
69
+ const node = value as Record<string, any>
70
+ const loc = node.loc
71
+ if (loc?.start && loc?.end) {
72
+ const start = tracePosition(loweredMap, loc.start)
73
+ const end = tracePosition(loweredMap, loc.end)
74
+ if (start && end) {
75
+ node.start = sourceIndex(starts, start)
76
+ node.end = sourceIndex(starts, end)
77
+ node.loc = {
78
+ ...loc,
79
+ start,
80
+ end,
81
+ filename,
82
+ }
83
+ }
84
+ }
85
+ for (const [key, child] of Object.entries(node)) {
86
+ if (key === 'loc' || key === 'tokens') continue
87
+ visit(child)
88
+ }
89
+ }
90
+ visit(ast)
91
+ }
92
+
93
+ /**
94
+ * Applies the cacheable E3 plan to the raw module source, then runs the user's
95
+ * Babel transformer once over the lowered source. Plans carry spans into raw
96
+ * source, so this process's Babel output never needs to match the planning
97
+ * process's byte for byte — Babel options can differ freely between them.
98
+ */
99
+ export async function applyMetroCompilerPlan(
100
+ args: MetroBabelTransformArgs,
101
+ plan: LoweredModulePlan,
102
+ transformerPath: string
103
+ ): Promise<{ compiled: CompiledMetroModule; lowering: MetroCompilerLoweringResult }> {
104
+ const output = applyLoweredModule(args.src, resolvedModuleId(args.filename), plan)
105
+ if (!output.changed || !output.map) {
106
+ return {
107
+ compiled: await compileWithUserBabel(transformerPath, args),
108
+ lowering: {
109
+ applied: false,
110
+ diagnostics: plan.diagnostics,
111
+ sourceMapComposed: false,
112
+ stats: plan.stats,
113
+ },
114
+ }
115
+ }
116
+
117
+ const compiled = await compileWithUserBabel(transformerPath, {
118
+ ...args,
119
+ src: output.code,
120
+ })
121
+ remapAstLocations(
122
+ compiled.result.ast,
123
+ new TraceMap(output.map as any),
124
+ args.src,
125
+ args.filename
126
+ )
127
+ return {
128
+ compiled,
129
+ lowering: {
130
+ applied: true,
131
+ diagnostics: plan.diagnostics,
132
+ sourceMapComposed: true,
133
+ stats: plan.stats,
134
+ },
135
+ }
136
+ }
@@ -0,0 +1,209 @@
1
+ import { existsSync, lstatSync, readFileSync, realpathSync, statSync } from 'node:fs'
2
+ import { createRequire } from 'node:module'
3
+ import { dirname, extname, isAbsolute, join, parse, relative, resolve } from 'node:path'
4
+
5
+ export interface MetroResolverConfig {
6
+ projectRoot: string
7
+ resolver?: Record<string, any>
8
+ }
9
+
10
+ export interface MetroResolvedDependency {
11
+ specifier: string
12
+ resolvedId: string
13
+ external: boolean
14
+ }
15
+
16
+ export interface MetroModuleSpecifier {
17
+ specifier: string
18
+ isESMImport: boolean
19
+ }
20
+
21
+ type MetroResolve = (
22
+ context: Record<string, any>,
23
+ specifier: string,
24
+ platform: string | null
25
+ ) => { type: string; filePath?: string }
26
+
27
+ function compareCodeUnits(left: string, right: string): number {
28
+ return left < right ? -1 : left > right ? 1 : 0
29
+ }
30
+
31
+ function findClosestPackage(path: string): {
32
+ rootPath: string
33
+ packageJson: Record<string, any>
34
+ packageRelativePath: string
35
+ } | null {
36
+ let directory = existsSync(path) && statSync(path).isDirectory() ? path : dirname(path)
37
+ const root = parse(directory).root
38
+ while (true) {
39
+ const packagePath = join(directory, 'package.json')
40
+ if (existsSync(packagePath)) {
41
+ try {
42
+ return {
43
+ rootPath: directory,
44
+ packageJson: JSON.parse(readFileSync(packagePath, 'utf8')),
45
+ packageRelativePath: relative(directory, path),
46
+ }
47
+ } catch {
48
+ return null
49
+ }
50
+ }
51
+ if (directory === root || directory.endsWith(`${join('node_modules')}`)) {
52
+ return null
53
+ }
54
+ directory = dirname(directory)
55
+ }
56
+ }
57
+
58
+ function lookup(projectRoot: string, path: string) {
59
+ const absolutePath = isAbsolute(path) ? path : resolve(projectRoot, path)
60
+ try {
61
+ const stat = lstatSync(absolutePath)
62
+ const realPath = realpathSync(absolutePath)
63
+ return {
64
+ exists: true as const,
65
+ type: (stat.isDirectory() ? 'd' : 'f') as 'd' | 'f',
66
+ realPath,
67
+ }
68
+ } catch {
69
+ return { exists: false as const }
70
+ }
71
+ }
72
+
73
+ export function moduleSpecifiersFromAst(ast: unknown): MetroModuleSpecifier[] {
74
+ const specifiers = new Map<string, boolean>()
75
+ const seen = new Set<object>()
76
+
77
+ function add(value: unknown, isESMImport: boolean): void {
78
+ if (typeof value !== 'string') return
79
+ specifiers.set(value, (specifiers.get(value) ?? false) || isESMImport)
80
+ }
81
+
82
+ function visit(value: unknown): void {
83
+ if (!value || typeof value !== 'object') return
84
+ if (seen.has(value)) return
85
+ seen.add(value)
86
+ if (Array.isArray(value)) {
87
+ for (const child of value) visit(child)
88
+ return
89
+ }
90
+ const node = value as Record<string, any>
91
+ if (
92
+ node.type === 'ImportDeclaration' ||
93
+ node.type === 'ExportAllDeclaration' ||
94
+ node.type === 'ExportNamedDeclaration'
95
+ ) {
96
+ add(node.source?.value, true)
97
+ } else if (node.type === 'CallExpression') {
98
+ const first = node.arguments?.[0]
99
+ if (node.callee?.type === 'Import') {
100
+ add(first?.value, true)
101
+ } else if (node.callee?.type === 'Identifier' && node.callee.name === 'require') {
102
+ add(first?.value, false)
103
+ }
104
+ }
105
+ for (const [key, child] of Object.entries(node)) {
106
+ if (key === 'loc' || key === 'comments' || key === 'tokens') continue
107
+ visit(child)
108
+ }
109
+ }
110
+
111
+ visit(ast)
112
+ return [...specifiers]
113
+ .sort(([left], [right]) => compareCodeUnits(left, right))
114
+ .map(([specifier, isESMImport]) => ({ specifier, isESMImport }))
115
+ }
116
+
117
+ export function createMetroCompilerResolver(config: MetroResolverConfig): {
118
+ version: string
119
+ resolve(
120
+ importer: string,
121
+ dependency: MetroModuleSpecifier,
122
+ platform: string | null
123
+ ): MetroResolvedDependency | null
124
+ } {
125
+ const requireFromProject = createRequire(join(config.projectRoot, 'package.json'))
126
+ const resolverPackage = requireFromProject('metro-resolver/package.json') as {
127
+ version: string
128
+ }
129
+ if (!resolverPackage.version.startsWith('0.84.')) {
130
+ throw new Error(
131
+ `@tamagui/metro-plugin requires the Metro 0.84 resolver contract, found ${resolverPackage.version}`
132
+ )
133
+ }
134
+ const resolverModule = requireFromProject('metro-resolver')
135
+ const metroResolve: MetroResolve = resolverModule.resolve
136
+ const createDefaultContextModule = requireFromProject(
137
+ 'metro-resolver/private/createDefaultContext'
138
+ )
139
+ const createDefaultContext =
140
+ createDefaultContextModule.default ?? createDefaultContextModule
141
+ const resolver = config.resolver ?? {}
142
+ const fileSystemLookup = (path: string) => lookup(config.projectRoot, path)
143
+ const getPackage = (packagePath: string) => {
144
+ try {
145
+ return JSON.parse(readFileSync(packagePath, 'utf8'))
146
+ } catch {
147
+ return null
148
+ }
149
+ }
150
+
151
+ return {
152
+ version: resolverPackage.version,
153
+ resolve(importer, dependency, platform) {
154
+ const dependencyDescriptor = {
155
+ name: dependency.specifier,
156
+ data: {
157
+ asyncType: null,
158
+ isESMImport: dependency.isESMImport,
159
+ key: dependency.specifier,
160
+ locs: [],
161
+ },
162
+ }
163
+ const context = createDefaultContext(
164
+ {
165
+ allowHaste: false,
166
+ assetExts: new Set(resolver.assetExts ?? []),
167
+ customResolverOptions: {},
168
+ dev: true,
169
+ disableHierarchicalLookup: resolver.disableHierarchicalLookup ?? false,
170
+ doesFileExist: (path: string) => fileSystemLookup(path).type === 'f',
171
+ extraNodeModules: resolver.extraNodeModules ?? null,
172
+ fileSystemLookup,
173
+ getPackage,
174
+ getPackageForModule: findClosestPackage,
175
+ isESMImport: dependency.isESMImport,
176
+ mainFields: resolver.resolverMainFields ?? ['react-native', 'browser', 'main'],
177
+ nodeModulesPaths: resolver.nodeModulesPaths ?? [],
178
+ originModulePath: importer,
179
+ preferNativePlatform: true,
180
+ projectRoot: config.projectRoot,
181
+ resolveAsset: () => null,
182
+ resolveHasteModule: () => null,
183
+ resolveHastePackage: () => null,
184
+ resolveRequest: resolver.resolveRequest ?? null,
185
+ sourceExts: resolver.sourceExts ?? ['js', 'jsx', 'json', 'ts', 'tsx'],
186
+ unstable_conditionNames: resolver.unstable_conditionNames ?? [],
187
+ unstable_conditionsByPlatform: resolver.unstable_conditionsByPlatform ?? {},
188
+ unstable_enablePackageExports: resolver.unstable_enablePackageExports ?? true,
189
+ unstable_logWarning: (message: string) =>
190
+ config.resolver?.unstable_logWarning?.(message),
191
+ },
192
+ dependencyDescriptor
193
+ )
194
+ const result = metroResolve(context, dependency.specifier, platform)
195
+ if (result.type === 'empty') return null
196
+ if (result.type !== 'sourceFile' || !result.filePath) return null
197
+ const resolvedId = realpathSync(result.filePath)
198
+ return {
199
+ specifier: dependency.specifier,
200
+ resolvedId,
201
+ external: resolvedId.includes(`${join('node_modules')}`),
202
+ }
203
+ },
204
+ }
205
+ }
206
+
207
+ export function isCompilerSourceFile(path: string): boolean {
208
+ return ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'].includes(extname(path))
209
+ }
@@ -0,0 +1,36 @@
1
+ import type { MetroCompilerFrontend } from './frontend'
2
+
3
+ export interface MetroGetTransformOptionsContext {
4
+ dev: boolean
5
+ hot: boolean
6
+ platform: string | null
7
+ }
8
+
9
+ export type MetroGetTransformOptions = (
10
+ this: unknown,
11
+ entryFiles: string[],
12
+ transformOptions: MetroGetTransformOptionsContext,
13
+ getDependencies: (path: string) => Promise<string[]>
14
+ ) => Promise<{ transform?: Record<string, any>; [key: string]: any }>
15
+
16
+ export function composeMetroGetTransformOptions(
17
+ frontend: Pick<MetroCompilerFrontend, 'ensureValidCache'>,
18
+ userGetTransformOptions?: MetroGetTransformOptions
19
+ ): MetroGetTransformOptions {
20
+ return async function (this: unknown, entryFiles, transformOptions, getDependencies) {
21
+ const userOptions = userGetTransformOptions
22
+ ? await userGetTransformOptions.call(
23
+ this,
24
+ entryFiles,
25
+ transformOptions,
26
+ getDependencies
27
+ )
28
+ : { transform: {} }
29
+ await frontend.ensureValidCache({
30
+ ...transformOptions,
31
+ entryFiles,
32
+ transform: userOptions.transform ?? {},
33
+ })
34
+ return userOptions
35
+ }
36
+ }