zyzz 0.0.21 → 0.0.23
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/Zyzz.podspec +16 -0
- package/dist/Style.d.ts +3 -1
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js.map +1 -1
- package/dist/babel/NativeJsx.d.ts.map +1 -1
- package/dist/babel/NativeJsx.js +217 -15
- package/dist/babel/NativeJsx.js.map +1 -1
- package/dist/babel/index.d.ts +6 -2
- package/dist/babel/index.d.ts.map +1 -1
- package/dist/babel/index.js +10 -1
- package/dist/babel/index.js.map +1 -1
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +107 -0
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +6 -3
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +142 -24
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +2 -0
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +17 -5
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts +2 -0
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +21 -3
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +75 -38
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/Contributions.d.ts.map +1 -1
- package/dist/compiler/internal/Contributions.js +7 -1
- package/dist/compiler/internal/Contributions.js.map +1 -1
- package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
- package/dist/compiler/internal/Descriptors.js +11 -2
- package/dist/compiler/internal/Descriptors.js.map +1 -1
- package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
- package/dist/compiler/internal/Dynamic.js +3 -0
- package/dist/compiler/internal/Dynamic.js.map +1 -1
- package/dist/compiler/internal/Expression.js +2 -2
- package/dist/compiler/internal/Expression.js.map +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts +24 -26
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +62 -8
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/NativeMedia.d.ts +14 -0
- package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
- package/dist/compiler/internal/NativeMedia.js +287 -0
- package/dist/compiler/internal/NativeMedia.js.map +1 -0
- package/dist/compiler/internal/NativeVars.d.ts +28 -0
- package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
- package/dist/compiler/internal/NativeVars.js +160 -0
- package/dist/compiler/internal/NativeVars.js.map +1 -0
- package/dist/compiler/internal/PackedStyles.d.ts +2 -0
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +2 -0
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +24 -1
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +120 -3
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/compiler/internal/Vars.d.ts +1 -1
- package/dist/compiler/internal/Vars.d.ts.map +1 -1
- package/dist/compiler/internal/Vars.js +7 -2
- package/dist/compiler/internal/Vars.js.map +1 -1
- package/dist/default.js.zyzz.json +1 -1
- package/dist/internal/Completion.d.ts +1 -1
- package/dist/internal/Completion.d.ts.map +1 -1
- package/dist/internal/Targets.d.ts.map +1 -1
- package/dist/internal/Targets.js +2 -0
- package/dist/internal/Targets.js.map +1 -1
- package/dist/internal/Token.d.ts +0 -2
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Value.d.ts +7 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/metro/Graph.d.ts +22 -7
- package/dist/metro/Graph.d.ts.map +1 -1
- package/dist/metro/Graph.js +188 -42
- package/dist/metro/Graph.js.map +1 -1
- package/dist/metro/Watch.d.ts +28 -4
- package/dist/metro/Watch.d.ts.map +1 -1
- package/dist/metro/Watch.js +39 -7
- package/dist/metro/Watch.js.map +1 -1
- package/dist/metro/index.d.ts +15 -4
- package/dist/metro/index.d.ts.map +1 -1
- package/dist/metro/index.js +27 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/metro/transformer.d.ts +5 -1
- package/dist/metro/transformer.d.ts.map +1 -1
- package/dist/metro/transformer.js +5 -3
- package/dist/metro/transformer.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +8 -3
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +25 -34
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/index.d.ts +1 -1
- package/dist/react-native/index.d.ts.map +1 -1
- package/dist/react-native/index.js +1 -1
- package/dist/react-native/index.js.map +1 -1
- package/dist/react-native/index.native.d.ts +5 -0
- package/dist/react-native/index.native.js +20 -0
- package/dist/react-native/index.native.js.map +7 -0
- package/dist/react-native/internal/Calculation.d.ts +7 -0
- package/dist/react-native/internal/Calculation.d.ts.map +1 -0
- package/dist/react-native/internal/Calculation.js +134 -0
- package/dist/react-native/internal/Calculation.js.map +1 -0
- package/dist/react-native/internal/Device.js +218 -0
- package/dist/react-native/internal/Device.js.map +7 -0
- package/dist/react-native/internal/NativeZyzz.js +7 -0
- package/dist/react-native/internal/NativeZyzz.js.map +7 -0
- package/dist/react-native/internal/Scalar.d.ts +16 -0
- package/dist/react-native/internal/Scalar.d.ts.map +1 -1
- package/dist/react-native/internal/Scalar.js +62 -4
- package/dist/react-native/internal/Scalar.js.map +1 -1
- package/dist/react-native/internal/Store.d.ts +32 -0
- package/dist/react-native/internal/Store.d.ts.map +1 -0
- package/dist/react-native/internal/Store.js +57 -0
- package/dist/react-native/internal/Store.js.map +1 -0
- package/dist/react-native/internal/Styles.d.ts +19 -0
- package/dist/react-native/internal/Styles.d.ts.map +1 -0
- package/dist/react-native/internal/Styles.js +35 -0
- package/dist/react-native/internal/Styles.js.map +1 -0
- package/dist/react-native/internal/Tokens.d.ts +21 -0
- package/dist/react-native/internal/Tokens.d.ts.map +1 -0
- package/dist/react-native/internal/Tokens.js +58 -0
- package/dist/react-native/internal/Tokens.js.map +1 -0
- package/dist/react-native/internal/Viewport.d.ts +6 -0
- package/dist/react-native/internal/Viewport.d.ts.map +1 -0
- package/dist/react-native/internal/Viewport.js +4 -0
- package/dist/react-native/internal/Viewport.js.map +1 -0
- package/dist/react-native/react.d.ts +102 -14
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +86 -18
- package/dist/react-native/react.js.map +1 -1
- package/dist/react-native/reanimated.d.ts +39 -0
- package/dist/react-native/reanimated.d.ts.map +1 -0
- package/dist/react-native/reanimated.js +76 -0
- package/dist/react-native/reanimated.js.map +1 -0
- package/dist/runtime/NativeContext.d.ts +10 -1
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +67 -14
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts +3 -2
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js +37 -7
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/dist/runtime/NativeStatic.d.ts +23 -0
- package/dist/runtime/NativeStatic.d.ts.map +1 -0
- package/dist/runtime/NativeStatic.js +106 -0
- package/dist/runtime/NativeStatic.js.map +1 -0
- package/dist/runtime/NativeVars.d.ts +53 -0
- package/dist/runtime/NativeVars.d.ts.map +1 -0
- package/dist/runtime/NativeVars.js +77 -0
- package/dist/runtime/NativeVars.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/internal/NativeMedia.d.ts +25 -0
- package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
- package/dist/runtime/internal/NativeMedia.js +36 -0
- package/dist/runtime/internal/NativeMedia.js.map +1 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +7 -35
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +31 -19
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +0 -2
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +30 -28
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/web/page.d.ts +2 -0
- package/dist/web/page.d.ts.map +1 -1
- package/native/NativeZyzz.cpp +215 -0
- package/native/NativeZyzz.h +52 -0
- package/native/ZyzzSpecJSI.h +65 -0
- package/native/android/CMakeLists.txt +8 -0
- package/native/android/ZyzzSpec-generated.cpp +50 -0
- package/native/android/ZyzzSpec.h +31 -0
- package/native/ios/ZyzzModuleProvider.mm +13 -0
- package/package.json +39 -1
- package/react-native.config.ts +14 -0
- package/src/Style.ts +11 -8
- package/src/babel/NativeJsx.ts +319 -33
- package/src/babel/index.ts +21 -3
- package/src/compiler/Graph.ts +126 -0
- package/src/compiler/Native.ts +158 -26
- package/src/compiler/Source.ts +32 -5
- package/src/compiler/internal/Configurations.ts +25 -3
- package/src/compiler/internal/Contract.ts +98 -50
- package/src/compiler/internal/Contributions.ts +7 -1
- package/src/compiler/internal/Descriptors.ts +12 -8
- package/src/compiler/internal/Dynamic.ts +4 -0
- package/src/compiler/internal/Expression.ts +2 -2
- package/src/compiler/internal/NativeBindings.ts +99 -15
- package/src/compiler/internal/NativeMedia.ts +342 -0
- package/src/compiler/internal/NativeVars.ts +231 -0
- package/src/compiler/internal/PackedStyles.ts +4 -0
- package/src/compiler/internal/Themes.ts +193 -2
- package/src/compiler/internal/Vars.ts +12 -1
- package/src/internal/Completion.ts +6 -3
- package/src/internal/Targets.ts +2 -0
- package/src/internal/Token.ts +0 -2
- package/src/internal/Value.ts +20 -0
- package/src/metro/Graph.ts +250 -50
- package/src/metro/Watch.ts +95 -10
- package/src/metro/index.ts +62 -4
- package/src/metro/transformer.ts +21 -11
- package/src/react-native/StyleSheet.ts +56 -49
- package/src/react-native/index.native.ts +10 -0
- package/src/react-native/index.ts +1 -1
- package/src/react-native/internal/Calculation.ts +159 -0
- package/src/react-native/internal/Device.ts +297 -0
- package/src/react-native/internal/NativeZyzz.ts +20 -0
- package/src/react-native/internal/Scalar.ts +86 -4
- package/src/react-native/internal/Store.ts +78 -0
- package/src/react-native/internal/Styles.ts +77 -0
- package/src/react-native/internal/Tokens.ts +85 -0
- package/src/react-native/internal/Viewport.ts +8 -0
- package/src/react-native/react.ts +218 -26
- package/src/react-native/reanimated.ts +162 -0
- package/src/runtime/NativeContext.ts +88 -17
- package/src/runtime/NativeDynamic.ts +42 -10
- package/src/runtime/NativeStatic.ts +145 -0
- package/src/runtime/NativeVars.ts +189 -0
- package/src/runtime/index.ts +6 -0
- package/src/runtime/internal/NativeMedia.ts +63 -0
- package/src/vite/index.ts +9 -42
- package/src/web/Css.ts +42 -27
- package/src/web/internal/Themes.ts +34 -57
- package/src/web/page.ts +2 -0
package/src/metro/Graph.ts
CHANGED
|
@@ -1,80 +1,280 @@
|
|
|
1
|
-
/** Reads
|
|
1
|
+
/** Reads resolved source and packed native authoring without evaluating modules. @module */
|
|
2
|
+
import type * as Ast from '@oxc-project/types'
|
|
3
|
+
import type * as Compiler from '../compiler/Graph.js'
|
|
2
4
|
import * as Fs from 'node:fs'
|
|
3
5
|
import * as Path from 'node:path'
|
|
4
6
|
import * as Snapshot from '../node/internal/Snapshot.js'
|
|
5
|
-
import type * as Graph from '../compiler/Graph.js'
|
|
6
7
|
|
|
7
|
-
/**
|
|
8
|
+
/** Serializable source snapshot delivered to a Metro transformer worker. */
|
|
9
|
+
export type Input = Pick<
|
|
10
|
+
Compiler.compile.Options,
|
|
11
|
+
'contracts' | 'imports' | 'modules'
|
|
12
|
+
> & {
|
|
13
|
+
/** Files and sidecars whose edits invalidate this consumer. */
|
|
14
|
+
readonly files: readonly string[]
|
|
15
|
+
/** Portable compiler identity for the requesting module. */
|
|
16
|
+
readonly moduleId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Collects authoring through Metro resolutions, retaining packed contracts and watched inputs. */
|
|
8
20
|
export function read(
|
|
9
21
|
filename: string,
|
|
10
22
|
source: string,
|
|
11
23
|
platform: string,
|
|
12
24
|
root: string,
|
|
13
25
|
snapshot = Snapshot.create(),
|
|
14
|
-
|
|
26
|
+
resolve?: Resolver,
|
|
27
|
+
): read.ReturnType {
|
|
28
|
+
const contracts: Record<string, string> = Object.create(null)
|
|
15
29
|
const files = new Set<string>()
|
|
16
|
-
const modules: Record<string, string> = Object.create(null)
|
|
17
30
|
const imports: Record<string, Record<string, string | null>> = Object.create(
|
|
18
31
|
null,
|
|
19
32
|
)
|
|
33
|
+
const modules: Record<string, string> = Object.create(null)
|
|
34
|
+
|
|
20
35
|
function identity(filename: string) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
const parts = Path.relative(root, filename).split(Path.sep)
|
|
37
|
+
let parents = 0
|
|
38
|
+
while (parts[0] === '..') {
|
|
39
|
+
parents++
|
|
40
|
+
parts.shift()
|
|
41
|
+
}
|
|
42
|
+
return `${parents ? `external/${parents}` : 'project'}/${parts.map(encodeURIComponent).join('/')}`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function local(filename: string): boolean {
|
|
46
|
+
const relative = Path.relative(root, filename)
|
|
47
|
+
return (
|
|
48
|
+
relative !== '..' &&
|
|
49
|
+
!relative.startsWith(`..${Path.sep}`) &&
|
|
50
|
+
!Path.isAbsolute(relative) &&
|
|
51
|
+
!relative.split(Path.sep).includes('node_modules')
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function manifest(filename: string): string | undefined {
|
|
56
|
+
let directory = Path.dirname(filename)
|
|
57
|
+
while (true) {
|
|
58
|
+
const file = Path.join(directory, 'package.json')
|
|
59
|
+
files.add(file)
|
|
60
|
+
if (Fs.existsSync(file)) return file
|
|
61
|
+
|
|
62
|
+
const parent = Path.dirname(directory)
|
|
63
|
+
if (parent === directory) return undefined
|
|
64
|
+
directory = parent
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function target(
|
|
69
|
+
filename: string,
|
|
70
|
+
specifier: string,
|
|
71
|
+
isESMImport = true,
|
|
72
|
+
): string | undefined {
|
|
73
|
+
if (resolve) {
|
|
74
|
+
const next = resolve(filename, specifier, isESMImport)
|
|
75
|
+
if (next && !specifier.startsWith('.')) manifest(next)
|
|
76
|
+
return next
|
|
77
|
+
}
|
|
78
|
+
if (!specifier.startsWith('.')) return undefined
|
|
79
|
+
const base = Path.resolve(Path.dirname(filename), specifier)
|
|
80
|
+
const stem = base.replace(/\.[cm]?jsx?$/, '')
|
|
81
|
+
const candidates = [
|
|
82
|
+
...['.ts', '.tsx', '.js', '.jsx'].flatMap((extension) => [
|
|
83
|
+
`${stem}.${platform}${extension}`,
|
|
84
|
+
`${stem}.native${extension}`,
|
|
85
|
+
`${stem}${extension}`,
|
|
86
|
+
]),
|
|
87
|
+
base,
|
|
88
|
+
...['.ts', '.tsx', '.js', '.jsx'].map((extension) =>
|
|
89
|
+
Path.join(base, `index${extension}`),
|
|
90
|
+
),
|
|
91
|
+
]
|
|
92
|
+
for (const candidate of candidates) files.add(candidate)
|
|
93
|
+
return candidates.find(
|
|
94
|
+
(path) => Fs.existsSync(path) && Fs.statSync(path).isFile(),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function specifier(node: Ast.Program['body'][number]): string | undefined {
|
|
99
|
+
if (
|
|
100
|
+
(node.type !== 'ImportDeclaration' &&
|
|
101
|
+
node.type !== 'ExportAllDeclaration' &&
|
|
102
|
+
node.type !== 'ExportNamedDeclaration') ||
|
|
103
|
+
!node.source ||
|
|
104
|
+
('importKind' in node && node.importKind === 'type') ||
|
|
105
|
+
('exportKind' in node && node.exportKind === 'type') ||
|
|
106
|
+
('specifiers' in node &&
|
|
107
|
+
node.specifiers.length > 0 &&
|
|
108
|
+
node.specifiers.every(
|
|
109
|
+
(specifier) =>
|
|
110
|
+
('importKind' in specifier && specifier.importKind === 'type') ||
|
|
111
|
+
('exportKind' in specifier && specifier.exportKind === 'type'),
|
|
112
|
+
))
|
|
113
|
+
)
|
|
114
|
+
return undefined
|
|
115
|
+
|
|
116
|
+
return node.source.value
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function requires(node: unknown, names: Map<string, boolean>): void {
|
|
120
|
+
if (!node || typeof node !== 'object') return
|
|
121
|
+
if (Array.isArray(node)) {
|
|
122
|
+
for (const child of node) requires(child, names)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const entry = node as Ast.Node
|
|
127
|
+
if (
|
|
128
|
+
entry.type === 'CallExpression' &&
|
|
129
|
+
entry.callee.type === 'Identifier' &&
|
|
130
|
+
entry.callee.name === 'require' &&
|
|
131
|
+
entry.arguments.length === 1
|
|
132
|
+
) {
|
|
133
|
+
const argument = entry.arguments[0]!
|
|
134
|
+
if (argument.type === 'Literal' && typeof argument.value === 'string')
|
|
135
|
+
names.set(argument.value, false)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const child of Object.values(node)) requires(child, names)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const authored = new Set<string>()
|
|
142
|
+
function authoring(filename: string, seen = new Set<string>()): boolean {
|
|
143
|
+
files.add(filename)
|
|
144
|
+
files.add(`${filename}.zyzz.json`)
|
|
145
|
+
if (Fs.existsSync(`${filename}.zyzz.json`)) return true
|
|
146
|
+
if (authored.has(filename)) return true
|
|
147
|
+
if (seen.has(filename)) return false
|
|
148
|
+
seen.add(filename)
|
|
149
|
+
const source = snapshot.readSync(filename)
|
|
150
|
+
const file = manifest(filename)
|
|
151
|
+
if (
|
|
152
|
+
file &&
|
|
153
|
+
!/['"]zyzz(?:\/default|\/react-native(?:\/react)?)?['"]/.test(source)
|
|
154
|
+
) {
|
|
155
|
+
const data = JSON.parse(snapshot.readSync(file)) as Record<
|
|
156
|
+
string,
|
|
157
|
+
unknown
|
|
158
|
+
>
|
|
159
|
+
if (
|
|
160
|
+
![
|
|
161
|
+
'dependencies',
|
|
162
|
+
'devDependencies',
|
|
163
|
+
'optionalDependencies',
|
|
164
|
+
'peerDependencies',
|
|
165
|
+
].some((key) => {
|
|
166
|
+
const dependencies = data[key]
|
|
167
|
+
return (
|
|
168
|
+
dependencies &&
|
|
169
|
+
typeof dependencies === 'object' &&
|
|
170
|
+
Object.hasOwn(dependencies, 'zyzz')
|
|
171
|
+
)
|
|
172
|
+
})
|
|
25
173
|
)
|
|
26
|
-
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const node of snapshot.parse({ moduleId: identity(filename), source })
|
|
178
|
+
.program.body) {
|
|
179
|
+
const name = specifier(node)
|
|
180
|
+
// Consumers import vars profiles from defineConfig modules, so those modules must compile.
|
|
181
|
+
const native =
|
|
182
|
+
(name === 'zyzz/react-native' || name === 'zyzz/react-native/react') &&
|
|
183
|
+
node.type === 'ImportDeclaration' &&
|
|
184
|
+
node.specifiers.some(
|
|
185
|
+
(entry) =>
|
|
186
|
+
entry.type === 'ImportSpecifier' &&
|
|
187
|
+
entry.importKind !== 'type' &&
|
|
188
|
+
(entry.imported.type === 'Identifier'
|
|
189
|
+
? entry.imported.name
|
|
190
|
+
: entry.imported.value) === 'defineConfig',
|
|
191
|
+
)
|
|
192
|
+
if (name === 'zyzz' || name === 'zyzz/default' || native) {
|
|
193
|
+
authored.add(filename)
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
if (!name || name.startsWith('zyzz/')) continue
|
|
197
|
+
const next = target(filename, name)
|
|
198
|
+
if (next && /\.[cm]?[jt]sx?$/.test(next) && authoring(next, seen)) {
|
|
199
|
+
authored.add(filename)
|
|
200
|
+
return true
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false
|
|
27
204
|
}
|
|
28
|
-
|
|
205
|
+
const visited = new Set<string>()
|
|
206
|
+
function visit(filename: string, source: string, packed = false) {
|
|
29
207
|
files.add(filename)
|
|
30
208
|
const id = identity(filename)
|
|
31
|
-
if (
|
|
32
|
-
|
|
209
|
+
if (visited.has(id)) return
|
|
210
|
+
visited.add(id)
|
|
211
|
+
if (!packed) modules[id] = source
|
|
33
212
|
const resolved: Record<string, string | null> = Object.create(null)
|
|
34
213
|
imports[id] = resolved
|
|
35
|
-
|
|
36
|
-
|
|
214
|
+
const program = snapshot.parse({ moduleId: id, source }).program
|
|
215
|
+
const names = new Map<string, boolean>()
|
|
216
|
+
for (const statement of program.body) {
|
|
217
|
+
const name = specifier(statement)
|
|
218
|
+
if (name) names.set(name, true)
|
|
219
|
+
}
|
|
220
|
+
if (packed) requires(program, names)
|
|
221
|
+
|
|
222
|
+
for (const [name, isESMImport] of names) {
|
|
223
|
+
resolved[name] = null
|
|
37
224
|
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
!('value' in statement.source) ||
|
|
42
|
-
typeof statement.source.value !== 'string'
|
|
225
|
+
name === 'zyzz' ||
|
|
226
|
+
name.startsWith('zyzz/') ||
|
|
227
|
+
['react', 'react-native'].includes(name)
|
|
43
228
|
)
|
|
44
229
|
continue
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
),
|
|
60
|
-
]
|
|
61
|
-
for (const candidate of candidates) files.add(candidate)
|
|
62
|
-
const target = candidates.find(
|
|
63
|
-
(path) => Fs.existsSync(path) && Fs.statSync(path).isFile(),
|
|
64
|
-
)
|
|
65
|
-
if (!target || !/\.[cm]?[jt]sx?$/.test(target)) continue
|
|
66
|
-
resolved[specifier] = identity(target)
|
|
67
|
-
visit(target, snapshot.readSync(target))
|
|
230
|
+
const next = target(filename, name, isESMImport)
|
|
231
|
+
if (!next || !/\.[cm]?[jt]sx?$/.test(next)) continue
|
|
232
|
+
const packed = `${next}.zyzz.json`
|
|
233
|
+
files.add(packed)
|
|
234
|
+
if (Fs.existsSync(packed)) {
|
|
235
|
+
files.add(next)
|
|
236
|
+
contracts[identity(next)] = snapshot.readSync(packed)
|
|
237
|
+
resolved[name] = identity(next)
|
|
238
|
+
visit(next, snapshot.readSync(next), true)
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
if (!name.startsWith('.') && !local(next) && !authoring(next)) continue
|
|
242
|
+
resolved[name] = identity(next)
|
|
243
|
+
visit(next, snapshot.readSync(next))
|
|
68
244
|
}
|
|
69
245
|
}
|
|
246
|
+
if (!local(filename) && !authoring(filename))
|
|
247
|
+
return { files: [...files], input: undefined }
|
|
248
|
+
|
|
70
249
|
visit(filename, source)
|
|
250
|
+
|
|
251
|
+
const watched = [...files]
|
|
71
252
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
253
|
+
files: watched,
|
|
254
|
+
input: {
|
|
255
|
+
contracts,
|
|
256
|
+
files: watched,
|
|
257
|
+
imports,
|
|
258
|
+
moduleId: identity(filename),
|
|
259
|
+
modules,
|
|
260
|
+
},
|
|
79
261
|
}
|
|
80
262
|
}
|
|
263
|
+
|
|
264
|
+
/** Source classification and all paths that can change its result. */
|
|
265
|
+
export declare namespace read {
|
|
266
|
+
/** Candidate files remain watchable even when the module currently needs no compilation. */
|
|
267
|
+
type ReturnType = {
|
|
268
|
+
/** Sources, manifests, and sidecar candidates that affect classification. */
|
|
269
|
+
readonly files: readonly string[]
|
|
270
|
+
/** Compiler input when the module belongs to the authoring graph. */
|
|
271
|
+
readonly input: Input | undefined
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Source resolution supplied by Metro's live dependency graph. */
|
|
276
|
+
export type Resolver = (
|
|
277
|
+
filename: string,
|
|
278
|
+
specifier: string,
|
|
279
|
+
isESMImport: boolean,
|
|
280
|
+
) => string | undefined
|
package/src/metro/Watch.ts
CHANGED
|
@@ -1,24 +1,57 @@
|
|
|
1
1
|
/** Keeps Metro's transform cache and delta graph aligned with imported authoring. @module */
|
|
2
|
+
import * as AsyncHooks from 'node:async_hooks'
|
|
2
3
|
import * as Crypto from 'node:crypto'
|
|
3
4
|
import * as Fs from 'node:fs'
|
|
4
5
|
import * as Path from 'node:path'
|
|
6
|
+
import * as Snapshot from '../node/internal/Snapshot.js'
|
|
5
7
|
import type { EventEmitter } from 'node:events'
|
|
6
8
|
import * as Graph from './Graph.js'
|
|
7
9
|
|
|
8
10
|
type Options = {
|
|
9
|
-
readonly platform?: string
|
|
10
11
|
readonly customTransformOptions?: Readonly<Record<string, unknown>>
|
|
12
|
+
readonly platform?: string
|
|
11
13
|
}
|
|
12
14
|
type Bundler = {
|
|
13
|
-
getDependencyGraph(): Promise<{
|
|
15
|
+
getDependencyGraph(): Promise<{
|
|
16
|
+
getWatcher(): EventEmitter
|
|
17
|
+
resolveDependency(
|
|
18
|
+
filename: string,
|
|
19
|
+
dependency: {
|
|
20
|
+
data: {
|
|
21
|
+
asyncType: null
|
|
22
|
+
isESMImport: boolean
|
|
23
|
+
key: string
|
|
24
|
+
locs: readonly never[]
|
|
25
|
+
}
|
|
26
|
+
name: string
|
|
27
|
+
},
|
|
28
|
+
platform: string,
|
|
29
|
+
options: { customResolverOptions: Readonly<Record<string, unknown>> },
|
|
30
|
+
): { type: string; filePath?: string }
|
|
31
|
+
}>
|
|
14
32
|
transformFile(
|
|
15
33
|
filename: string,
|
|
16
34
|
options: Options,
|
|
17
35
|
buffer?: Buffer,
|
|
18
36
|
): Promise<unknown>
|
|
19
37
|
}
|
|
38
|
+
type ResolverOptions = {
|
|
39
|
+
readonly customResolverOptions: Readonly<Record<string, unknown>>
|
|
40
|
+
}
|
|
41
|
+
type IncrementalBundler = {
|
|
42
|
+
buildGraphForEntries: Build
|
|
43
|
+
getBundler(): Bundler
|
|
44
|
+
getDependencies: Build
|
|
45
|
+
updateGraph(revision: { graph: object }, reset: boolean): Promise<unknown>
|
|
46
|
+
}
|
|
47
|
+
type Build = (
|
|
48
|
+
entries: readonly string[],
|
|
49
|
+
transform: unknown,
|
|
50
|
+
resolver: ResolverOptions,
|
|
51
|
+
options?: unknown,
|
|
52
|
+
) => Promise<object>
|
|
20
53
|
/** Metro server methods used by its own serializers and development middleware. */
|
|
21
|
-
export type Server = { getBundler():
|
|
54
|
+
export type Server = { getBundler(): IncrementalBundler }
|
|
22
55
|
type Changes = {
|
|
23
56
|
rootDir: string
|
|
24
57
|
changes: {
|
|
@@ -30,24 +63,74 @@ type Changes = {
|
|
|
30
63
|
|
|
31
64
|
/** Includes imported source contents in transform keys and invalidates their consumers on edits. */
|
|
32
65
|
export async function attach(server: Server, root: string) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
66
|
+
const incremental = server.getBundler()
|
|
67
|
+
const bundler = incremental.getBundler()
|
|
68
|
+
const requests = new AsyncHooks.AsyncLocalStorage<ResolverOptions>()
|
|
69
|
+
const selections = new WeakMap<object, ResolverOptions>()
|
|
70
|
+
for (const name of ['buildGraphForEntries', 'getDependencies'] as const) {
|
|
71
|
+
const build = incremental[name].bind(incremental)
|
|
72
|
+
incremental[name] = (entries, transform, resolver, options) =>
|
|
73
|
+
requests.run(resolver, async () => {
|
|
74
|
+
const graph = await build(entries, transform, resolver, options)
|
|
75
|
+
selections.set(graph, resolver)
|
|
76
|
+
return graph
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
const update = incremental.updateGraph.bind(incremental)
|
|
80
|
+
incremental.updateGraph = (revision, reset) =>
|
|
81
|
+
requests.run(
|
|
82
|
+
selections.get(revision.graph) ?? { customResolverOptions: {} },
|
|
83
|
+
() => update(revision, reset),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const ready = bundler.getDependencyGraph()
|
|
35
87
|
const dependencies = new Map<string, Map<string, Set<string>>>()
|
|
88
|
+
const snapshot = Snapshot.create()
|
|
36
89
|
const transform = bundler.transformFile.bind(bundler)
|
|
37
|
-
bundler.transformFile = (filename, options, buffer) => {
|
|
90
|
+
bundler.transformFile = async (filename, options, buffer) => {
|
|
38
91
|
if (
|
|
39
92
|
(options.platform !== 'ios' && options.platform !== 'android') ||
|
|
40
93
|
!/\.[cm]?[jt]sx?$/.test(filename) ||
|
|
41
|
-
|
|
42
|
-
|
|
94
|
+
filename.startsWith(
|
|
95
|
+
`${Path.resolve(import.meta.dirname, '..')}${Path.sep}`,
|
|
96
|
+
)
|
|
43
97
|
)
|
|
44
98
|
return transform(filename, options, buffer)
|
|
45
99
|
const source = buffer?.toString('utf8') ?? Fs.readFileSync(filename, 'utf8')
|
|
46
|
-
const
|
|
100
|
+
const graph = await ready
|
|
101
|
+
const read = Graph.read(
|
|
102
|
+
filename,
|
|
103
|
+
source,
|
|
104
|
+
options.platform,
|
|
105
|
+
root,
|
|
106
|
+
snapshot,
|
|
107
|
+
(filename, specifier, isESMImport) => {
|
|
108
|
+
const resolved = graph.resolveDependency(
|
|
109
|
+
filename,
|
|
110
|
+
{
|
|
111
|
+
data: {
|
|
112
|
+
asyncType: null,
|
|
113
|
+
isESMImport,
|
|
114
|
+
key: specifier,
|
|
115
|
+
locs: [],
|
|
116
|
+
},
|
|
117
|
+
name: specifier,
|
|
118
|
+
},
|
|
119
|
+
options.platform!,
|
|
120
|
+
requests.getStore() ?? { customResolverOptions: {} },
|
|
121
|
+
)
|
|
122
|
+
return resolved.type === 'sourceFile' ? resolved.filePath : undefined
|
|
123
|
+
},
|
|
124
|
+
)
|
|
47
125
|
const platforms =
|
|
48
126
|
dependencies.get(filename) ?? new Map<string, Set<string>>()
|
|
49
|
-
platforms.set(
|
|
127
|
+
platforms.set(
|
|
128
|
+
JSON.stringify([options.platform, requests.getStore()]),
|
|
129
|
+
new Set(read.files),
|
|
130
|
+
)
|
|
50
131
|
dependencies.set(filename, platforms)
|
|
132
|
+
const input = read.input
|
|
133
|
+
if (!input) return transform(filename, options, buffer)
|
|
51
134
|
const hash = Crypto.hash('sha256', JSON.stringify(input))
|
|
52
135
|
return transform(
|
|
53
136
|
filename,
|
|
@@ -56,11 +139,13 @@ export async function attach(server: Server, root: string) {
|
|
|
56
139
|
customTransformOptions: {
|
|
57
140
|
...options.customTransformOptions,
|
|
58
141
|
zyzzGraph: hash,
|
|
142
|
+
zyzzSources: input,
|
|
59
143
|
},
|
|
60
144
|
},
|
|
61
145
|
buffer,
|
|
62
146
|
)
|
|
63
147
|
}
|
|
148
|
+
const graph = await ready
|
|
64
149
|
graph.getWatcher().prependListener('change', (event: Changes) => {
|
|
65
150
|
const entries = [
|
|
66
151
|
...event.changes.addedFiles,
|
package/src/metro/index.ts
CHANGED
|
@@ -6,12 +6,23 @@ import * as Module from 'node:module'
|
|
|
6
6
|
import * as Path from 'node:path'
|
|
7
7
|
import * as Watch from './Watch.js'
|
|
8
8
|
|
|
9
|
+
type Context = {
|
|
10
|
+
readonly resolveRequest: Resolve
|
|
11
|
+
}
|
|
12
|
+
type Resolve = (
|
|
13
|
+
context: Context,
|
|
14
|
+
name: string,
|
|
15
|
+
platform: string | null,
|
|
16
|
+
) => unknown
|
|
17
|
+
|
|
9
18
|
/** Minimum Metro configuration consumed by the adapter. */
|
|
10
19
|
export type Config = {
|
|
11
|
-
/** Existing development middleware settings. */
|
|
12
|
-
readonly server?: object | undefined
|
|
13
20
|
/** Application root used for the adapter cache. */
|
|
14
21
|
readonly projectRoot?: string | undefined
|
|
22
|
+
/** Existing package, alias, and platform resolution settings. */
|
|
23
|
+
readonly resolver?: object | undefined
|
|
24
|
+
/** Existing development middleware settings. */
|
|
25
|
+
readonly server?: object | undefined
|
|
15
26
|
/** Existing transformer configuration, including Expo's Babel transformer. */
|
|
16
27
|
readonly transformer?:
|
|
17
28
|
| {
|
|
@@ -29,7 +40,13 @@ export function zyzz<const config extends Config>(
|
|
|
29
40
|
config: config,
|
|
30
41
|
options: Omit<
|
|
31
42
|
NativeOptions,
|
|
32
|
-
|
|
43
|
+
| 'platform'
|
|
44
|
+
| 'target'
|
|
45
|
+
| 'moduleId'
|
|
46
|
+
| 'modules'
|
|
47
|
+
| 'imports'
|
|
48
|
+
| 'contracts'
|
|
49
|
+
| 'colorScheme'
|
|
33
50
|
> = {},
|
|
34
51
|
): zyzz.ReturnType<config> {
|
|
35
52
|
if (Object.hasOwn(options, 'colorScheme'))
|
|
@@ -62,8 +79,42 @@ export function zyzz<const config extends Config>(
|
|
|
62
79
|
| ((middleware: Middleware, server: Watch.Server) => Middleware)
|
|
63
80
|
| undefined)
|
|
64
81
|
type Middleware = (...args: unknown[]) => unknown
|
|
82
|
+
const resolve =
|
|
83
|
+
config.resolver &&
|
|
84
|
+
(Reflect.get(config.resolver, 'resolveRequest') as Resolve | undefined)
|
|
85
|
+
|
|
65
86
|
return {
|
|
66
87
|
...config,
|
|
88
|
+
resolver: {
|
|
89
|
+
...config.resolver,
|
|
90
|
+
resolveRequest(context: Context, name: string, platform: string | null) {
|
|
91
|
+
const next = resolve ?? context.resolveRequest
|
|
92
|
+
try {
|
|
93
|
+
return next(context, name, platform)
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (
|
|
96
|
+
(platform !== 'ios' && platform !== 'android') ||
|
|
97
|
+
!name.startsWith('.') ||
|
|
98
|
+
!name.endsWith('.js') ||
|
|
99
|
+
!(error instanceof Error && 'candidates' in error)
|
|
100
|
+
)
|
|
101
|
+
throw error
|
|
102
|
+
|
|
103
|
+
// NodeNext imports name emitted JavaScript; source packages retain TypeScript files.
|
|
104
|
+
const source = next(context, name.slice(0, -3), platform)
|
|
105
|
+
if (
|
|
106
|
+
source &&
|
|
107
|
+
typeof source === 'object' &&
|
|
108
|
+
'filePath' in source &&
|
|
109
|
+
typeof source.filePath === 'string' &&
|
|
110
|
+
/\.tsx?$/.test(source.filePath)
|
|
111
|
+
)
|
|
112
|
+
return source
|
|
113
|
+
|
|
114
|
+
throw error
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
},
|
|
67
118
|
server: {
|
|
68
119
|
...config.server,
|
|
69
120
|
enhanceMiddleware(middleware: Middleware, server: Watch.Server) {
|
|
@@ -89,7 +140,14 @@ export function zyzz<const config extends Config>(
|
|
|
89
140
|
/** Configuration returned by the Metro adapter. */
|
|
90
141
|
export declare namespace zyzz {
|
|
91
142
|
/** Preserves caller configuration fields and the existing transformer options. */
|
|
92
|
-
type ReturnType<config extends Config> = Omit<
|
|
143
|
+
type ReturnType<config extends Config> = Omit<
|
|
144
|
+
config,
|
|
145
|
+
'resolver' | 'transformer'
|
|
146
|
+
> & {
|
|
147
|
+
/** Existing resolver options with the chained native source resolver. */
|
|
148
|
+
resolver: Omit<NonNullable<config['resolver']>, 'resolveRequest'> & {
|
|
149
|
+
resolveRequest: Resolve
|
|
150
|
+
}
|
|
93
151
|
/** Existing transformer configuration with the native compilation entrypoint. */
|
|
94
152
|
transformer: Omit<
|
|
95
153
|
NonNullable<config['transformer']>,
|
package/src/metro/transformer.ts
CHANGED
|
@@ -13,7 +13,12 @@ import { type NativeOptions, zyzz } from '../babel/index.js'
|
|
|
13
13
|
/** Babel transformer inputs passed through without removing upstream fields. */
|
|
14
14
|
type Input = {
|
|
15
15
|
readonly filename: string
|
|
16
|
-
readonly options: {
|
|
16
|
+
readonly options: {
|
|
17
|
+
readonly platform?: string | undefined
|
|
18
|
+
readonly customTransformOptions?:
|
|
19
|
+
| { readonly zyzzSources?: Graph.Input | undefined }
|
|
20
|
+
| undefined
|
|
21
|
+
}
|
|
17
22
|
readonly plugins?: readonly Babel.PluginItem[] | undefined
|
|
18
23
|
readonly src: string
|
|
19
24
|
}
|
|
@@ -28,7 +33,13 @@ export function create(
|
|
|
28
33
|
upstreamPath: string,
|
|
29
34
|
options: Omit<
|
|
30
35
|
NativeOptions,
|
|
31
|
-
|
|
36
|
+
| 'platform'
|
|
37
|
+
| 'target'
|
|
38
|
+
| 'moduleId'
|
|
39
|
+
| 'modules'
|
|
40
|
+
| 'imports'
|
|
41
|
+
| 'contracts'
|
|
42
|
+
| 'colorScheme'
|
|
32
43
|
> & { readonly root: string },
|
|
33
44
|
) {
|
|
34
45
|
const compilers = new Map<string, ReturnType<typeof Compiler.create>>()
|
|
@@ -78,18 +89,17 @@ export function create(
|
|
|
78
89
|
if (
|
|
79
90
|
(platform !== 'ios' && platform !== 'android') ||
|
|
80
91
|
!/\.[cm]?[jt]sx?$/.test(input.filename) ||
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
filename.startsWith(
|
|
93
|
+
`${Path.resolve(import.meta.dirname, '..')}${Path.sep}`,
|
|
94
|
+
)
|
|
83
95
|
)
|
|
84
96
|
return upstream.transform(input)
|
|
85
97
|
|
|
86
|
-
const graph =
|
|
87
|
-
|
|
88
|
-
input.src,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
snapshot,
|
|
92
|
-
)
|
|
98
|
+
const graph =
|
|
99
|
+
input.options.customTransformOptions?.zyzzSources ??
|
|
100
|
+
Graph.read(filename, input.src, platform, options.root, snapshot).input
|
|
101
|
+
if (!graph) return upstream.transform(input)
|
|
102
|
+
|
|
93
103
|
const key = `${platform}:${filename}`
|
|
94
104
|
let compiler = compilers.get(key)
|
|
95
105
|
if (!compiler) {
|