one 1.25.16 → 1.26.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.
Files changed (78) hide show
  1. package/dist/cjs/native-transforms.cjs +28 -0
  2. package/dist/cjs/native-transforms.native.js +31 -0
  3. package/dist/cjs/native-transforms.native.js.map +1 -0
  4. package/dist/cjs/router/useViteRoutes.cjs +3 -2
  5. package/dist/cjs/router/useViteRoutes.native.js +3 -2
  6. package/dist/cjs/router/useViteRoutes.native.js.map +1 -1
  7. package/dist/cjs/router/useViteRoutes.test.cjs +101 -1
  8. package/dist/cjs/router/useViteRoutes.test.native.js +109 -1
  9. package/dist/cjs/router/useViteRoutes.test.native.js.map +1 -1
  10. package/dist/cjs/vercel/build/generate/createApiServerlessFunction.cjs +45 -44
  11. package/dist/cjs/vercel/build/generate/createApiServerlessFunction.native.js +64 -39
  12. package/dist/cjs/vercel/build/generate/createApiServerlessFunction.native.js.map +1 -1
  13. package/dist/cjs/vite/one.cjs +10 -2
  14. package/dist/cjs/vite/one.native.js +13 -4
  15. package/dist/cjs/vite/one.native.js.map +1 -1
  16. package/dist/cjs/vite/plugins/clientTreeShakePlugin.cjs +597 -92
  17. package/dist/cjs/vite/plugins/clientTreeShakePlugin.native.js +1358 -113
  18. package/dist/cjs/vite/plugins/clientTreeShakePlugin.native.js.map +1 -1
  19. package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.cjs +185 -0
  20. package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.native.js +185 -0
  21. package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.native.js.map +1 -1
  22. package/dist/cjs/vite/plugins/useDomPlugin.cjs +14 -37
  23. package/dist/cjs/vite/plugins/useDomPlugin.native.js +14 -36
  24. package/dist/cjs/vite/plugins/useDomPlugin.native.js.map +1 -1
  25. package/dist/esm/native-transforms.mjs +3 -0
  26. package/dist/esm/native-transforms.mjs.map +1 -0
  27. package/dist/esm/native-transforms.native.js +3 -0
  28. package/dist/esm/native-transforms.native.js.map +1 -0
  29. package/dist/esm/router/useViteRoutes.mjs +3 -2
  30. package/dist/esm/router/useViteRoutes.mjs.map +1 -1
  31. package/dist/esm/router/useViteRoutes.native.js +3 -2
  32. package/dist/esm/router/useViteRoutes.native.js.map +1 -1
  33. package/dist/esm/router/useViteRoutes.test.mjs +77 -1
  34. package/dist/esm/router/useViteRoutes.test.mjs.map +1 -1
  35. package/dist/esm/router/useViteRoutes.test.native.js +85 -1
  36. package/dist/esm/router/useViteRoutes.test.native.js.map +1 -1
  37. package/dist/esm/vercel/build/generate/createApiServerlessFunction.mjs +45 -44
  38. package/dist/esm/vercel/build/generate/createApiServerlessFunction.mjs.map +1 -1
  39. package/dist/esm/vercel/build/generate/createApiServerlessFunction.native.js +64 -39
  40. package/dist/esm/vercel/build/generate/createApiServerlessFunction.native.js.map +1 -1
  41. package/dist/esm/vite/one.mjs +10 -2
  42. package/dist/esm/vite/one.mjs.map +1 -1
  43. package/dist/esm/vite/one.native.js +13 -4
  44. package/dist/esm/vite/one.native.js.map +1 -1
  45. package/dist/esm/vite/plugins/clientTreeShakePlugin.mjs +597 -92
  46. package/dist/esm/vite/plugins/clientTreeShakePlugin.mjs.map +1 -1
  47. package/dist/esm/vite/plugins/clientTreeShakePlugin.native.js +1358 -113
  48. package/dist/esm/vite/plugins/clientTreeShakePlugin.native.js.map +1 -1
  49. package/dist/esm/vite/plugins/clientTreeShakePlugin.test.mjs +185 -0
  50. package/dist/esm/vite/plugins/clientTreeShakePlugin.test.mjs.map +1 -1
  51. package/dist/esm/vite/plugins/clientTreeShakePlugin.test.native.js +185 -0
  52. package/dist/esm/vite/plugins/clientTreeShakePlugin.test.native.js.map +1 -1
  53. package/dist/esm/vite/plugins/useDomPlugin.mjs +13 -25
  54. package/dist/esm/vite/plugins/useDomPlugin.mjs.map +1 -1
  55. package/dist/esm/vite/plugins/useDomPlugin.native.js +14 -25
  56. package/dist/esm/vite/plugins/useDomPlugin.native.js.map +1 -1
  57. package/package.json +15 -10
  58. package/src/createApp.tsx +0 -1
  59. package/src/headless.tsx +0 -1
  60. package/src/native-transforms.ts +11 -0
  61. package/src/router/useViteRoutes.test.ts +91 -0
  62. package/src/router/useViteRoutes.tsx +7 -4
  63. package/src/vercel/build/generate/createApiServerlessFunction.ts +65 -112
  64. package/src/vite/one.ts +22 -2
  65. package/src/vite/plugins/clientTreeShakePlugin.test.ts +201 -0
  66. package/src/vite/plugins/clientTreeShakePlugin.ts +732 -152
  67. package/src/vite/plugins/useDomPlugin.ts +20 -32
  68. package/src/vite/types.ts +60 -5
  69. package/types/native-transforms.d.ts +11 -0
  70. package/types/native-transforms.d.ts.map +1 -0
  71. package/types/router/useViteRoutes.d.ts.map +1 -1
  72. package/types/vercel/build/generate/createApiServerlessFunction.d.ts.map +1 -1
  73. package/types/vite/one.d.ts.map +1 -1
  74. package/types/vite/plugins/clientTreeShakePlugin.d.ts +1 -9
  75. package/types/vite/plugins/clientTreeShakePlugin.d.ts.map +1 -1
  76. package/types/vite/plugins/useDomPlugin.d.ts.map +1 -1
  77. package/types/vite/types.d.ts +57 -5
  78. package/types/vite/types.d.ts.map +1 -1
@@ -1,5 +1,4 @@
1
- import { extname } from "path";
2
- import * as swc from "@swc/core";
1
+ import { parseSync } from "oxc-parser";
3
2
  function useDOMPlugin() {
4
3
  return {
5
4
  name: "one-vite-dom-plugin",
@@ -7,37 +6,27 @@ function useDOMPlugin() {
7
6
  if (!code.includes("use dom")) {
8
7
  return;
9
8
  }
10
- var ext = extname(id);
11
- var mod = swc.parseSync(code, parseOpts(ext));
12
- var hasUseDom = false;
13
- for (var i = 0; i < mod.body.length; ++i) {
14
- var item = mod.body[i];
15
- if (item.type === "ExpressionStatement") {
16
- if (item.expression.type === "StringLiteral") {
17
- if (item.expression.value === "use dom") {
9
+ try {
10
+ var parsed = parseSync(id, code);
11
+ var hasUseDom = false;
12
+ for (var i = 0; i < parsed.program.body.length; ++i) {
13
+ var item = parsed.program.body[i];
14
+ if (item.type === "ExpressionStatement") {
15
+ var _item_expression, _item_expression1, _item_expression2, _item_expression3;
16
+ if (item.directive === "use dom" || ((_item_expression = item.expression) === null || _item_expression === void 0 ? void 0 : _item_expression.type) === "Literal" && ((_item_expression1 = item.expression) === null || _item_expression1 === void 0 ? void 0 : _item_expression1.value) === "use dom" || ((_item_expression2 = item.expression) === null || _item_expression2 === void 0 ? void 0 : _item_expression2.type) === "StringLiteral" && ((_item_expression3 = item.expression) === null || _item_expression3 === void 0 ? void 0 : _item_expression3.value) === "use dom") {
18
17
  hasUseDom = true;
19
18
  break;
20
19
  }
21
20
  }
22
- } else {}
23
- }
24
- if (!hasUseDom) {
21
+ }
22
+ if (!hasUseDom) {
23
+ return;
24
+ }
25
+ } catch (unused) {
25
26
  return;
26
27
  }
27
28
  }
28
29
  };
29
30
  }
30
- var parseOpts = function (ext) {
31
- if (ext === ".ts" || ext === ".tsx") {
32
- return {
33
- syntax: "typescript",
34
- tsx: ext.endsWith("x")
35
- };
36
- }
37
- return {
38
- syntax: "ecmascript",
39
- jsx: ext.endsWith("x")
40
- };
41
- };
42
31
  export { useDOMPlugin };
43
32
  //# sourceMappingURL=useDomPlugin.native.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["extname","swc","useDOMPlugin","name","transform","code","id","options","includes","ext","mod","parseSync","parseOpts","hasUseDom","i","body","length","item","type","expression","value","syntax","tsx","endsWith","jsx"],"sources":["../../../../src/vite/plugins/useDomPlugin.ts"],"sourcesContent":[null],"mappings":"AAwBA,SAASA,OAAA,QAAe;AACxB,YAAYC,GAAA,MAAS;AAOd,SAASC,aAAA,EAAuB;EACrC,OAAO;IACLC,IAAA,EAAM;IAEN,MAAMC,UAAUC,IAAA,EAAMC,EAAA,EAAIC,OAAA,EAAS;MACjC,IAAI,CAACF,IAAA,CAAKG,QAAA,CAAS,SAAS,GAAG;QAC7B;MACF;MAEA,IAAAC,GAAM,GAAAT,OAAM,CAAAM,EAAQ;MACpB,IAAAI,GAAM,GAAAT,GAAM,CAAAU,SAAI,CAAAN,IAAU,EAAAO,SAAM,CAAAH,GAAU,EAAG;MAE7C,IAAII,SAAA,GAAY;MAEhB,SAASC,CAAA,GAAI,GAAGA,CAAA,GAAIJ,GAAA,CAAIK,IAAA,CAAKC,MAAA,EAAQ,EAAEF,CAAA,EAAG;QACxC,IAAAG,IAAM,GAAAP,GAAO,CAAAK,IAAI,CAAAD,CAAA,CAAK;QACtB,IAAIG,IAAA,CAAKC,IAAA,KAAS,uBAAuB;UACvC,IAAID,IAAA,CAAKE,UAAA,CAAWD,IAAA,KAAS,iBAAiB;YAC5C,IAAID,IAAA,CAAKE,UAAA,CAAWC,KAAA,KAAU,WAAW;cACvCP,SAAA,GAAY;cACZ;YACF;UACF;QACF,OAAO,CAGP;MACF;MAEA,IAAI,CAACA,SAAA,EAAW;QACd;MACF;IAGF;EACF;AACF;AAEA,IAAAD,SAAM,YAAAA,CAAaH,GAAgB;EACjC,IAAIA,GAAA,KAAQ,SAASA,GAAA,KAAQ,QAAQ;IACnC,OAAO;MACLY,MAAA,EAAQ;MACRC,GAAA,EAAKb,GAAA,CAAIc,QAAA,CAAS,GAAG;IACvB;EACF;EAGA,OAAO;IACLF,MAAA,EAAQ;IACRG,GAAA,EAAKf,GAAA,CAAIc,QAAA,CAAS,GAAG;EACvB;AACF","ignoreList":[]}
1
+ {"version":3,"names":["parseSync","useDOMPlugin","name","transform","code","id","options","includes","parsed","hasUseDom","i","program","body","length","item","type","_item_expression","_item_expression1","_item_expression2","_item_expression3","directive","expression","value","unused"],"sources":["../../../../src/vite/plugins/useDomPlugin.ts"],"sourcesContent":[null],"mappings":"AAyBA,SAASA,SAAA,QAAiB;AAOnB,SAASC,aAAA,EAAuB;EACrC,OAAO;IACLC,IAAA,EAAM;IAEN,MAAMC,UAAUC,IAAA,EAAMC,EAAA,EAAIC,OAAA,EAAS;MACjC,IAAI,CAACF,IAAA,CAAKG,QAAA,CAAS,SAAS,GAAG;QAC7B;MACF;MAEA,IAAI;QACF,IAAAC,MAAM,GAAAR,SAAS,CAAAK,EAAU,EAAAD,IAAI;QAC7B,IAAIK,SAAA,GAAY;QAEhB,SAASC,CAAA,GAAI,GAAGA,CAAA,GAAIF,MAAA,CAAOG,OAAA,CAAQC,IAAA,CAAKC,MAAA,EAAQ,EAAEH,CAAA,EAAG;UACnD,IAAAI,IAAM,GAAAN,MAAO,CAAAG,OAAO,CAAAC,IAAQ,CAAAF,CAAA,CAAK;UACjC,IAAII,IAAA,CAAKC,IAAA,KAAS,uBAAuB;YACvC,IACGC,gBAAa,EAAAC,iBACZ,EAAaC,iBAAY,EAAAC,iBACxB;YAIH,IAAAL,IAAA,CAAAM,SAAY,oBAAAJ,gBAAA,GAAAF,IAAA,CAAAO,UAAA,cAAAL,gBAAA,uBAAAA,gBAAA,CAAAD,IAAA,qBAAAE,iBAAA,GAAAH,IAAA,CAAAO,UAAA,cAAAJ,iBAAA,uBAAAA,iBAAA,CAAAK,KAAA,qBAAAJ,iBAAA,GAAAJ,IAAA,CAAAO,UAAA,cAAAH,iBAAA,uBAAAA,iBAAA,CAAAH,IAAA,2BAAAI,iBAAA,GAAAL,IAAA,CAAAO,UAAA,cAAAF,iBAAA,uBAAAA,iBAAA,CAAAG,KAAA;cACZb,SAAA;cACF;YACF;UACF;QAEA;QACE,KAAAA,SAAA;UACF;QAGF;MACE,SAAAc,MAAA;QACF;MACF;IACF;EACF","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "1.25.16",
3
+ "version": "1.26.0",
4
4
  "license": "BSD-3-Clause",
5
5
  "sideEffects": [
6
6
  "setup.mjs",
@@ -91,6 +91,11 @@
91
91
  "import": "./dist/esm/babel-preset/index.mjs",
92
92
  "require": "./dist/cjs/babel-preset/index.cjs"
93
93
  },
94
+ "./native-transforms": {
95
+ "types": "./types/native-transforms.d.ts",
96
+ "import": "./dist/esm/native-transforms.mjs",
97
+ "require": "./dist/cjs/native-transforms.cjs"
98
+ },
94
99
  "./metro-config": {
95
100
  "types": "./types/metro-config/withOne.d.ts",
96
101
  "import": "./dist/esm/metro-config/withOne.mjs",
@@ -191,20 +196,19 @@
191
196
  "@react-navigation/native-stack": "~7.18.8",
192
197
  "@react-navigation/routers": "~7.6.4",
193
198
  "@cloudflare/vite-plugin": "^1.33.1",
194
- "@swc/core": "^1.14.0",
195
199
  "@ungap/structured-clone": "^1.2.0",
196
- "@vxrn/color-scheme": "1.25.16",
197
- "@vxrn/compiler": "1.25.16",
198
- "@vxrn/resolve": "1.25.16",
199
- "@vxrn/tslib-lite": "1.25.16",
200
- "@vxrn/use-isomorphic-layout-effect": "1.25.16",
201
- "@vxrn/vite-plugin-metro": "1.25.16",
200
+ "@vxrn/color-scheme": "1.26.0",
201
+ "@vxrn/compiler": "1.26.0",
202
+ "@vxrn/resolve": "1.26.0",
203
+ "@vxrn/tslib-lite": "1.26.0",
204
+ "@vxrn/use-isomorphic-layout-effect": "1.26.0",
205
+ "@vxrn/vite-plugin-metro": "1.26.0",
202
206
  "babel-dead-code-elimination": "1.0.10",
203
207
  "babel-plugin-module-resolver": "^5.0.2",
204
208
  "babel-preset-expo": "~57.0.5",
205
209
  "citty": "^0.1.6",
206
210
  "core-js": "^3.38.1",
207
- "create-vxrn": "1.25.16",
211
+ "create-vxrn": "1.26.0",
208
212
  "escape-string-regexp": "^5.0.0",
209
213
  "expo-linking": "~57.0.5",
210
214
  "expo-modules-core": "~57.0.10",
@@ -213,6 +217,7 @@
213
217
  "fs-extra": "^11.2.0",
214
218
  "hono": "^4.12.8",
215
219
  "lightningcss": "^1.30.1",
220
+ "magic-string": "^0.30.21",
216
221
  "micromatch": "^4.0.8",
217
222
  "nanoid": "^3.3.18",
218
223
  "oxc-parser": "^0.112.0",
@@ -224,7 +229,7 @@
224
229
  "tsconfig-paths": "^4",
225
230
  "use-latest-callback": "^0.2.3",
226
231
  "vite": "^8.2.2",
227
- "vxrn": "1.25.16",
232
+ "vxrn": "1.26.0",
228
233
  "web-streams-polyfill": "^4.3.0",
229
234
  "ws": "^8.21.0",
230
235
  "xxhashjs": "^0.2.2"
package/src/createApp.tsx CHANGED
@@ -298,4 +298,3 @@ export function createApp(options: CreateAppProps) {
298
298
  }
299
299
  })
300
300
  }
301
-
package/src/headless.tsx CHANGED
@@ -57,4 +57,3 @@ export function createApp(options: CreateHeadlessAppProps) {
57
57
  }
58
58
  })
59
59
  }
60
-
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The transforms one needs applied to native bundles, in a form the metro
3
+ * transformer worker can reach.
4
+ *
5
+ * The worker lives in `@vxrn/vite-plugin-metro`, which one depends on, so it
6
+ * cannot import from here directly — it resolves this subpath out of the user's
7
+ * own `one` install instead. Everything re-exported here must stay free of
8
+ * vite: the worker runs in a bare node process.
9
+ */
10
+
11
+ export { transformTreeShakeClient } from './vite/plugins/clientTreeShakePlugin'
@@ -2,22 +2,113 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
2
2
 
3
3
  const mocks = vi.hoisted(() => ({
4
4
  hmrImport: vi.fn(),
5
+ diagnoseRouteLoadFailure: vi.fn(),
5
6
  }))
6
7
 
7
8
  vi.mock('./hmrImport', () => ({
8
9
  hmrImport: mocks.hmrImport,
9
10
  }))
10
11
 
12
+ vi.mock('./diagnoseRouteLoadFailure', () => ({
13
+ diagnoseRouteLoadFailure: mocks.diagnoseRouteLoadFailure,
14
+ }))
15
+
11
16
  import { globbedRoutesToRouteContext } from './useViteRoutes'
12
17
 
13
18
  const realWindow = (globalThis as any).window
14
19
 
15
20
  afterEach(() => {
16
21
  vi.unstubAllEnvs()
22
+ vi.unstubAllGlobals()
23
+ vi.restoreAllMocks()
17
24
  mocks.hmrImport.mockReset()
25
+ mocks.diagnoseRouteLoadFailure.mockReset()
18
26
  ;(globalThis as any).window = realWindow
19
27
  })
20
28
 
29
+ describe('route load error diagnostics', () => {
30
+ it.each(['native', 'server', 'web shim', 'browser'])(
31
+ '%s preserves the original route error',
32
+ async (environment) => {
33
+ vi.stubEnv('NODE_ENV', 'development')
34
+ vi.stubEnv('TAMAGUI_TARGET', environment === 'native' ? 'native' : 'web')
35
+ vi.stubGlobal(
36
+ 'window',
37
+ environment === 'server'
38
+ ? undefined
39
+ : environment === 'browser'
40
+ ? {
41
+ history: {},
42
+ location: { href: 'http://localhost/', origin: 'http://localhost' },
43
+ }
44
+ : {}
45
+ )
46
+ const report = vi.fn()
47
+ vi.stubGlobal('__oneReportRouteLoadError', report)
48
+ vi.spyOn(console, 'error').mockImplementation(() => {})
49
+ mocks.diagnoseRouteLoadFailure.mockResolvedValue(null)
50
+ vi.resetModules()
51
+ const { globbedRoutesToRouteContext } = await import('./useViteRoutes')
52
+ const error = new Error('original route failure')
53
+ const context = globbedRoutesToRouteContext(
54
+ { '/app/index.tsx': () => Promise.reject(error) },
55
+ 'app'
56
+ )
57
+
58
+ const route = await resolveRoute(context)
59
+
60
+ expect(route.default()).toBeNull()
61
+ expect(report).toHaveBeenCalledExactlyOnceWith({
62
+ id: './index.tsx',
63
+ message: error.message,
64
+ stack: error.stack,
65
+ })
66
+ if (environment === 'browser') {
67
+ expect(mocks.diagnoseRouteLoadFailure).toHaveBeenCalledExactlyOnceWith(
68
+ './index.tsx',
69
+ '/app/index.tsx'
70
+ )
71
+ } else {
72
+ expect(mocks.diagnoseRouteLoadFailure).not.toHaveBeenCalled()
73
+ }
74
+ }
75
+ )
76
+
77
+ it('keeps the original report when a browser diagnostic rejects', async () => {
78
+ vi.stubEnv('NODE_ENV', 'development')
79
+ vi.stubEnv('TAMAGUI_TARGET', 'web')
80
+ vi.stubGlobal('window', {
81
+ history: {},
82
+ location: { href: 'http://localhost/', origin: 'http://localhost' },
83
+ })
84
+ const report = vi.fn()
85
+ vi.stubGlobal('__oneReportRouteLoadError', report)
86
+ vi.spyOn(console, 'error').mockImplementation(() => {})
87
+ mocks.diagnoseRouteLoadFailure.mockRejectedValue(new Error('diagnostic failed'))
88
+ vi.resetModules()
89
+ const { globbedRoutesToRouteContext } = await import('./useViteRoutes')
90
+ const error = new Error('original route failure')
91
+ const context = globbedRoutesToRouteContext(
92
+ { '/app/index.tsx': () => Promise.reject(error) },
93
+ 'app'
94
+ )
95
+
96
+ const route = await resolveRoute(context)
97
+ await new Promise((resolve) => setImmediate(resolve))
98
+
99
+ expect(route.default()).toBeNull()
100
+ expect(report).toHaveBeenCalledExactlyOnceWith({
101
+ id: './index.tsx',
102
+ message: error.message,
103
+ stack: error.stack,
104
+ })
105
+ expect(mocks.diagnoseRouteLoadFailure).toHaveBeenCalledExactlyOnceWith(
106
+ './index.tsx',
107
+ '/app/index.tsx'
108
+ )
109
+ })
110
+ })
111
+
21
112
  async function resolveRoute(context: ReturnType<typeof globbedRoutesToRouteContext>) {
22
113
  try {
23
114
  return context('./index.tsx')
@@ -1,5 +1,6 @@
1
1
  import type { GlobbedRouteImports } from '../types'
2
2
  import type { One } from '../vite/types'
3
+ import { hasWebHistory } from '../constants'
3
4
  import { handleSkewError, isChunkLoadError } from '../utils/dynamicImport'
4
5
  import { diagnoseRouteLoadFailure } from './diagnoseRouteLoadFailure'
5
6
  import { hmrImport } from './hmrImport'
@@ -316,10 +317,12 @@ export function globbedRoutesToRouteContext(
316
317
  // `Importing a module script failed` names neither the module nor
317
318
  // the reason. when a browser content blocker refused one of the
318
319
  // route's imports, walking the graph finds the exact file.
319
- if (process.env.NODE_ENV === 'development' && routePaths[id]) {
320
- diagnoseRouteLoadFailure(id, routePaths[id]).then((message) => {
321
- if (message) console.error(message)
322
- })
320
+ if (hasWebHistory && routePaths[id]) {
321
+ diagnoseRouteLoadFailure(id, routePaths[id])
322
+ .then((message) => {
323
+ if (message) console.error(message)
324
+ })
325
+ .catch(() => {})
323
326
  }
324
327
  }
325
328
  loadedRoutes[id] = {
@@ -1,8 +1,6 @@
1
1
  import { dirname, join, resolve } from 'node:path'
2
- import generator from '@babel/generator'
3
- import parser from '@babel/parser'
4
- import traverse from '@babel/traverse'
5
- import t from '@babel/types'
2
+ import { parseSync } from 'oxc-parser'
3
+ import MagicString from 'magic-string'
6
4
  import { resolvePath } from '@vxrn/resolve'
7
5
  import FSExtra from 'fs-extra'
8
6
 
@@ -86,114 +84,69 @@ export async function createApiServerlessFunction(
86
84
  * So we need to wrap the handler function to parse the params from the request,
87
85
  * and pass them to the handler function.
88
86
  */
89
- function wrapHandlerFunctions(code) {
90
- const ast = parser.parse(code, {
91
- sourceType: 'module',
92
- })
93
-
94
- // TODO: idk why the TypeScript type does not match the actual type. Seems that we should use `traverse.default`, but TypeScript thinks we should use `traverse` directly.
95
- ;((traverse as any).default as typeof traverse)(ast, {
96
- FunctionDeclaration(path) {
97
- const { node } = path
98
-
99
- const functionNamesToHandle = [
100
- 'GET',
101
- 'POST',
102
- 'PUT',
103
- 'PATCH',
104
- 'DELETE',
105
- 'HEAD',
106
- 'OPTIONS',
107
- // TODO: more possibilities?
108
- ]
109
-
110
- if (!node.id || !functionNamesToHandle.includes(node.id.name)) return
111
- // TODO: may need to also check if the function is export in any way, if
112
- // the isn't exported at all, we can skip.
113
- if (node.extra && node.extra.isWrapper) return
114
- if (node.extra && node.extra.isWrapped) return
115
-
116
- const originalName = `orig_${node.id.name}`
117
-
118
- const originalFunction = t.functionDeclaration(
119
- t.identifier(originalName),
120
- node.params,
121
- node.body,
122
- node.generator,
123
- node.async
124
- )
87
+ function wrapHandlerFunctions(code: string): string {
88
+ let parsed: any
89
+ try {
90
+ parsed = parseSync('api.js', code)
91
+ } catch {
92
+ return code
93
+ }
125
94
 
126
- /* The first argument of the handler function, which is the request object. */
127
- const requestIdentifier = t.identifier('request')
128
- const wrapperParams = [requestIdentifier]
129
-
130
- /* A local variable in the wrapper function to hold the URL object. */
131
- const urlIdentifier = t.identifier('url')
132
- /* A local variable in the wrapper function to hold parsed params. */
133
- const paramsIdentifier = t.identifier('params')
134
-
135
- const urlDecl = t.variableDeclaration('const', [
136
- t.variableDeclarator(
137
- urlIdentifier,
138
- t.newExpression(t.identifier('URL') /* Node.js global */, [
139
- t.memberExpression(requestIdentifier, t.identifier('url')) /* request.url */,
140
- ])
141
- ),
142
- ])
143
-
144
- const paramsDecl = t.variableDeclaration('const', [
145
- t.variableDeclarator(
146
- paramsIdentifier,
147
- t.callExpression(
148
- t.memberExpression(t.identifier('Object'), t.identifier('fromEntries')),
149
- [
150
- t.callExpression(
151
- t.memberExpression(
152
- t.memberExpression(
153
- urlIdentifier,
154
- t.identifier('searchParams')
155
- ) /* url.searchParams */,
156
- t.identifier('entries')
157
- ),
158
- []
159
- ),
160
- ]
161
- )
162
- ),
163
- ])
164
-
165
- const callOrigFnStatement = t.callExpression(t.identifier(originalName), [
166
- requestIdentifier,
167
- t.objectExpression([t.objectProperty(t.identifier('params'), paramsIdentifier)]),
168
- ])
169
-
170
- const wrapperFunction = t.functionDeclaration(
171
- t.identifier(node.id.name + ''),
172
- wrapperParams,
173
- t.blockStatement([urlDecl, paramsDecl, t.returnStatement(callOrigFnStatement)])
174
- // No need to care if the wrapper function should be async,
175
- // since we didn't use any await in the wrapper function, and we'll
176
- // just return what the original function returns.
177
- )
95
+ if (!parsed?.program) return code
96
+
97
+ const functionNamesToHandle = new Set([
98
+ 'GET',
99
+ 'POST',
100
+ 'PUT',
101
+ 'PATCH',
102
+ 'DELETE',
103
+ 'HEAD',
104
+ 'OPTIONS',
105
+ ])
106
+
107
+ const ms = new MagicString(code)
108
+ const wrappersToAppend: string[] = []
109
+
110
+ for (const item of parsed.program.body || []) {
111
+ let fnDecl: any = null
112
+ let isExported = false
113
+ let exportNode: any = null
114
+
115
+ if (
116
+ item.type === 'ExportNamedDeclaration' &&
117
+ item.declaration?.type === 'FunctionDeclaration'
118
+ ) {
119
+ fnDecl = item.declaration
120
+ isExported = true
121
+ exportNode = item
122
+ } else if (item.type === 'FunctionDeclaration') {
123
+ fnDecl = item
124
+ isExported = false
125
+ }
126
+
127
+ if (!fnDecl || !fnDecl.id?.name || !functionNamesToHandle.has(fnDecl.id.name)) {
128
+ continue
129
+ }
130
+
131
+ const name = fnDecl.id.name
132
+ const originalName = `orig_${name}`
133
+
134
+ if (isExported) {
135
+ ms.remove(exportNode.start, fnDecl.start)
136
+ }
137
+
138
+ ms.overwrite(fnDecl.id.start, fnDecl.id.end, originalName)
139
+
140
+ const exportPrefix = isExported ? 'export ' : ''
141
+ wrappersToAppend.push(
142
+ `\n${exportPrefix}function ${name}(request) {\n const url = new URL(request.url);\n const params = Object.fromEntries(url.searchParams.entries());\n return ${originalName}(request, { params });\n}`
143
+ )
144
+ }
145
+
146
+ if (wrappersToAppend.length > 0) {
147
+ ms.append(wrappersToAppend.join('\n'))
148
+ return ms.toString()
149
+ }
178
150
 
179
- node.extra = node.extra || {}
180
- node.extra.isWrapped = true
181
-
182
- wrapperFunction.extra = wrapperFunction.extra || {}
183
- wrapperFunction.extra.isWrapper = true
184
-
185
- if (path.parentPath.isExportNamedDeclaration()) {
186
- path.replaceWithMultiple([
187
- originalFunction,
188
- t.exportNamedDeclaration(wrapperFunction, []),
189
- ])
190
- } else {
191
- path.replaceWithMultiple([originalFunction, wrapperFunction])
192
- }
193
- },
194
- })
195
-
196
- // TODO: idk why the TypeScript type does not match the actual type. Seems that we should use `generator.default`, but TypeScript thinks we should use `generator` directly.
197
- const output = ((generator as any).default as typeof generator)(ast, {}).code
198
- return output
151
+ return code
199
152
  }
package/src/vite/one.ts CHANGED
@@ -87,7 +87,19 @@ export function one(options: One.PluginOptions = {}): PluginOption {
87
87
  | (MetroOptions & ExpoManifestRequestHandlerPluginPluginOptions)
88
88
  | null = (() => {
89
89
  if (nativeDisabled) return null
90
- if (nativeOptions?.bundler !== 'metro' && !process.env.ONE_METRO_MODE) return null
90
+ if (nativeOptions?.bundler !== 'metro' && !process.env.ONE_METRO_MODE) {
91
+ // the vite native bundler runs no babel and has nowhere to put a babel
92
+ // plugin, so anything configured here is dropped. that is silent by
93
+ // default, and a dropped plugin usually means a broken bundle rather than
94
+ // a missing optimization, so name it.
95
+ const dropped = nativeOptions?.bundlerOptions as any
96
+ if (dropped?.babelConfig || dropped?.babelConfigOverrides) {
97
+ console.warn(
98
+ `[one] native.bundlerOptions configures babel plugins, but native.bundler is "${nativeOptions?.bundler}", which runs no babel. Those plugins will not run and whatever they were rewriting will reach the bundle untransformed.`
99
+ )
100
+ }
101
+ return null
102
+ }
91
103
 
92
104
  if (process.env.ONE_METRO_MODE) {
93
105
  console.info('ONE_METRO_MODE environment variable is set, enabling Metro mode')
@@ -842,11 +854,19 @@ export function one(options: One.PluginOptions = {}): PluginOption {
842
854
  // TODO move to single config and through environments
843
855
  const nativeWebDevAndProdPlugsin: Plugin[] = [clientTreeShakePlugin({ routerRoot })]
844
856
 
845
- // TODO make this passed into vxrn through real API
857
+ // TODO move this handoff off globalThis, like the other one -> vxrn options
846
858
  if (!nativeDisabled) {
859
+ // the native bundler is its own rolldown instance rather than a vite
860
+ // environment, so nothing in `config.plugins` reaches it. one's own plugins
861
+ // go first, then whatever the app opted in through native.bundlerOptions.
862
+ const viteBundlerOptions =
863
+ nativeOptions?.bundler === 'metro' ? undefined : (nativeOptions?.bundlerOptions as any)
864
+
847
865
  globalThis.__vxrnAddNativePlugins = [
848
866
  clientTreeShakePlugin({ runtime: 'rolldown', routerRoot }),
867
+ ...(viteBundlerOptions?.plugins ?? []),
849
868
  ]
869
+ ;(globalThis as any).__vxrnNativeUserDefine = viteBundlerOptions?.define
850
870
  }
851
871
  globalThis.__vxrnAddWebPluginsProd = devAndProdPlugins
852
872