@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.
- package/README.md +12 -0
- package/dist/cjs/babel.cjs +77 -0
- package/dist/cjs/compilerCache.cjs +237 -0
- package/dist/cjs/diagnostics.cjs +41 -0
- package/dist/cjs/frontend.cjs +870 -0
- package/dist/cjs/index.cjs +102 -0
- package/dist/cjs/lowering.cjs +109 -0
- package/dist/cjs/metroResolver.cjs +197 -0
- package/dist/cjs/transformOptions.cjs +35 -0
- package/dist/cjs/transformer.cjs +142 -0
- package/dist/cjs/zeroRuntime.cjs +140 -0
- package/dist/cjs/zeroSerializer.cjs +150 -0
- package/dist/esm/babel.mjs +52 -0
- package/dist/esm/babel.mjs.map +1 -0
- package/dist/esm/compilerCache.mjs +212 -0
- package/dist/esm/compilerCache.mjs.map +1 -0
- package/dist/esm/diagnostics.mjs +18 -0
- package/dist/esm/diagnostics.mjs.map +1 -0
- package/dist/esm/frontend.mjs +839 -0
- package/dist/esm/frontend.mjs.map +1 -0
- package/dist/esm/index.mjs +64 -22
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/lowering.mjs +89 -0
- package/dist/esm/lowering.mjs.map +1 -0
- package/dist/esm/metroResolver.mjs +173 -0
- package/dist/esm/metroResolver.mjs.map +1 -0
- package/dist/esm/transformOptions.mjs +14 -0
- package/dist/esm/transformOptions.mjs.map +1 -0
- package/dist/esm/transformer.mjs +119 -0
- package/dist/esm/transformer.mjs.map +1 -0
- package/dist/esm/zeroRuntime.mjs +105 -0
- package/dist/esm/zeroRuntime.mjs.map +1 -0
- package/dist/esm/zeroSerializer.mjs +123 -0
- package/dist/esm/zeroSerializer.mjs.map +1 -0
- package/package.json +33 -5
- package/src/babel.ts +87 -0
- package/src/compilerCache.ts +346 -0
- package/src/diagnostics.ts +47 -0
- package/src/frontend.ts +1178 -0
- package/src/index.ts +117 -14
- package/src/lowering.ts +136 -0
- package/src/metroResolver.ts +209 -0
- package/src/transformOptions.ts +36 -0
- package/src/transformer.ts +210 -0
- package/src/zeroRuntime.ts +212 -0
- package/src/zeroSerializer.ts +175 -0
- package/types/babel.d.ts +28 -0
- package/types/babel.d.ts.map +11 -0
- package/types/compilerCache.d.ts +63 -0
- package/types/compilerCache.d.ts.map +11 -0
- package/types/diagnostics.d.ts +16 -0
- package/types/diagnostics.d.ts.map +11 -0
- package/types/frontend.d.ts +73 -0
- package/types/frontend.d.ts.map +11 -0
- package/types/index.d.ts +49 -32
- package/types/index.d.ts.map +11 -1
- package/types/lowering.d.ts +20 -0
- package/types/lowering.d.ts.map +11 -0
- package/types/metroResolver.d.ts +21 -0
- package/types/metroResolver.d.ts.map +11 -0
- package/types/transformOptions.d.ts +13 -0
- package/types/transformOptions.d.ts.map +11 -0
- package/types/transformer.d.ts +26 -0
- package/types/transformer.d.ts.map +11 -0
- package/types/zeroRuntime.d.ts +75 -0
- package/types/zeroRuntime.d.ts.map +11 -0
- package/types/zeroSerializer.d.ts +6 -0
- package/types/zeroSerializer.d.ts.map +11 -0
- package/dist/cjs/index.js +0 -45
- package/dist/cjs/index.js.map +0 -6
- package/dist/esm/index.js +0 -25
- package/dist/esm/index.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,20 +1,56 @@
|
|
|
1
|
-
import {
|
|
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
|
-
*
|
|
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
|
-
|
|
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 {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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'
|
package/src/lowering.ts
ADDED
|
@@ -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
|
+
}
|