vxrn 2.7.6 → 2.8.0
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/dist/config/getBaseViteConfigOnly.mjs +1 -1
- package/dist/config/getBaseViteConfigOnly.mjs.map +1 -1
- package/dist/config/getBaseViteConfigOnly.native.js +1 -1
- package/dist/config/getBaseViteConfigOnly.native.js.map +1 -1
- package/dist/config/getOptimizeDeps.mjs +1 -1
- package/dist/config/getOptimizeDeps.mjs.map +1 -1
- package/dist/config/getOptimizeDeps.native.js +3 -1
- package/dist/config/getOptimizeDeps.native.js.map +1 -1
- package/dist/config/getReactNativePlugins.mjs +1 -2
- package/dist/config/getReactNativePlugins.mjs.map +1 -1
- package/dist/config/getReactNativePlugins.native.js +4 -2
- package/dist/config/getReactNativePlugins.native.js.map +1 -1
- package/dist/exports/prebuildWithoutExpo.mjs +26 -97
- package/dist/exports/prebuildWithoutExpo.mjs.map +1 -1
- package/dist/exports/prebuildWithoutExpo.native.js +47 -133
- package/dist/exports/prebuildWithoutExpo.native.js.map +1 -1
- package/dist/plugins/autoDepOptimizePlugin.mjs +5 -2
- package/dist/plugins/autoDepOptimizePlugin.mjs.map +1 -1
- package/dist/plugins/autoDepOptimizePlugin.native.js +10 -7
- package/dist/plugins/autoDepOptimizePlugin.native.js.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.mjs +5 -4
- package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.native.js +5 -4
- package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.mjs +30 -7
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +64 -10
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/package.json +27 -20
- package/src/config/getBaseViteConfigOnly.ts +1 -1
- package/src/config/getOptimizeDeps.ts +3 -1
- package/src/config/getReactNativePlugins.ts +8 -4
- package/src/config/ssrVendorOptimization.test.ts +115 -0
- package/src/exports/prebuildWithoutExpo.test.ts +62 -157
- package/src/exports/prebuildWithoutExpo.ts +31 -125
- package/src/plugins/autoDepOptimizeExternal.test.ts +101 -0
- package/src/plugins/autoDepOptimizePlugin.ts +12 -6
- package/src/plugins/rnCodegenPlugin.ts +1 -2
- package/src/rn-commands/bundle/buildBundle.ts +8 -7
- package/src/utils/createNativeDevEngine.test.ts +308 -9
- package/src/utils/createNativeDevEngine.ts +71 -29
- package/types/config/getOptimizeDeps.d.ts.map +1 -1
- package/types/config/getReactNativePlugins.d.ts +3 -2
- package/types/config/getReactNativePlugins.d.ts.map +1 -1
- package/types/exports/prebuildWithoutExpo.d.ts.map +1 -1
- package/types/plugins/autoDepOptimizePlugin.d.ts +1 -0
- package/types/plugins/autoDepOptimizePlugin.d.ts.map +1 -1
- package/types/plugins/rnCodegenPlugin.d.ts.map +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +3 -4
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
- package/src/plugins/autoDepOptimizePlugin.test.ts +0 -42
|
@@ -151,14 +151,9 @@ function escapeXml(value: string): string {
|
|
|
151
151
|
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// always renders the scene manifest, a SceneDelegate that owns the window
|
|
158
|
-
// and the RN root, and a trimmed AppDelegate. no toggle: the scene path is
|
|
159
|
-
// the only path, on every ios version prebuild supports.
|
|
160
|
-
const SCENE_DELEGATE_FILE_REF_ID = '1A2B3C4D5E6F7A8B9C0D1E2F'
|
|
161
|
-
const SCENE_DELEGATE_BUILD_FILE_ID = '2B3C4D5E6F7A8B9C0D1E2F1A'
|
|
154
|
+
// the rn template supplies the scene manifest and xcode source references.
|
|
155
|
+
// keep factory ownership at app scope so background tasks and app intents can
|
|
156
|
+
// start react native before a scene connects; each scene owns its window.
|
|
162
157
|
const PUSH_ENTITLEMENTS_FILE_REF_ID = '3C4D5E6F7A8B9C0D1E2F1A2B'
|
|
163
158
|
|
|
164
159
|
export function renderSceneDelegateSwift(appName: string): string {
|
|
@@ -185,40 +180,19 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
|
185
180
|
let window = UIWindow(windowScene: windowScene)
|
|
186
181
|
self.window = window
|
|
187
182
|
|
|
188
|
-
// cold-start links arrive in connectionOptions under scenes, never in
|
|
189
|
-
// the app launchOptions, so they are translated into the launchOptions
|
|
190
|
-
// shape RCTLinkingManager.getInitialURL reads.
|
|
191
|
-
var launchOptions: [UIApplication.LaunchOptionsKey: Any] = [:]
|
|
192
|
-
if let url = connectionOptions.urlContexts.first?.url {
|
|
193
|
-
launchOptions[.url] = url
|
|
194
|
-
}
|
|
195
|
-
if let activity = connectionOptions.userActivities.first(where: {
|
|
196
|
-
$0.activityType == NSUserActivityTypeBrowsingWeb
|
|
197
|
-
}) {
|
|
198
|
-
launchOptions[.userActivityDictionary] = [
|
|
199
|
-
"UIApplicationLaunchOptionsUserActivityTypeKey": activity.activityType,
|
|
200
|
-
"UIApplicationLaunchOptionsUserActivityKey": activity,
|
|
201
|
-
]
|
|
202
|
-
}
|
|
203
|
-
|
|
204
183
|
factory.startReactNative(
|
|
205
184
|
withModuleName: "${appName}",
|
|
206
185
|
in: window,
|
|
207
|
-
|
|
186
|
+
connectionOptions: connectionOptions
|
|
208
187
|
)
|
|
209
188
|
}
|
|
210
189
|
|
|
211
190
|
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
|
212
|
-
|
|
213
|
-
RCTLinkingManager.application(UIApplication.shared, open: url, options: [:])
|
|
191
|
+
RCTLinkingManager.scene(scene, openURLContexts: URLContexts)
|
|
214
192
|
}
|
|
215
193
|
|
|
216
194
|
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
|
217
|
-
RCTLinkingManager.
|
|
218
|
-
UIApplication.shared,
|
|
219
|
-
continue: userActivity,
|
|
220
|
-
restorationHandler: { _ in }
|
|
221
|
-
)
|
|
195
|
+
RCTLinkingManager.scene(scene, continue: userActivity)
|
|
222
196
|
}
|
|
223
197
|
}
|
|
224
198
|
`
|
|
@@ -232,79 +206,32 @@ function insertAfterLine(haystack: string, anchor: string, insertion: string): s
|
|
|
232
206
|
return `${haystack.slice(0, lineEnd + 1)}${insertion}\n${haystack.slice(lineEnd + 1)}`
|
|
233
207
|
}
|
|
234
208
|
|
|
235
|
-
function patchIosInfoPlistSceneManifest(rendered: string): string {
|
|
236
|
-
const anchor = '\t<key>LSRequiresIPhoneOS</key>'
|
|
237
|
-
if (!rendered.includes(anchor)) {
|
|
238
|
-
throw new Error(
|
|
239
|
-
'[vxrn] prebuild template Info.plist lost its LSRequiresIPhoneOS anchor'
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
return rendered.replace(
|
|
243
|
-
anchor,
|
|
244
|
-
`\t<key>UIApplicationSceneManifest</key>
|
|
245
|
-
\t<dict>
|
|
246
|
-
\t\t<key>UIApplicationSupportsMultipleScenes</key>
|
|
247
|
-
\t\t<false/>
|
|
248
|
-
\t\t<key>UISceneConfigurations</key>
|
|
249
|
-
\t\t<dict>
|
|
250
|
-
\t\t\t<key>UIWindowSceneSessionRoleApplication</key>
|
|
251
|
-
\t\t\t<array>
|
|
252
|
-
\t\t\t\t<dict>
|
|
253
|
-
\t\t\t\t\t<key>UISceneConfigurationName</key>
|
|
254
|
-
\t\t\t\t\t<string>Default Configuration</string>
|
|
255
|
-
\t\t\t\t\t<key>UISceneDelegateClassName</key>
|
|
256
|
-
\t\t\t\t\t<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
|
257
|
-
\t\t\t\t</dict>
|
|
258
|
-
\t\t\t</array>
|
|
259
|
-
\t\t</dict>
|
|
260
|
-
\t</dict>
|
|
261
|
-
${anchor}`
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
209
|
function patchIosAppDelegateSceneLifecycle(
|
|
266
210
|
rendered: string,
|
|
267
|
-
appName: string,
|
|
268
211
|
backgroundTasks: boolean,
|
|
269
212
|
appIntents: boolean
|
|
270
213
|
): string {
|
|
271
214
|
const anchor = `@main
|
|
272
215
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
273
|
-
var window: UIWindow?
|
|
274
|
-
|
|
275
|
-
var reactNativeDelegate: ReactNativeDelegate?
|
|
276
|
-
var reactNativeFactory: RCTReactNativeFactory?
|
|
277
|
-
|
|
278
216
|
func application(
|
|
279
217
|
_ application: UIApplication,
|
|
280
218
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
281
219
|
) -> Bool {
|
|
282
|
-
|
|
283
|
-
let factory = RCTReactNativeFactory(delegate: delegate)
|
|
284
|
-
delegate.dependencyProvider = RCTAppDependencyProvider()
|
|
285
|
-
|
|
286
|
-
reactNativeDelegate = delegate
|
|
287
|
-
reactNativeFactory = factory
|
|
288
|
-
|
|
289
|
-
window = UIWindow(frame: UIScreen.main.bounds)
|
|
290
|
-
|
|
291
|
-
factory.startReactNative(
|
|
292
|
-
withModuleName: "${appName}",
|
|
293
|
-
in: window,
|
|
294
|
-
launchOptions: launchOptions
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
return true
|
|
220
|
+
true
|
|
298
221
|
}
|
|
299
222
|
}`
|
|
300
223
|
if (!rendered.includes(anchor)) {
|
|
301
224
|
throw new Error(
|
|
302
|
-
'[vxrn] prebuild template AppDelegate.swift changed shape: cannot
|
|
225
|
+
'[vxrn] prebuild template AppDelegate.swift changed shape: cannot register the app-level React Native factory'
|
|
303
226
|
)
|
|
304
227
|
}
|
|
305
228
|
return rendered.replace(
|
|
306
229
|
anchor,
|
|
307
|
-
|
|
230
|
+
`import React
|
|
231
|
+
import React_RCTAppDelegate
|
|
232
|
+
import ReactAppDependencyProvider
|
|
233
|
+
|
|
234
|
+
@main
|
|
308
235
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
309
236
|
var reactNativeDelegate: ReactNativeDelegate?
|
|
310
237
|
var reactNativeFactory: RCTReactNativeFactory?
|
|
@@ -322,6 +249,16 @@ ${backgroundTasks ? ` OneBackgroundTasksRegisterHost(factory, launchOptions)
|
|
|
322
249
|
` : ''}${appIntents ? ` OneAppIntentsRegisterHost(factory, launchOptions)
|
|
323
250
|
` : ''} return true
|
|
324
251
|
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
|
|
255
|
+
override func bundleURL() -> URL? {
|
|
256
|
+
#if DEBUG
|
|
257
|
+
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
258
|
+
#else
|
|
259
|
+
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
260
|
+
#endif
|
|
261
|
+
}
|
|
325
262
|
}`
|
|
326
263
|
)
|
|
327
264
|
}
|
|
@@ -362,38 +299,6 @@ function patchIosPbxprojOneBridgingHeader(project: string, appName: string): str
|
|
|
362
299
|
.join(`${anchor}\n\t\t\t\t${setting}`)
|
|
363
300
|
}
|
|
364
301
|
|
|
365
|
-
function patchIosPbxprojSceneDelegate(rendered: string, appName: string): string {
|
|
366
|
-
const edits: Array<[string, string]> = [
|
|
367
|
-
[
|
|
368
|
-
'/* AppDelegate.swift in Sources */ = {isa = PBXBuildFile;',
|
|
369
|
-
`\t\t${SCENE_DELEGATE_BUILD_FILE_ID} /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${SCENE_DELEGATE_FILE_REF_ID} /* SceneDelegate.swift */; };`,
|
|
370
|
-
],
|
|
371
|
-
[
|
|
372
|
-
'/* AppDelegate.swift */ = {isa = PBXFileReference;',
|
|
373
|
-
`\t\t${SCENE_DELEGATE_FILE_REF_ID} /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SceneDelegate.swift; path = ${appName}/SceneDelegate.swift; sourceTree = "<group>"; };`,
|
|
374
|
-
],
|
|
375
|
-
[
|
|
376
|
-
'/* AppDelegate.swift */,',
|
|
377
|
-
`\t\t\t\t${SCENE_DELEGATE_FILE_REF_ID} /* SceneDelegate.swift */,`,
|
|
378
|
-
],
|
|
379
|
-
[
|
|
380
|
-
'/* AppDelegate.swift in Sources */,',
|
|
381
|
-
`\t\t\t\t${SCENE_DELEGATE_BUILD_FILE_ID} /* SceneDelegate.swift in Sources */,`,
|
|
382
|
-
],
|
|
383
|
-
]
|
|
384
|
-
let patched = rendered
|
|
385
|
-
for (const [anchor, insertion] of edits) {
|
|
386
|
-
const next = insertAfterLine(patched, anchor, insertion)
|
|
387
|
-
if (next === patched) {
|
|
388
|
-
throw new Error(
|
|
389
|
-
`[vxrn] prebuild template project.pbxproj lost its AppDelegate.swift anchor (${anchor})`
|
|
390
|
-
)
|
|
391
|
-
}
|
|
392
|
-
patched = next
|
|
393
|
-
}
|
|
394
|
-
return patched
|
|
395
|
-
}
|
|
396
|
-
|
|
397
302
|
function patchIosPbxprojAppIntents(rendered: string, appName: string): string {
|
|
398
303
|
const name = 'OneAppIntents.swift'
|
|
399
304
|
const fileRef = pbxprojId('one-app-intents-file')
|
|
@@ -2703,12 +2608,10 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
|
|
|
2703
2608
|
/\t<key>NS\w+UsageDescription<\/key>\s*<string><\/string>\n/g,
|
|
2704
2609
|
''
|
|
2705
2610
|
)
|
|
2706
|
-
rendered = patchIosInfoPlistSceneManifest(rendered)
|
|
2707
2611
|
}
|
|
2708
2612
|
if (platform === 'ios' && relativePath.endsWith('/AppDelegate.swift')) {
|
|
2709
2613
|
rendered = patchIosAppDelegateSceneLifecycle(
|
|
2710
2614
|
rendered,
|
|
2711
|
-
appName,
|
|
2712
2615
|
!!app.ios?.backgroundTasks,
|
|
2713
2616
|
!!app.ios?.appIntents
|
|
2714
2617
|
)
|
|
@@ -2734,15 +2637,19 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
|
|
|
2734
2637
|
rendered = nativeProjectPatches.pointReleaseBundleURLAtOneUpdates(rendered)
|
|
2735
2638
|
}
|
|
2736
2639
|
}
|
|
2737
|
-
if (platform === 'ios'
|
|
2640
|
+
if (platform === 'ios') {
|
|
2641
|
+
// one requires ios 17 (see the schema minimumVersion and
|
|
2642
|
+
// One.podspec); the template's react native 15.1 default fails pod
|
|
2643
|
+
// install, so an unset target falls back to one's floor.
|
|
2644
|
+
const deploymentTarget = app.ios?.deploymentTarget ?? '17.0'
|
|
2738
2645
|
rendered = rendered
|
|
2739
2646
|
.replace(
|
|
2740
2647
|
'platform :ios, min_ios_version_supported',
|
|
2741
|
-
`platform :ios, '${
|
|
2648
|
+
`platform :ios, '${deploymentTarget}'`
|
|
2742
2649
|
)
|
|
2743
2650
|
.replace(
|
|
2744
2651
|
/IPHONEOS_DEPLOYMENT_TARGET = \d+(?:\.\d+)?;/g,
|
|
2745
|
-
`IPHONEOS_DEPLOYMENT_TARGET = ${
|
|
2652
|
+
`IPHONEOS_DEPLOYMENT_TARGET = ${deploymentTarget};`
|
|
2746
2653
|
)
|
|
2747
2654
|
if (relativePath === 'Podfile') {
|
|
2748
2655
|
rendered = rendered.replace(
|
|
@@ -2751,7 +2658,7 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
|
|
|
2751
2658
|
|
|
2752
2659
|
installer.pods_project.targets.each do |target|
|
|
2753
2660
|
target.build_configurations.each do |config|
|
|
2754
|
-
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '${
|
|
2661
|
+
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '${deploymentTarget}'
|
|
2755
2662
|
end
|
|
2756
2663
|
end
|
|
2757
2664
|
end
|
|
@@ -2761,7 +2668,6 @@ end`
|
|
|
2761
2668
|
}
|
|
2762
2669
|
if (platform === 'ios' && relativePath.endsWith('.xcodeproj/project.pbxproj')) {
|
|
2763
2670
|
rendered = patchIosBundlePhase(rendered, app.updates?.runtimeVersion)
|
|
2764
|
-
rendered = patchIosPbxprojSceneDelegate(rendered, appName)
|
|
2765
2671
|
if (app.ios?.appIntents) rendered = patchIosPbxprojAppIntents(rendered, appName)
|
|
2766
2672
|
rendered = patchIosPbxprojOneBridgingHeader(rendered, appName)
|
|
2767
2673
|
if (Object.keys(appEntitlements(app)).length && !app.ios?.widgets) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { createServer } from 'vite'
|
|
5
|
+
import { expect, it } from 'vitest'
|
|
6
|
+
import { autoDepOptimizePlugin } from './autoDepOptimizePlugin'
|
|
7
|
+
|
|
8
|
+
it.each([
|
|
9
|
+
{ external: 'external-dep', rootBundled: false, exclude: [] },
|
|
10
|
+
{ external: 'external-dep/feature', rootBundled: true, exclude: [] },
|
|
11
|
+
{ external: 'external-dep', rootBundled: false, exclude: ['internal-dep'] },
|
|
12
|
+
{
|
|
13
|
+
external: 'external-dep',
|
|
14
|
+
rootBundled: false,
|
|
15
|
+
exclude: [/some-pattern/, 'internal-dep'],
|
|
16
|
+
},
|
|
17
|
+
])(
|
|
18
|
+
'keeps SSR external $external and exclusions $exclude outside prebundling',
|
|
19
|
+
async ({ external, rootBundled, exclude }) => {
|
|
20
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), 'vxrn-ssr-external-')))
|
|
21
|
+
const bundled = new Set<string>()
|
|
22
|
+
const names = ['external-dep', 'external-dep-extra', 'internal-dep']
|
|
23
|
+
writeFileSync(
|
|
24
|
+
join(root, 'package.json'),
|
|
25
|
+
JSON.stringify({
|
|
26
|
+
private: true,
|
|
27
|
+
dependencies: Object.fromEntries(names.map((name) => [name, '1.0.0'])),
|
|
28
|
+
})
|
|
29
|
+
)
|
|
30
|
+
for (const name of names) {
|
|
31
|
+
const folder = join(root, 'node_modules', name)
|
|
32
|
+
mkdirSync(folder, { recursive: true })
|
|
33
|
+
writeFileSync(
|
|
34
|
+
join(folder, 'package.json'),
|
|
35
|
+
JSON.stringify({
|
|
36
|
+
name,
|
|
37
|
+
version: '1.0.0',
|
|
38
|
+
type: 'module',
|
|
39
|
+
peerDependencies: { react: '*' },
|
|
40
|
+
exports: {
|
|
41
|
+
'.': { import: './index.js' },
|
|
42
|
+
'./feature': { import: './feature.js' },
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
writeFileSync(join(folder, 'index.js'), 'export const value = 1')
|
|
47
|
+
writeFileSync(join(folder, 'feature.js'), 'export const value = 2')
|
|
48
|
+
}
|
|
49
|
+
writeFileSync(
|
|
50
|
+
join(root, 'entry.js'),
|
|
51
|
+
`import { value as external } from 'external-dep'
|
|
52
|
+
import { value as feature } from 'external-dep/feature'
|
|
53
|
+
import { value as sibling } from 'external-dep-extra'
|
|
54
|
+
import { value as internal } from 'internal-dep'
|
|
55
|
+
export const value = external + feature + sibling + internal
|
|
56
|
+
`
|
|
57
|
+
)
|
|
58
|
+
const server = await createServer({
|
|
59
|
+
root,
|
|
60
|
+
configFile: false,
|
|
61
|
+
server: { middlewareMode: true },
|
|
62
|
+
optimizeDeps: { noDiscovery: true },
|
|
63
|
+
ssr: { external: [external] },
|
|
64
|
+
plugins: [autoDepOptimizePlugin({ root, exclude })],
|
|
65
|
+
environments: {
|
|
66
|
+
ssr: {
|
|
67
|
+
optimizeDeps: {
|
|
68
|
+
noDiscovery: true,
|
|
69
|
+
rolldownOptions: {
|
|
70
|
+
plugins: [
|
|
71
|
+
{
|
|
72
|
+
name: 'record-prebundled-packages',
|
|
73
|
+
load(id) {
|
|
74
|
+
if (id.startsWith(join(root, 'node_modules'))) bundled.add(id)
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
try {
|
|
84
|
+
await server.environments.ssr.listen(server)
|
|
85
|
+
expect((await server.ssrLoadModule(join(root, 'entry.js'))).value).toBe(5)
|
|
86
|
+
expect(bundled.has(join(root, 'node_modules/internal-dep/index.js'))).toBe(
|
|
87
|
+
!exclude.includes('internal-dep')
|
|
88
|
+
)
|
|
89
|
+
expect(bundled.has(join(root, 'node_modules/external-dep-extra/index.js'))).toBe(
|
|
90
|
+
true
|
|
91
|
+
)
|
|
92
|
+
expect(bundled.has(join(root, 'node_modules/external-dep/index.js'))).toBe(
|
|
93
|
+
rootBundled
|
|
94
|
+
)
|
|
95
|
+
expect(bundled.has(join(root, 'node_modules/external-dep/feature.js'))).toBe(false)
|
|
96
|
+
} finally {
|
|
97
|
+
await server.close()
|
|
98
|
+
rmSync(root, { recursive: true, force: true })
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
)
|
|
@@ -47,6 +47,7 @@ export function autoDepOptimizePlugin(props: FindDepsOptions): Plugin {
|
|
|
47
47
|
const finalConfig = await getScannedOptimizeDepsConfig({
|
|
48
48
|
...props,
|
|
49
49
|
mode: env.mode,
|
|
50
|
+
external: cfg.ssr?.external,
|
|
50
51
|
exclude: [
|
|
51
52
|
...exclude,
|
|
52
53
|
...userExcludes,
|
|
@@ -68,7 +69,10 @@ export const getSSRExternalsCachePath = (root: string) => {
|
|
|
68
69
|
return path.join(getCacheDir(root), 'deps-to-pre-bundle-for-ssr-cache.json')
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
type FindDepsOptionsByMode = FindDepsOptions & {
|
|
72
|
+
type FindDepsOptionsByMode = FindDepsOptions & {
|
|
73
|
+
mode: string
|
|
74
|
+
external?: string[] | true
|
|
75
|
+
}
|
|
72
76
|
|
|
73
77
|
export async function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMode) {
|
|
74
78
|
const result = await findDepsToOptimize(props)
|
|
@@ -80,8 +84,13 @@ export async function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMode)
|
|
|
80
84
|
const excludeArray = Array.isArray(props.exclude) ? props.exclude : [props.exclude]
|
|
81
85
|
const excludeStrings = excludeArray.filter((e): e is string => typeof e === 'string')
|
|
82
86
|
const excludeSet = new Set(excludeStrings)
|
|
87
|
+
const external = props.external
|
|
88
|
+
const shouldOptimize = (dep: string) =>
|
|
89
|
+
!excludeSet.has(dep) &&
|
|
90
|
+
external !== true &&
|
|
91
|
+
!external?.some((name) => dep === name || dep.startsWith(`${name}/`))
|
|
83
92
|
|
|
84
|
-
const filteredDeps = result.prebundleDeps.filter(
|
|
93
|
+
const filteredDeps = result.prebundleDeps.filter(shouldOptimize)
|
|
85
94
|
|
|
86
95
|
return {
|
|
87
96
|
ssr: {
|
|
@@ -89,10 +98,7 @@ export async function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMode)
|
|
|
89
98
|
include: filteredDeps,
|
|
90
99
|
exclude: EXCLUDE_LIST,
|
|
91
100
|
},
|
|
92
|
-
noExternal: [
|
|
93
|
-
...filteredDeps,
|
|
94
|
-
...result.noExternalDeps.filter((dep) => !excludeSet.has(dep)),
|
|
95
|
-
],
|
|
101
|
+
noExternal: [...filteredDeps, ...result.noExternalDeps.filter(shouldOptimize)],
|
|
96
102
|
},
|
|
97
103
|
}
|
|
98
104
|
}
|
|
@@ -2,8 +2,7 @@ import type { Plugin } from 'vite'
|
|
|
2
2
|
import { transformReactNativeCodegen } from '@vxrn/compiler'
|
|
3
3
|
|
|
4
4
|
// the transform itself lives in @vxrn/compiler so the metro transformer worker
|
|
5
|
-
// can run it too
|
|
6
|
-
// reach back into this package.
|
|
5
|
+
// can run it too without loading vxrn.
|
|
7
6
|
export { transformReactNativeCodegen } from '@vxrn/compiler'
|
|
8
7
|
|
|
9
8
|
export function rnCodegenPlugin(options?: { projectRoot?: string }): Plugin {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import FSExtra from 'fs-extra'
|
|
2
2
|
import { clearTransformCache } from '@vxrn/compiler'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type BundleCommandArgs,
|
|
6
|
-
} from '@vxrn/vite-plugin-metro/rn-commands'
|
|
3
|
+
import type { BundleCommandArgs } from '@vxrn/vite-plugin-metro/rn-commands'
|
|
4
|
+
import { resolveConfig } from 'vite'
|
|
7
5
|
import { loadEnv } from '../../exports/loadEnv'
|
|
8
6
|
import { buildNativeBundle } from '../../utils/createNativeDevEngine'
|
|
9
7
|
|
|
@@ -48,11 +46,14 @@ export async function buildBundle(
|
|
|
48
46
|
throw new Error(`Expected ctx.root to be a string, but got ${typeof root}`)
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
const
|
|
49
|
+
const viteConfig = await resolveConfig({}, 'build', 'production')
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
// resolving the config runs the metro plugin only in metro mode, and metro is
|
|
52
|
+
// an optional peer, so only that mode loads it
|
|
53
|
+
if (globalThis['__viteMetroPluginOptions__']) {
|
|
54
54
|
console.info('Using @vxrn/vite-plugin-metro to build the bundle.')
|
|
55
|
-
|
|
55
|
+
const { bundle } = await import('@vxrn/vite-plugin-metro/rn-commands')
|
|
56
|
+
await bundle.getBuildBundleFn(viteConfig)(_argv, ctx, args, bundleImpl)
|
|
56
57
|
// metroBuildBundleFn calls ensureProcessExitsAfterDelay internally,
|
|
57
58
|
// which kills lingering child handles (esbuild --service) and lets
|
|
58
59
|
// the event loop drain naturally. no extra setTimeout needed —
|