peachsim 0.1.2180 → 0.1.2185
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/cli/commands/shell-url.ts +28 -22
- package/cli/outbound-endpoints.ts +11 -1
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +457 -96
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +352 -58
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +415 -85
- package/dist-lib/host/fetch-proxy-handler.cjs +7 -4
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +7 -4
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +7 -4
- package/dist-lib/native-app-identity-project.cjs +6 -3
- package/dist-lib/native-app-identity-project.mjs +6 -3
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/pdfium-raster.cjs +1 -1
- package/dist-lib/pdfium-raster.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/public-origins-lib-esm.json +25 -24
- package/dist-lib/public-origins-lib.json +63 -53
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/reap-browser.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +447 -93
- package/dist-lib/swift-compile.cjs +13 -4
- package/dist-lib/swift-compile.mjs +13 -4
- package/dist-lib/swift-package.cjs +26 -1
- package/dist-lib/swift-package.mjs +25 -1
- package/dist-lib/swift-submit.cjs +2 -2
- package/dist-lib/swift-submit.mjs +2 -2
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +298 -27
- package/dist-lib/vite.cjs +7 -4
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/dist-types/packages/peach-engine/src/render-worker/map-command-channel.d.ts +14 -0
- package/package.json +1 -1
- package/sdk/ios/System/Library/Frameworks/ActivityKit.framework/Headers/ActivityKit.h +1 -0
- package/sdk/ios/System/Library/Frameworks/ActivityKit.framework/Modules/module.modulemap +4 -0
- package/sdk/ios/usr/lib/swift/ActivityKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +14 -0
- package/sdk/ios/usr/lib/swift/CoreGraphics.swiftmodule/arm64-apple-ios-simulator.swiftinterface +14 -0
- package/sdk/ios/usr/lib/swift/Dispatch.swiftmodule/arm64-apple-ios-simulator.swiftinterface +3 -0
- package/sdk/ios/usr/lib/swift/Foundation.swiftmodule/arm64-apple-ios-simulator.swiftinterface +147 -16
- package/sdk/ios/usr/lib/swift/UIKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +5 -0
- package/src/expo-native-workspace.ts +19 -21
- package/src/expo-swift-bundle.ts +6 -1
- package/src/expo-swift-host.ts +35 -6
- package/src/expoSwiftContract.ts +3 -0
- package/src/native-bundles.ts +30 -10
- package/src/one-sdk/source.ts +10 -4
- package/src/one-sdk-bundle.ts +56 -24
- package/src/swift-compile.ts +3 -1
- package/src/swift-interface-bodies.ts +165 -0
- package/src/swift-objc-standin.ts +73 -13
- package/src/swift-package.ts +26 -0
- package/src/swift-source-pods.ts +27 -4
- package/src/swift-standin-emit.ts +30 -3
- package/src/swift-standins.ts +75 -12
- package/src/swift-submit.ts +2 -1
- package/src/wasm-optimize.ts +11 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// a swift interface without function bodies. a library-evolution interface
|
|
2
|
+
// keeps the bodies of its inlinable functions and accessors, and compiling it
|
|
3
|
+
// type-checks them against the sdk it is read with. the objective-c intake
|
|
4
|
+
// declares Foundation and CoreGraphics only as far as headers name them, so a
|
|
5
|
+
// body that builds a Date or reads CGRect.zero fails the whole module although
|
|
6
|
+
// no declaration does. an interface prints every non-inlinable function
|
|
7
|
+
// without a body, so dropping the rest leaves the same public surface. a
|
|
8
|
+
// default argument value is a body of its own kind: it type-checks against the
|
|
9
|
+
// same partial sdk, and the symbol graph spells it into the stand-in, so it
|
|
10
|
+
// stays unless the caller finds the interface does not load with it.
|
|
11
|
+
|
|
12
|
+
const INLINE_ATTRIBUTE =
|
|
13
|
+
/@(?:inlinable|_alwaysEmitIntoClient|_transparent|inline\(__always\)|_effects\([^()]*\)|_specialize\((?:[^()]|\([^()]*\))*\))\s*/g
|
|
14
|
+
|
|
15
|
+
const ACCESSOR = /^(?:mutating|nonmutating|async|throws|rethrows|get|set|_modify|_read)$/
|
|
16
|
+
|
|
17
|
+
type Frame = 'container' | 'accessors' | 'expression'
|
|
18
|
+
|
|
19
|
+
// the keyword that says what a `{` after this line opens.
|
|
20
|
+
function declarationKind(head: string): 'function' | 'property' | 'container' {
|
|
21
|
+
const words = head
|
|
22
|
+
.replace(/@\w+(?:\((?:[^()]|\([^()]*\))*\))?/g, ' ')
|
|
23
|
+
.match(/[A-Za-z_]\w*/g)
|
|
24
|
+
for (const word of words ?? []) {
|
|
25
|
+
if (word === 'func' || word === 'init' || word === 'deinit') return 'function'
|
|
26
|
+
if (word === 'var' || word === 'let' || word === 'subscript') return 'property'
|
|
27
|
+
if (
|
|
28
|
+
word === 'struct' ||
|
|
29
|
+
word === 'class' ||
|
|
30
|
+
word === 'enum' ||
|
|
31
|
+
word === 'protocol' ||
|
|
32
|
+
word === 'extension' ||
|
|
33
|
+
word === 'actor' ||
|
|
34
|
+
word === 'precedencegroup'
|
|
35
|
+
)
|
|
36
|
+
return 'container'
|
|
37
|
+
}
|
|
38
|
+
return 'container'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** the index after the string, comment or character literal starting at `i`, or `i`. */
|
|
42
|
+
function skipLiteral(text: string, i: number): number {
|
|
43
|
+
if (text.startsWith('//', i)) {
|
|
44
|
+
const end = text.indexOf('\n', i)
|
|
45
|
+
return end < 0 ? text.length : end
|
|
46
|
+
}
|
|
47
|
+
if (text.startsWith('/*', i)) {
|
|
48
|
+
const end = text.indexOf('*/', i + 2)
|
|
49
|
+
return end < 0 ? text.length : end + 2
|
|
50
|
+
}
|
|
51
|
+
if (text[i] !== '"' && text[i] !== '#') return i
|
|
52
|
+
let hashes = 0
|
|
53
|
+
let j = i
|
|
54
|
+
while (text[j] === '#') {
|
|
55
|
+
hashes++
|
|
56
|
+
j++
|
|
57
|
+
}
|
|
58
|
+
if (text[j] !== '"') return i
|
|
59
|
+
const multiline = text.startsWith('"""', j)
|
|
60
|
+
const open = multiline ? '"""' : '"'
|
|
61
|
+
j += open.length
|
|
62
|
+
const close = `${open}${'#'.repeat(hashes)}`
|
|
63
|
+
const escape = `\\${'#'.repeat(hashes)}`
|
|
64
|
+
while (j < text.length) {
|
|
65
|
+
if (text.startsWith(escape, j)) j += escape.length + 1
|
|
66
|
+
else if (text.startsWith(close, j)) return j + close.length
|
|
67
|
+
else j++
|
|
68
|
+
}
|
|
69
|
+
return text.length
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** the index after the brace block opened at `open`. */
|
|
73
|
+
function skipBlock(text: string, open: number): number {
|
|
74
|
+
let depth = 0
|
|
75
|
+
for (let i = open; i < text.length; ) {
|
|
76
|
+
const after = skipLiteral(text, i)
|
|
77
|
+
if (after !== i) {
|
|
78
|
+
i = after
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
if (text[i] === '{') depth++
|
|
82
|
+
else if (text[i] === '}' && --depth === 0) return i + 1
|
|
83
|
+
i++
|
|
84
|
+
}
|
|
85
|
+
return text.length
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** the index of the `,` or `)` that ends the default argument value starting at `i`. */
|
|
89
|
+
function skipExpression(text: string, start: number): number {
|
|
90
|
+
let depth = 0
|
|
91
|
+
for (let i = start; i < text.length; ) {
|
|
92
|
+
const after = skipLiteral(text, i)
|
|
93
|
+
if (after !== i) {
|
|
94
|
+
i = after
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
const char = text[i]!
|
|
98
|
+
if (char === '(' || char === '[' || char === '{' || char === '<') depth++
|
|
99
|
+
else if (char === '>' && text[i - 1] !== '-') depth--
|
|
100
|
+
else if (char === ')' || char === ']' || char === '}') {
|
|
101
|
+
if (depth === 0) return i
|
|
102
|
+
depth--
|
|
103
|
+
} else if (char === ',' && depth === 0) return i
|
|
104
|
+
i++
|
|
105
|
+
}
|
|
106
|
+
return text.length
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function swiftInterfaceWithoutBodies(text: string, keepDefaults = false): string {
|
|
110
|
+
let out = ''
|
|
111
|
+
const frames: Frame[] = []
|
|
112
|
+
let parens = 0
|
|
113
|
+
let lineStart = 0
|
|
114
|
+
for (let i = 0; i < text.length; ) {
|
|
115
|
+
const after = skipLiteral(text, i)
|
|
116
|
+
if (after !== i) {
|
|
117
|
+
out += text.slice(i, after)
|
|
118
|
+
i = after
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
const char = text[i]!
|
|
122
|
+
if (char === '=' && parens > 0 && text[i - 1] === ' ' && text[i + 1] === ' ') {
|
|
123
|
+
out = out.trimEnd()
|
|
124
|
+
i = skipExpression(text, i + 1)
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
if (char === '\n') lineStart = out.length + 1
|
|
128
|
+
else if (char === '(') parens++
|
|
129
|
+
else if (char === ')') parens--
|
|
130
|
+
else if (char === '}') {
|
|
131
|
+
frames.pop()
|
|
132
|
+
} else if (char === '{') {
|
|
133
|
+
const parent = frames.at(-1)
|
|
134
|
+
const head = out.slice(lineStart)
|
|
135
|
+
if (parens > 0 || parent === 'expression') frames.push('expression')
|
|
136
|
+
else if (parent === 'accessors') {
|
|
137
|
+
// an accessor's body: keep its keyword, drop the attributes that need one
|
|
138
|
+
out = `${out.slice(0, lineStart)}${head.replace(INLINE_ATTRIBUTE, '')}`.trimEnd()
|
|
139
|
+
i = skipBlock(text, i)
|
|
140
|
+
continue
|
|
141
|
+
} else {
|
|
142
|
+
const kind = declarationKind(head)
|
|
143
|
+
const body = text.slice(i + 1, skipBlock(text, i) - 1)
|
|
144
|
+
if (kind === 'function') {
|
|
145
|
+
out =
|
|
146
|
+
`${out.slice(0, lineStart)}${head.replace(INLINE_ATTRIBUTE, '')}`.trimEnd()
|
|
147
|
+
i = skipBlock(text, i)
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
// `{ get set }` keeps its keywords; `{ get { .. } }` is a property with bodies
|
|
151
|
+
const bare = body.split(/\s+/).every((word) => word === '' || ACCESSOR.test(word))
|
|
152
|
+
frames.push(kind === 'property' && !bare ? 'accessors' : 'container')
|
|
153
|
+
if (kind === 'property' && bare) {
|
|
154
|
+
out += `{${body}}`
|
|
155
|
+
i = skipBlock(text, i)
|
|
156
|
+
frames.pop()
|
|
157
|
+
continue
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
out += char
|
|
162
|
+
i++
|
|
163
|
+
}
|
|
164
|
+
return out.replace(INLINE_ATTRIBUTE, '')
|
|
165
|
+
}
|
|
@@ -19,6 +19,7 @@ import { createHash } from 'node:crypto'
|
|
|
19
19
|
import fs from 'node:fs'
|
|
20
20
|
import path from 'node:path'
|
|
21
21
|
import { isRecord } from './is-record'
|
|
22
|
+
import { swiftInterfaceWithoutBodies } from './swift-interface-bodies'
|
|
22
23
|
import { swiftObjCPlatformFlags } from './swift-objc-platform'
|
|
23
24
|
import { IOS_SDK_HEADERS, IOS_SDK_OVERLAYS, IOS_SDK_TARGET } from './swift-objc-sdk'
|
|
24
25
|
import { clangImportNames, moduleMapTokens } from './swift-package'
|
|
@@ -460,6 +461,63 @@ function sdkDeclaration(name: string): SdkDeclaration | null {
|
|
|
460
461
|
return sdkDeclarations.get(name) ?? null
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
// a swift interface as the intake sdk reads it. the intake reads
|
|
465
|
+
// CoreFoundation's headers textually, so a type the interface qualifies with
|
|
466
|
+
// CoreFoundation lives in CoreGraphics (CGSize, CGFloat) or Foundation, and
|
|
467
|
+
// inlinable bodies would type-check against its partial Foundation, so only
|
|
468
|
+
// the declarations load.
|
|
469
|
+
function intakeInterface(text: string, keepDefaults = false): string {
|
|
470
|
+
return (
|
|
471
|
+
swiftInterfaceWithoutBodies(text, keepDefaults)
|
|
472
|
+
// the intake modules have no clang submodules
|
|
473
|
+
.replace(/^((?:@_exported[ \t]+)?import[ \t]+)(\w+)\.[\w.]+[ \t]*$/gm, '$1$2')
|
|
474
|
+
.replace(/\bCoreFoundation\.(?=CG)/g, 'CoreGraphics.')
|
|
475
|
+
.replace(/\bCoreFoundation\.(?=\w)/g, 'Foundation.')
|
|
476
|
+
// the intake has no Darwin module; OSStatus is its Int32
|
|
477
|
+
.replace(/\bDarwin\.OSStatus\b/g, 'Swift.Int32')
|
|
478
|
+
// Data's base64 option sets live in the swift overlay; the
|
|
479
|
+
// intake only has their NSData spelling, which Data aliases.
|
|
480
|
+
.replace(
|
|
481
|
+
/\bFoundation\.Data\.(?=Base64(?:En|De)codingOptions\b)/g,
|
|
482
|
+
'Foundation.NSData.',
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// a vendor framework directory the headers import, its swift interfaces read
|
|
488
|
+
// as the intake reads the surface's own.
|
|
489
|
+
function stageVendorFrameworks(dir: string, stage: string): string {
|
|
490
|
+
fs.mkdirSync(stage, { recursive: true })
|
|
491
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
492
|
+
if (!entry.endsWith('.framework')) continue
|
|
493
|
+
const source = path.join(dir, entry)
|
|
494
|
+
const target = path.join(stage, entry)
|
|
495
|
+
fs.mkdirSync(target, { recursive: true })
|
|
496
|
+
for (const child of fs.readdirSync(source)) {
|
|
497
|
+
if (child !== 'Modules') {
|
|
498
|
+
fs.symlinkSync(path.join(source, child), path.join(target, child))
|
|
499
|
+
continue
|
|
500
|
+
}
|
|
501
|
+
for (const file of fs.readdirSync(path.join(source, child), {
|
|
502
|
+
recursive: true,
|
|
503
|
+
encoding: 'utf8',
|
|
504
|
+
})) {
|
|
505
|
+
const from = path.join(source, child, file)
|
|
506
|
+
const to = path.join(target, child, file)
|
|
507
|
+
if (fs.statSync(from).isDirectory()) {
|
|
508
|
+
fs.mkdirSync(to, { recursive: true })
|
|
509
|
+
continue
|
|
510
|
+
}
|
|
511
|
+
fs.mkdirSync(path.dirname(to), { recursive: true })
|
|
512
|
+
if (file.endsWith('.swiftinterface'))
|
|
513
|
+
fs.writeFileSync(to, intakeInterface(fs.readFileSync(from, 'utf8')))
|
|
514
|
+
else fs.copyFileSync(from, to)
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return stage
|
|
519
|
+
}
|
|
520
|
+
|
|
463
521
|
/** the symbol graphs, enum values and imports of an objective-c module. */
|
|
464
522
|
export function objcView(
|
|
465
523
|
surface: ObjcSurface,
|
|
@@ -507,6 +565,7 @@ export function objcView(
|
|
|
507
565
|
fs.writeFileSync(file, text)
|
|
508
566
|
}
|
|
509
567
|
const interfaceCopies: string[] = []
|
|
568
|
+
let keepDefaults = true
|
|
510
569
|
if (framework) {
|
|
511
570
|
fs.mkdirSync(path.join(frameworks, `${surface.module}.framework`, 'Modules'), {
|
|
512
571
|
recursive: true,
|
|
@@ -541,18 +600,7 @@ export function objcView(
|
|
|
541
600
|
interfaceCopies.push(path.join(to, name))
|
|
542
601
|
fs.writeFileSync(
|
|
543
602
|
path.join(to, name),
|
|
544
|
-
fs
|
|
545
|
-
.readFileSync(path.join(from, name), 'utf8')
|
|
546
|
-
.replace(/\bCoreFoundation\.(?=CG)/g, 'CoreGraphics.')
|
|
547
|
-
.replace(/\bCoreFoundation\.(?=\w)/g, 'Foundation.')
|
|
548
|
-
// the intake has no Darwin module; OSStatus is its Int32
|
|
549
|
-
.replace(/\bDarwin\.OSStatus\b/g, 'Swift.Int32')
|
|
550
|
-
// Data's base64 option sets live in the swift overlay; the
|
|
551
|
-
// intake only has their NSData spelling, which Data aliases.
|
|
552
|
-
.replace(
|
|
553
|
-
/\bFoundation\.Data\.(?=Base64(?:En|De)codingOptions\b)/g,
|
|
554
|
-
'Foundation.NSData.',
|
|
555
|
-
),
|
|
603
|
+
intakeInterface(fs.readFileSync(path.join(from, name), 'utf8'), true),
|
|
556
604
|
)
|
|
557
605
|
}
|
|
558
606
|
}
|
|
@@ -619,7 +667,10 @@ export function objcView(
|
|
|
619
667
|
stubs,
|
|
620
668
|
'-I',
|
|
621
669
|
stubIncludes,
|
|
622
|
-
...vendorFrameworks.flatMap((dir) => [
|
|
670
|
+
...vendorFrameworks.flatMap((dir, index) => [
|
|
671
|
+
'-F',
|
|
672
|
+
stageVendorFrameworks(dir, path.join(work, 'vendor-frameworks', String(index))),
|
|
673
|
+
]),
|
|
623
674
|
]
|
|
624
675
|
const missing: Missing = new Map()
|
|
625
676
|
const stubbed = new Set<string>()
|
|
@@ -853,6 +904,15 @@ export function objcView(
|
|
|
853
904
|
)
|
|
854
905
|
}
|
|
855
906
|
}
|
|
907
|
+
// a default argument value the intake cannot type-check fails the whole
|
|
908
|
+
// interface, so the declarations load without any
|
|
909
|
+
if ((!progress || round === ROUNDS) && keepDefaults && interfaceCopies.length > 0) {
|
|
910
|
+
keepDefaults = false
|
|
911
|
+
for (const file of interfaceCopies)
|
|
912
|
+
fs.writeFileSync(file, swiftInterfaceWithoutBodies(fs.readFileSync(file, 'utf8')))
|
|
913
|
+
writeHeaders()
|
|
914
|
+
continue
|
|
915
|
+
}
|
|
856
916
|
if (!progress || round === ROUNDS)
|
|
857
917
|
throw new Error(
|
|
858
918
|
`its headers do not import as Swift: ${
|
package/src/swift-package.ts
CHANGED
|
@@ -321,6 +321,32 @@ export function moduleMapName(text: string): string | undefined {
|
|
|
321
321
|
return index < 0 ? undefined : tokens[index + 1]
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
// the top-level modules a clang module map declares, each with its own text.
|
|
325
|
+
// a uniffi xcframework ships one map holding a module per generated header.
|
|
326
|
+
export function moduleMapModules(text: string): { name: string; text: string }[] {
|
|
327
|
+
const modules: { name: string; text: string }[] = []
|
|
328
|
+
const token = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|"[^"]*"|\{|\}|\bmodule\s+([\w.]+)/g
|
|
329
|
+
let depth = 0
|
|
330
|
+
let start = -1
|
|
331
|
+
let name = ''
|
|
332
|
+
for (const match of text.matchAll(token)) {
|
|
333
|
+
if (match[0] === '{') depth++
|
|
334
|
+
else if (match[0] === '}') {
|
|
335
|
+
depth--
|
|
336
|
+
if (depth === 0 && start >= 0) {
|
|
337
|
+
modules.push({ name, text: text.slice(start, match.index + 1) })
|
|
338
|
+
start = -1
|
|
339
|
+
}
|
|
340
|
+
} else if (match[1] !== undefined && depth === 0) {
|
|
341
|
+
const before = text.slice(0, match.index).trimEnd()
|
|
342
|
+
if (/\bextern$/.test(before)) continue
|
|
343
|
+
start = match.index
|
|
344
|
+
name = match[1]
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return modules
|
|
348
|
+
}
|
|
349
|
+
|
|
324
350
|
// swiftpm's module name for a target: its name as a c99 identifier, so
|
|
325
351
|
// target `cmark-gfm` is imported as `cmark_gfm`.
|
|
326
352
|
export function swiftTargetModuleName(name: string): string {
|
package/src/swift-source-pods.ts
CHANGED
|
@@ -96,6 +96,7 @@ async function trunkSpecification(
|
|
|
96
96
|
'specifications',
|
|
97
97
|
`${checksum}.json`,
|
|
98
98
|
)
|
|
99
|
+
const sha1 = (value: string) => createHash('sha1').update(value).digest('hex')
|
|
99
100
|
let text: string
|
|
100
101
|
if (fs.existsSync(file)) text = fs.readFileSync(file, 'utf8')
|
|
101
102
|
else {
|
|
@@ -105,16 +106,38 @@ async function trunkSpecification(
|
|
|
105
106
|
.slice(0, 3)
|
|
106
107
|
.split('')
|
|
107
108
|
.join('/')
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
)
|
|
109
|
+
const specPath = `Specs/${shard}/${root}/${version}/${root}.podspec.json`
|
|
110
|
+
const response = await fetch(`https://cdn.cocoapods.org/${specPath}`)
|
|
111
111
|
if (!response.ok)
|
|
112
112
|
throw new Error(
|
|
113
113
|
`CocoaPods specification ${root} ${version}: HTTP ${response.status}`,
|
|
114
114
|
)
|
|
115
115
|
text = await response.text()
|
|
116
|
+
// trunk edits a published specification in place (a deprecation adds a
|
|
117
|
+
// field), so a lock written earlier names an older revision of the file.
|
|
118
|
+
if (sha1(text) !== checksum) {
|
|
119
|
+
const history = await fetch(
|
|
120
|
+
`https://api.github.com/repos/CocoaPods/Specs/commits?path=${specPath}&per_page=100`,
|
|
121
|
+
)
|
|
122
|
+
if (!history.ok)
|
|
123
|
+
throw new Error(`CocoaPods specification ${root} history: HTTP ${history.status}`)
|
|
124
|
+
const commits: unknown = await history.json()
|
|
125
|
+
for (const commit of Array.isArray(commits) ? commits : []) {
|
|
126
|
+
const sha = record(commit) ? commit.sha : undefined
|
|
127
|
+
if (typeof sha !== 'string') continue
|
|
128
|
+
const revision = await fetch(
|
|
129
|
+
`https://raw.githubusercontent.com/CocoaPods/Specs/${sha}/${specPath}`,
|
|
130
|
+
)
|
|
131
|
+
if (!revision.ok) continue
|
|
132
|
+
const revisionText = await revision.text()
|
|
133
|
+
if (sha1(revisionText) === checksum) {
|
|
134
|
+
text = revisionText
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
116
139
|
}
|
|
117
|
-
if (
|
|
140
|
+
if (sha1(text) !== checksum)
|
|
118
141
|
throw new Error(`${root} podspec checksum differs from ${lockFile}`)
|
|
119
142
|
const spec: unknown = JSON.parse(text)
|
|
120
143
|
if (!record(spec) || spec.name !== root || spec.version !== version)
|
|
@@ -74,7 +74,7 @@ export function restoreSwiftInterfaceSPI(
|
|
|
74
74
|
for (const line of interfaceText.split('\n')) {
|
|
75
75
|
const enclosing = scopes.at(-1)?.path ?? []
|
|
76
76
|
const declaration =
|
|
77
|
-
/^\s*(?:@[\w.]+(?:\([^)]*\))?\s+|\w+\s+)*?(class(?!\s+(?:func|var|subscript)\b)|struct|enum|protocol|actor|extension|func|var|let|init|subscript|typealias|case)\b\s*([\w.`]*)/.exec(
|
|
77
|
+
/^\s*(?:@[\w.]+(?:\([^)]*\))?\s+|\w+\s+)*?(class(?!\s+(?:func|var|subscript)\b)|struct|enum|protocol|actor|extension|func|var|let|init|subscript|typealias|case)\b\s*([/=\-+!*%<>&|^~?]+|[\w.`]*)/.exec(
|
|
78
78
|
line.replace(/::/g, '.'),
|
|
79
79
|
)
|
|
80
80
|
if (declaration) {
|
|
@@ -178,6 +178,8 @@ export type StandinOptions = {
|
|
|
178
178
|
plain?: ReadonlySet<string>
|
|
179
179
|
/** paths whose type the guest cannot represent in objective-c: emitted without @objc */
|
|
180
180
|
unobjc?: ReadonlySet<string>
|
|
181
|
+
/** class path → objective-c protocols the compiler found its witnesses mismatch (nullability a swift protocol checks); the class drops the conformance */
|
|
182
|
+
unconformed?: ReadonlyMap<string, ReadonlySet<string>>
|
|
181
183
|
/** paths the compiler says must be marked override */
|
|
182
184
|
forcedOverride?: ReadonlySet<string>
|
|
183
185
|
/** class path → its generic superclass with arguments, from the swiftinterface */
|
|
@@ -267,6 +269,25 @@ const KEEP_EXTERNAL = new Map([
|
|
|
267
269
|
['s:Se', 'Decodable'],
|
|
268
270
|
['s:s23CustomStringConvertibleP', 'CustomStringConvertible'],
|
|
269
271
|
['s:s9CodingKeyP', 'CodingKey'],
|
|
272
|
+
['s:SL', 'Comparable'],
|
|
273
|
+
['s:ST', 'Sequence'],
|
|
274
|
+
['s:St', 'IteratorProtocol'],
|
|
275
|
+
['s:Sl', 'Collection'],
|
|
276
|
+
['s:SK', 'BidirectionalCollection'],
|
|
277
|
+
['s:Sk', 'RandomAccessCollection'],
|
|
278
|
+
['s:SM', 'MutableCollection'],
|
|
279
|
+
['s:Sm', 'RangeReplaceableCollection'],
|
|
280
|
+
['s:s28CustomDebugStringConvertibleP', 'CustomDebugStringConvertible'],
|
|
281
|
+
['s:s25LosslessStringConvertibleP', 'LosslessStringConvertible'],
|
|
282
|
+
['s:s20TextOutputStreamableP', 'TextOutputStreamable'],
|
|
283
|
+
['s:Sx', 'Strideable'],
|
|
284
|
+
['s:s18AdditiveArithmeticP', 'AdditiveArithmetic'],
|
|
285
|
+
['s:s25ExpressibleByArrayLiteralP', 'ExpressibleByArrayLiteral'],
|
|
286
|
+
['s:s30ExpressibleByDictionaryLiteralP', 'ExpressibleByDictionaryLiteral'],
|
|
287
|
+
['s:s26ExpressibleByStringLiteralP', 'ExpressibleByStringLiteral'],
|
|
288
|
+
['s:s27ExpressibleByIntegerLiteralP', 'ExpressibleByIntegerLiteral'],
|
|
289
|
+
['s:s25ExpressibleByFloatLiteralP', 'ExpressibleByFloatLiteral'],
|
|
290
|
+
['s:s27ExpressibleByBooleanLiteralP', 'ExpressibleByBooleanLiteral'],
|
|
270
291
|
])
|
|
271
292
|
const LEADING_ATTRS = /^((?:@\w+(?:\([^)]*\))?\s*|nonisolated\s+)*)(.*)$/s
|
|
272
293
|
|
|
@@ -1488,9 +1509,15 @@ export function emitSwiftStandin(
|
|
|
1488
1509
|
!(
|
|
1489
1510
|
parent &&
|
|
1490
1511
|
objectConformers.has(id) &&
|
|
1491
|
-
[
|
|
1512
|
+
[
|
|
1513
|
+
'Hashable',
|
|
1514
|
+
'Equatable',
|
|
1515
|
+
'CustomStringConvertible',
|
|
1516
|
+
'CustomDebugStringConvertible',
|
|
1517
|
+
].includes(name)
|
|
1492
1518
|
) &&
|
|
1493
|
-
!(name === nsObjectProtocol && externalRoot(id) === nsObject)
|
|
1519
|
+
!(name === nsObjectProtocol && externalRoot(id) === nsObject) &&
|
|
1520
|
+
!options.unconformed?.get(path)?.has(name),
|
|
1494
1521
|
),
|
|
1495
1522
|
].filter((name, at, all) => all.indexOf(name) === at)
|
|
1496
1523
|
const parentSymbol = superId.has(id) ? byId.get(superId.get(id)!) : undefined
|
package/src/swift-standins.ts
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
} from './swift-objc-standin'
|
|
46
46
|
import {
|
|
47
47
|
clangImportNames,
|
|
48
|
+
moduleMapModules,
|
|
48
49
|
moduleMapName,
|
|
49
50
|
swiftManifestDeclarations,
|
|
50
51
|
swiftTargetModuleName,
|
|
@@ -674,6 +675,22 @@ function cxxOnly(surface: Surface): boolean {
|
|
|
674
675
|
})
|
|
675
676
|
}
|
|
676
677
|
|
|
678
|
+
// the surfaces of a library slice's module map, one per module it declares: a
|
|
679
|
+
// uniffi xcframework ships a module per generated header in one map, and an
|
|
680
|
+
// app imports each by name.
|
|
681
|
+
function mapSurfaces(modulemap: string): SliceSurface[] {
|
|
682
|
+
const text = fs.readFileSync(modulemap, 'utf8')
|
|
683
|
+
const headers = path.dirname(modulemap)
|
|
684
|
+
const modules = moduleMapModules(text)
|
|
685
|
+
if (modules.length <= 1) {
|
|
686
|
+
const module = moduleMapName(text)
|
|
687
|
+
return module ? [headerSurface(module, modulemap, headers)] : []
|
|
688
|
+
}
|
|
689
|
+
return modules.map((module) =>
|
|
690
|
+
headerSurface(module.name, generatedModuleMap(`${module.text}\n`, false), headers),
|
|
691
|
+
)
|
|
692
|
+
}
|
|
693
|
+
|
|
677
694
|
function simulatorSlice(xcframeworkDir: string): string | null {
|
|
678
695
|
const slice = fs
|
|
679
696
|
.readdirSync(xcframeworkDir)
|
|
@@ -762,8 +779,7 @@ function surfaces(
|
|
|
762
779
|
const headers = path.join(root, 'Headers')
|
|
763
780
|
for (const map of files(headers, (name) => name === 'module.modulemap')) {
|
|
764
781
|
const modulemap = path.join(headers, map)
|
|
765
|
-
|
|
766
|
-
if (module) found.push(headerSurface(module, modulemap, path.dirname(modulemap)))
|
|
782
|
+
found.push(...mapSurfaces(modulemap))
|
|
767
783
|
}
|
|
768
784
|
if (found.length === 0 && files(headers, (name) => name.endsWith('.h')).length) {
|
|
769
785
|
const module = swiftTargetModuleName(target)
|
|
@@ -964,13 +980,22 @@ export function swiftInterfaceForWasm(text: string, cFlags: readonly string[]):
|
|
|
964
980
|
])
|
|
965
981
|
return `${interop} ${xcc.join(' ')}`
|
|
966
982
|
})
|
|
967
|
-
|
|
968
|
-
|
|
983
|
+
const lines = retargeted.split('\n')
|
|
984
|
+
// an optional requirement stays @objc, so a type its signature names must be
|
|
985
|
+
// objective-c representable: an enum it names keeps its own @objc
|
|
986
|
+
const required = lines.filter((line) => OPTIONAL_REQUIREMENT.test(line)).join('\n')
|
|
987
|
+
return lines
|
|
969
988
|
.map((line) => {
|
|
970
989
|
// an enum's @objc only bridges it to objective-c, which wasm has no
|
|
971
990
|
// runtime for, and an @objc error enum loses its raw-value Hashable.
|
|
972
|
-
if (/\benum\s/.test(line) && TYPE_DECLARATION.test(line))
|
|
973
|
-
|
|
991
|
+
if (/\benum\s/.test(line) && TYPE_DECLARATION.test(line)) {
|
|
992
|
+
const name = /\benum\s+(\w+)/.exec(line)?.[1]
|
|
993
|
+
return name &&
|
|
994
|
+
new RegExp(`\\b${name}\\b`).test(required) &&
|
|
995
|
+
!/\bSwift\.Error\b/.test(line)
|
|
996
|
+
? line
|
|
997
|
+
: line.replace(/@objc(?:\s*\([^()]*\))?\s+/g, '')
|
|
998
|
+
}
|
|
974
999
|
if (TYPE_DECLARATION.test(line)) return line.replace(/@objcMembers\b/g, '')
|
|
975
1000
|
if (OPTIONAL_REQUIREMENT.test(line)) return line
|
|
976
1001
|
return line.replace(OBJC_ATTRIBUTE, '')
|
|
@@ -1522,6 +1547,18 @@ function graphStandin(
|
|
|
1522
1547
|
const plain = new Set<string>()
|
|
1523
1548
|
const unobjc = new Set<string>()
|
|
1524
1549
|
const trapped = new Set<string>()
|
|
1550
|
+
const unconformed = new Map<string, Set<string>>()
|
|
1551
|
+
const objcProtocols = new Set(
|
|
1552
|
+
graphs.flatMap((graph) =>
|
|
1553
|
+
graph.symbols
|
|
1554
|
+
.filter(
|
|
1555
|
+
(symbol) =>
|
|
1556
|
+
symbol.kind.identifier === 'swift.protocol' &&
|
|
1557
|
+
symbol.identifier.precise.startsWith('c:'),
|
|
1558
|
+
)
|
|
1559
|
+
.map((symbol) => symbol.pathComponents.join('.')),
|
|
1560
|
+
),
|
|
1561
|
+
)
|
|
1525
1562
|
const file = path.join(work, `${module}.swift`)
|
|
1526
1563
|
// a source target whose implementation compiles keeps inert bodies, and a
|
|
1527
1564
|
// placeholder sends nothing. a target replaced because assembly cannot
|
|
@@ -1540,6 +1577,7 @@ function graphStandin(
|
|
|
1540
1577
|
omit,
|
|
1541
1578
|
plain,
|
|
1542
1579
|
unobjc,
|
|
1580
|
+
unconformed,
|
|
1543
1581
|
trapped,
|
|
1544
1582
|
inert,
|
|
1545
1583
|
imports: [...(placeholder?.peachImports ?? []), ...imports],
|
|
@@ -1589,7 +1627,7 @@ function graphStandin(
|
|
|
1589
1627
|
files: { [`${module}.swift`]: source },
|
|
1590
1628
|
...(imports.length ? { dependencies: imports } : {}),
|
|
1591
1629
|
}
|
|
1592
|
-
const before = omit.size + plain.size + unobjc.size + trapped.size
|
|
1630
|
+
const before = omit.size + plain.size + unobjc.size + trapped.size + unconformed.size
|
|
1593
1631
|
const elsewhere: string[] = []
|
|
1594
1632
|
for (const match of check.stderr.matchAll(/^(\S+?):(\d+):\d+: error: (.*)$/gm)) {
|
|
1595
1633
|
const owner = match[1] === file ? owners[Number(match[2]) - 1] : undefined
|
|
@@ -1599,6 +1637,15 @@ function graphStandin(
|
|
|
1599
1637
|
unobjc.add(owner)
|
|
1600
1638
|
continue
|
|
1601
1639
|
}
|
|
1640
|
+
// an objective-c protocol the guest adapts to a swift one checks the
|
|
1641
|
+
// nullability a swift class's exported witness differs in
|
|
1642
|
+
const conformance = /^type '\w+' does not conform to protocol '(\w+)'$/.exec(
|
|
1643
|
+
match[3]!,
|
|
1644
|
+
)?.[1]
|
|
1645
|
+
if (owner && conformance && objcProtocols.has(conformance)) {
|
|
1646
|
+
unconformed.set(owner, new Set([...(unconformed.get(owner) ?? []), conformance]))
|
|
1647
|
+
continue
|
|
1648
|
+
}
|
|
1602
1649
|
if (owner && swiftPaths.has(owner))
|
|
1603
1650
|
throw new Error(
|
|
1604
1651
|
`Swift declaration ${module}.${owner} does not typecheck: ${match[3]}`,
|
|
@@ -1615,7 +1662,7 @@ function graphStandin(
|
|
|
1615
1662
|
else if (owner) omit.set(owner, `rejected by swiftc: ${match[3]}`)
|
|
1616
1663
|
else elsewhere.push(match[3]!)
|
|
1617
1664
|
}
|
|
1618
|
-
if (omit.size + plain.size + unobjc.size + trapped.size === before)
|
|
1665
|
+
if (omit.size + plain.size + unobjc.size + trapped.size + unconformed.size === before)
|
|
1619
1666
|
throw new Error(
|
|
1620
1667
|
`its generated source does not typecheck: ${elsewhere.slice(0, 3).join('; ') || check.stderr.trim().slice(0, 400)}`,
|
|
1621
1668
|
)
|
|
@@ -1788,7 +1835,20 @@ function cStandin(
|
|
|
1788
1835
|
toolchain: Toolchain,
|
|
1789
1836
|
work: string,
|
|
1790
1837
|
): SwiftStandin {
|
|
1791
|
-
const
|
|
1838
|
+
const everyHeader = files(surface.headers, (name) => name.endsWith('.h'))
|
|
1839
|
+
const moduleRoots = moduleMapHeaders(surface.modulemap, everyHeader)
|
|
1840
|
+
// the module's headers and the local headers they include: a map of several modules
|
|
1841
|
+
// (one per uniffi header) gives each its own
|
|
1842
|
+
const headers = [...moduleRoots]
|
|
1843
|
+
for (let at = 0; at < headers.length; at++)
|
|
1844
|
+
for (const [, local] of fs
|
|
1845
|
+
.readFileSync(path.join(surface.headers, headers[at]!), 'utf8')
|
|
1846
|
+
.matchAll(/^\s*#\s*(?:include|import)\s*["<]([^">]+)[">]/gm)) {
|
|
1847
|
+
const file = path.posix.normalize(
|
|
1848
|
+
path.posix.join(path.posix.dirname(headers[at]!), local!),
|
|
1849
|
+
)
|
|
1850
|
+
if (everyHeader.includes(file) && !headers.includes(file)) headers.push(file)
|
|
1851
|
+
}
|
|
1792
1852
|
const framework = path.basename(path.dirname(surface.modulemap)) === 'Modules'
|
|
1793
1853
|
const headerDirectory = framework ? `${surface.module}.framework/Headers` : '.'
|
|
1794
1854
|
const texts = new Map(
|
|
@@ -1804,9 +1864,7 @@ function cStandin(
|
|
|
1804
1864
|
fs.mkdirSync(path.dirname(path.join(include, file)), { recursive: true })
|
|
1805
1865
|
fs.writeFileSync(path.join(include, file), text)
|
|
1806
1866
|
}
|
|
1807
|
-
const roots =
|
|
1808
|
-
path.posix.join(headerDirectory, file),
|
|
1809
|
-
)
|
|
1867
|
+
const roots = moduleRoots.map((file) => path.posix.join(headerDirectory, file))
|
|
1810
1868
|
const frameworkMap = `${surface.module}.framework/Modules/module.modulemap`
|
|
1811
1869
|
const modulemap = `module ${surface.module} {\n${roots.map((file) => ` header "${file}"\n`).join('')} export *\n}\n`
|
|
1812
1870
|
const probe = path.join(work, 'declarations.c')
|
|
@@ -2728,9 +2786,14 @@ async function binaryStandins(
|
|
|
2728
2786
|
fs.rmSync(work, { recursive: true, force: true })
|
|
2729
2787
|
return error instanceof Error ? error.message : String(error)
|
|
2730
2788
|
}
|
|
2789
|
+
// a framework whose module map names headers it does not ship declares nothing
|
|
2731
2790
|
const found = all.filter(
|
|
2732
2791
|
(surface) =>
|
|
2733
2792
|
!provided.has(surface.module) &&
|
|
2793
|
+
(surface.kind === 'swift' ||
|
|
2794
|
+
surface.kind === 'mixed' ||
|
|
2795
|
+
surface.kind === 'source' ||
|
|
2796
|
+
files(surface.headers, (name) => name.endsWith('.h')).length > 0) &&
|
|
2734
2797
|
!(binary.cxxHeaders === undefined && cxxOnly(surface)),
|
|
2735
2798
|
)
|
|
2736
2799
|
if (found.length === 0) {
|
package/src/swift-submit.ts
CHANGED
|
@@ -146,7 +146,8 @@ export const MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024
|
|
|
146
146
|
// 68: fetch headers replies carry `setCookies`, the response's Set-Cookie list.
|
|
147
147
|
// 69: `peach.icu_item_v1` replaces `icu_data_v1`: the host fills icu's table of
|
|
148
148
|
// contents and places each item's pack the first time icu opens it.
|
|
149
|
-
|
|
149
|
+
// 70: full Objective-C forwarding uses exact typed IMPs (`peach.imp_forwarder_mint`).
|
|
150
|
+
export const SWIFT_HOST_ABI = 70
|
|
150
151
|
|
|
151
152
|
export interface SwiftImageUpload {
|
|
152
153
|
/** the raster file picked for the asset (has the extension). */
|
package/src/wasm-optimize.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { installVerifiedTarball } from '@contrast/runtime-delivery'
|
|
4
4
|
import { BINARYEN_VERSION, wasmOpt } from 'wasm-opt'
|
|
5
|
+
import { windowAdmission } from './admitted-mac-build'
|
|
5
6
|
import { rnxHomeDir } from './home-paths'
|
|
6
7
|
|
|
7
8
|
// packaged native executables, size-optimized. clang's own
|
|
@@ -60,6 +61,15 @@ async function runWasmOpt(
|
|
|
60
61
|
skipPasses: readonly string[],
|
|
61
62
|
binary: string,
|
|
62
63
|
): Promise<void> {
|
|
64
|
+
const admittedCores = windowAdmission()?.cores ?? Number(process.env.TM_EXEC_CPU_CORES)
|
|
65
|
+
const requestedCores = Number(process.env.BINARYEN_CORES)
|
|
66
|
+
const environment: NodeJS.ProcessEnv = { ...process.env, WASM_OPT_PATH: binary }
|
|
67
|
+
if (Number.isInteger(admittedCores) && admittedCores > 0)
|
|
68
|
+
environment.BINARYEN_CORES = String(
|
|
69
|
+
Number.isInteger(requestedCores) && requestedCores > 0
|
|
70
|
+
? Math.min(admittedCores, requestedCores)
|
|
71
|
+
: admittedCores,
|
|
72
|
+
)
|
|
63
73
|
const result = await wasmOpt(
|
|
64
74
|
[
|
|
65
75
|
'-Oz',
|
|
@@ -69,7 +79,7 @@ async function runWasmOpt(
|
|
|
69
79
|
'-o',
|
|
70
80
|
output,
|
|
71
81
|
],
|
|
72
|
-
{ env:
|
|
82
|
+
{ env: environment },
|
|
73
83
|
)
|
|
74
84
|
if (result.status !== 0)
|
|
75
85
|
throw new Error(`wasm-opt exited ${result.status}: ${result.stderr}`)
|