rwsdk 1.4.1 → 1.5.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 (34) hide show
  1. package/dist/lib/e2e/environment.d.mts +1 -1
  2. package/dist/lib/e2e/environment.mjs +28 -1
  3. package/dist/lib/e2e/tarball.d.mts +2 -1
  4. package/dist/lib/e2e/tarball.mjs +2 -2
  5. package/dist/lib/e2e/testHarness.d.mts +5 -0
  6. package/dist/lib/e2e/testHarness.mjs +7 -1
  7. package/dist/lib/smokeTests/environment.mjs +1 -0
  8. package/dist/lib/smokeTests/types.d.mts +1 -0
  9. package/dist/runtime/render/assembleDocument.js +1 -1
  10. package/dist/runtime/render/renderDocumentHtmlStream.js +1 -1
  11. package/dist/runtime/render/stylesheets.js +22 -0
  12. package/dist/scripts/smoke-test.mjs +8 -0
  13. package/dist/vite/addOptimizeDepsPlugin.d.mts +10 -0
  14. package/dist/vite/addOptimizeDepsPlugin.mjs +6 -0
  15. package/dist/vite/buildApp.mjs +54 -15
  16. package/dist/vite/configPlugin.mjs +27 -20
  17. package/dist/vite/createDirectiveLookupPlugin.mjs +11 -15
  18. package/dist/vite/directiveModulesDevPlugin.mjs +86 -103
  19. package/dist/vite/directivesFilteringPlugin.mjs +55 -14
  20. package/dist/vite/directivesPlugin.mjs +44 -87
  21. package/dist/vite/knownDepsResolverPlugin.mjs +29 -36
  22. package/dist/vite/linkerPlugin.mjs +1 -1
  23. package/dist/vite/prismaPlugin.mjs +7 -10
  24. package/dist/vite/redwoodPlugin.mjs +5 -4
  25. package/dist/vite/runDirectivesScan.mjs +1 -1
  26. package/dist/vite/ssrBridgePlugin.mjs +11 -21
  27. package/dist/vite/statePlugin.mjs +8 -15
  28. package/dist/vite/transformJsxScriptTagsPlugin.mjs +3 -3
  29. package/dist/vite/viteCompat.d.mts +5 -0
  30. package/dist/vite/viteCompat.mjs +378 -0
  31. package/dist/vite/viteCompat.test.d.mts +1 -0
  32. package/dist/vite/viteCompat.test.mjs +72 -0
  33. package/dist/vite/vitePreamblePlugin.d.mts +2 -153
  34. package/package.json +8 -7
@@ -1,6 +1,7 @@
1
1
  import debug from "debug";
2
2
  import MagicString from "magic-string";
3
3
  import { INTERMEDIATE_SSR_BRIDGE_PATH } from "../lib/constants.mjs";
4
+ import { addOptimizeDepsPlugin } from "./addOptimizeDepsPlugin.mjs";
4
5
  import { externalModulesSet } from "./constants.mjs";
5
6
  import { findSsrImportCallSites } from "./findSsrSpecifiers.mjs";
6
7
  import { isVirtualSsrModuleId, normalizeVirtualSsrModuleId, VIRTUAL_SSR_PREFIX, } from "./ssrVirtualModule.mjs";
@@ -49,32 +50,21 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
49
50
  configEnvironment(env, config) {
50
51
  log("Configuring environment: env=%s", env);
51
52
  if (env === "worker") {
52
- // Configure esbuild to mark rwsdk/__ssr paths as external for worker environment
53
- log("Configuring esbuild options for worker environment");
53
+ log("Configuring rolldown options for worker environment");
54
54
  config.optimizeDeps ??= {};
55
- config.optimizeDeps.esbuildOptions ??= {};
56
- config.optimizeDeps.esbuildOptions.plugins ??= [];
57
55
  config.optimizeDeps.include ??= [];
58
- config.optimizeDeps.esbuildOptions.plugins.push({
56
+ addOptimizeDepsPlugin(config, {
59
57
  name: "rwsdk-ssr-external",
60
- setup(build) {
61
- log("Setting up esbuild plugin to mark rwsdk/__ssr paths as external for worker");
62
- build.onResolve({ filter: /.*$/ }, (args) => {
63
- process.env.VERBOSE &&
64
- log("Esbuild onResolve called for path=%s, args=%O", args.path, args);
65
- if (args.path === "rwsdk/__ssr_bridge" ||
66
- isVirtualSsrModuleId(args.path)) {
67
- const path = normalizeVirtualSsrModuleId(args.path) ?? args.path;
68
- log("Marking as external: %s", path);
69
- return {
70
- path,
71
- external: true,
72
- };
73
- }
74
- });
58
+ resolveId(id) {
59
+ if (id === "rwsdk/__ssr_bridge" ||
60
+ isVirtualSsrModuleId(id)) {
61
+ const path = normalizeVirtualSsrModuleId(id) ?? id;
62
+ log("Marking as external: %s", path);
63
+ return { id: path, external: true };
64
+ }
75
65
  },
76
66
  });
77
- log("Worker environment esbuild configuration complete");
67
+ log("Worker environment rolldown configuration complete");
78
68
  }
79
69
  },
80
70
  async resolveId(id, importer, options) {
@@ -1,6 +1,7 @@
1
1
  import debug from "debug";
2
2
  import fs from "node:fs/promises";
3
3
  import { RW_STATE_EXPORT_PATH } from "../lib/constants.mjs";
4
+ import { addOptimizeDepsPlugin } from "./addOptimizeDepsPlugin.mjs";
4
5
  import { maybeResolveEnvImport } from "./envResolvers.mjs";
5
6
  const log = debug("rwsdk:vite:state-plugin");
6
7
  const VIRTUAL_STATE_PREFIX = "virtual:rwsdk:state:";
@@ -22,22 +23,14 @@ export const statePlugin = ({ projectRootDir, }) => {
22
23
  },
23
24
  configEnvironment(env, config) {
24
25
  if (env === "worker") {
25
- config.optimizeDeps ??= {};
26
- config.optimizeDeps.esbuildOptions ??= {};
27
- config.optimizeDeps.esbuildOptions.plugins ??= [];
28
- config.optimizeDeps.esbuildOptions.plugins.push({
26
+ const stateFilter = new RegExp(`^(${RW_STATE_EXPORT_PATH}|${VIRTUAL_STATE_PREFIX}.*)$`);
27
+ addOptimizeDepsPlugin(config, {
29
28
  name: "rwsdk-state-external",
30
- setup(build) {
31
- build.onResolve({
32
- // context(justinvdm, 13 Oct 2025): Vite dep optimizer slugifies the export path
33
- filter: new RegExp(`^(${RW_STATE_EXPORT_PATH}|${VIRTUAL_STATE_PREFIX}.*)$`),
34
- }, (args) => {
35
- log("Marking as external: %s", args.path);
36
- return {
37
- path: args.path,
38
- external: true,
39
- };
40
- });
29
+ resolveId(id) {
30
+ if (stateFilter.test(id)) {
31
+ log("Marking as external: %s", id);
32
+ return { id, external: true };
33
+ }
41
34
  },
42
35
  });
43
36
  }
@@ -106,9 +106,9 @@ export async function transformJsxScriptTagsCode(code, clientEntryPoints, manife
106
106
  .forEach((callExpr) => {
107
107
  const expression = callExpr.getExpression();
108
108
  const expressionText = expression.getText();
109
- if (expressionText !== "jsx" &&
110
- expressionText !== "jsxs" &&
111
- expressionText !== "jsxDEV") {
109
+ if (!expressionText.endsWith("jsx") &&
110
+ !expressionText.endsWith("jsxs") &&
111
+ !expressionText.endsWith("jsxDEV")) {
112
112
  return;
113
113
  }
114
114
  const args = callExpr.getArguments();
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from "vite";
2
+ export interface CompatOptions {
3
+ viteVersion?: number;
4
+ }
5
+ export declare function compatTransform(plugins: Plugin[], options?: CompatOptions): Plugin[];
@@ -0,0 +1,378 @@
1
+ import { version as viteVersionString } from "vite";
2
+ import tsconfigPaths from "vite-tsconfig-paths";
3
+ const translatedOptimizeDepsPlugins = new WeakMap();
4
+ export function compatTransform(plugins, options) {
5
+ const viteMajor = options?.viteVersion ?? parseInt(viteVersionString.split(".")[0], 10);
6
+ if (viteMajor >= 8) {
7
+ return plugins;
8
+ }
9
+ return [...plugins, tsconfigPaths(), createVite7CompatPlugin()];
10
+ }
11
+ function createVite7CompatPlugin() {
12
+ return {
13
+ name: "rwsdk:vite7-compat",
14
+ enforce: "post",
15
+ config(config) {
16
+ translateRootConfig(config);
17
+ },
18
+ configEnvironment(_env, config) {
19
+ translateEnvironmentConfig(config);
20
+ },
21
+ configResolved(config) {
22
+ // context(justinvdm, 2026-06-24): Some plugins add optimizeDeps
23
+ // Rolldown plugins in configResolved after configEnvironment has run.
24
+ // Vite 7 still needs those late plugins translated before the esbuild
25
+ // optimizer runs, especially the dev vendor-barrel redirect plugin.
26
+ translateResolvedConfig(config);
27
+ installRolldownProxies(config);
28
+ },
29
+ };
30
+ }
31
+ function translateRootConfig(config) {
32
+ if (config.optimizeDeps) {
33
+ translateOptimizeDeps(config.optimizeDeps);
34
+ }
35
+ if (config.build) {
36
+ translateBuildOptions(config.build);
37
+ }
38
+ }
39
+ function translateEnvironmentConfig(config) {
40
+ if (!config) {
41
+ return;
42
+ }
43
+ if (config.optimizeDeps) {
44
+ translateOptimizeDeps(config.optimizeDeps);
45
+ }
46
+ if (config.build) {
47
+ translateBuildOptions(config.build);
48
+ }
49
+ }
50
+ function translateResolvedConfig(config) {
51
+ translateRootConfig(config);
52
+ if (!config.environments) {
53
+ return;
54
+ }
55
+ for (const env of Object.values(config.environments)) {
56
+ translateEnvironmentConfig(env);
57
+ translateEnvironmentConfig(env.config);
58
+ }
59
+ }
60
+ function translateBuildOptions(build) {
61
+ if (build.rolldownOptions == null) {
62
+ return;
63
+ }
64
+ const rolldownOptions = build.rolldownOptions;
65
+ // context(justinvdm, 2026-06-22): Start from a fresh rollupOptions object.
66
+ // Vite 7's environment config merge can share nested output objects across
67
+ // environments, so inheriting an existing rollupOptions.output would cause
68
+ // SSR's entryFileNames to leak into the client build.
69
+ const rollupOptions = { ...build.rollupOptions };
70
+ for (const key of Object.keys(rolldownOptions)) {
71
+ if (key === "output") {
72
+ rollupOptions.output = mirrorOutputOptions(rolldownOptions.output);
73
+ }
74
+ else {
75
+ rollupOptions[key] = rolldownOptions[key];
76
+ }
77
+ }
78
+ build.rollupOptions = rollupOptions;
79
+ build.rolldownOptions = createRolldownOptionsProxy(rolldownOptions, rollupOptions);
80
+ translateLibOptions(build);
81
+ }
82
+ function translateLibOptions(build) {
83
+ if (build.lib == null || build.lib === false) {
84
+ return;
85
+ }
86
+ build.rollupOptions ??= {};
87
+ const hasInput = build.rollupOptions.input != null &&
88
+ (Array.isArray(build.rollupOptions.input)
89
+ ? build.rollupOptions.input.length > 0
90
+ : Object.keys(build.rollupOptions.input).length > 0);
91
+ if (hasInput) {
92
+ return;
93
+ }
94
+ build.rollupOptions.input = build.lib.entry;
95
+ if (build.lib.fileName) {
96
+ // context(justinvdm, 2026-06-22): Vite 7's environment config merge can
97
+ // share the same output object across environments. Create a new object
98
+ // so SSR's entryFileNames do not leak into the client build.
99
+ build.rollupOptions.output = {
100
+ ...(build.rollupOptions.output ?? {}),
101
+ };
102
+ const fileName = typeof build.lib.fileName === "function"
103
+ ? build.lib.fileName()
104
+ : build.lib.fileName;
105
+ build.rollupOptions.output.entryFileNames = fileName;
106
+ build.rollupOptions.output.chunkFileNames = fileName;
107
+ }
108
+ }
109
+ function mirrorOutputOptions(output) {
110
+ if (output == null) {
111
+ return output;
112
+ }
113
+ if (Array.isArray(output)) {
114
+ return output.map(mirrorSingleOutputOptions);
115
+ }
116
+ return mirrorSingleOutputOptions(output);
117
+ }
118
+ function mirrorSingleOutputOptions(output) {
119
+ if (output == null || typeof output !== "object") {
120
+ return output;
121
+ }
122
+ const result = {};
123
+ for (const [key, value] of Object.entries(output)) {
124
+ if (key === "codeSplitting") {
125
+ result.inlineDynamicImports = value === false;
126
+ }
127
+ else {
128
+ result[key] = value;
129
+ }
130
+ }
131
+ return result;
132
+ }
133
+ function translateOptimizeDeps(optimizeDeps) {
134
+ if (optimizeDeps.rolldownOptions == null) {
135
+ return;
136
+ }
137
+ optimizeDeps.esbuildOptions ??= {};
138
+ const rolldownOptions = optimizeDeps.rolldownOptions;
139
+ const esbuildOptions = optimizeDeps.esbuildOptions;
140
+ if (rolldownOptions.transform) {
141
+ if (rolldownOptions.transform.jsx === "react-jsx") {
142
+ esbuildOptions.jsx = "automatic";
143
+ esbuildOptions.jsxImportSource = "react";
144
+ }
145
+ else if (rolldownOptions.transform.jsx != null) {
146
+ esbuildOptions.jsx = rolldownOptions.transform.jsx;
147
+ }
148
+ if (rolldownOptions.transform.define) {
149
+ esbuildOptions.define ??= {};
150
+ Object.assign(esbuildOptions.define, rolldownOptions.transform.define);
151
+ }
152
+ }
153
+ if (Array.isArray(rolldownOptions.plugins)) {
154
+ esbuildOptions.plugins ??= [];
155
+ let translatedPlugins = translatedOptimizeDepsPlugins.get(optimizeDeps);
156
+ if (!translatedPlugins) {
157
+ translatedPlugins = new WeakSet();
158
+ translatedOptimizeDepsPlugins.set(optimizeDeps, translatedPlugins);
159
+ }
160
+ for (const plugin of rolldownOptions.plugins) {
161
+ if (translatedPlugins.has(plugin)) {
162
+ continue;
163
+ }
164
+ esbuildOptions.plugins.push(toEsbuildPlugin(plugin));
165
+ translatedPlugins.add(plugin);
166
+ }
167
+ }
168
+ }
169
+ function toEsbuildPlugin(rolldownPlugin) {
170
+ return {
171
+ name: rolldownPlugin.name,
172
+ setup(build) {
173
+ if (rolldownPlugin.resolveId) {
174
+ build.onResolve({ filter: /.*/ }, async (args) => {
175
+ const result = await rolldownPlugin.resolveId(args.path, args.importer, { kind: args.kind });
176
+ if (result == null) {
177
+ return undefined;
178
+ }
179
+ const id = typeof result === "string" ? result : result.id ?? args.path;
180
+ const hasNullPrefix = id.startsWith("\0");
181
+ const namespace = hasNullPrefix ? rolldownPlugin.name : undefined;
182
+ const path = hasNullPrefix ? id.slice(1) : id;
183
+ return {
184
+ path,
185
+ namespace,
186
+ external: typeof result === "string" ? undefined : result.external,
187
+ };
188
+ });
189
+ }
190
+ if (rolldownPlugin.load) {
191
+ const loadCallback = async (args) => {
192
+ const id = args.namespace === rolldownPlugin.name
193
+ ? `\0${args.path}`
194
+ : args.path;
195
+ const result = await rolldownPlugin.load(id);
196
+ if (result == null) {
197
+ return undefined;
198
+ }
199
+ if (typeof result === "string") {
200
+ return { contents: result };
201
+ }
202
+ return {
203
+ contents: result.code,
204
+ loader: moduleTypeToLoader(result.moduleType),
205
+ };
206
+ };
207
+ build.onLoad({ filter: /.*/, namespace: rolldownPlugin.name }, loadCallback);
208
+ build.onLoad({ filter: /.*/ }, loadCallback);
209
+ }
210
+ },
211
+ };
212
+ }
213
+ function moduleTypeToLoader(moduleType) {
214
+ if (moduleType == null) {
215
+ return undefined;
216
+ }
217
+ switch (moduleType) {
218
+ case "js":
219
+ case "ts":
220
+ case "jsx":
221
+ case "tsx":
222
+ return moduleType;
223
+ default:
224
+ return undefined;
225
+ }
226
+ }
227
+ function installRolldownProxies(config) {
228
+ if (config.build?.rolldownOptions != null) {
229
+ config.build.rolldownOptions = createRolldownOptionsProxy(config.build.rolldownOptions, config.build.rollupOptions);
230
+ }
231
+ if (config.environments) {
232
+ for (const env of Object.values(config.environments)) {
233
+ const envBuild = env.build ?? env.config?.build;
234
+ if (envBuild?.rolldownOptions != null) {
235
+ envBuild.rolldownOptions = createRolldownOptionsProxy(envBuild.rolldownOptions, envBuild.rollupOptions);
236
+ }
237
+ }
238
+ }
239
+ // context(justinvdm, 2026-06-22): Vite 7 creates separate environment
240
+ // config objects at build time, so the proxies installed above on the
241
+ // resolved config do not reach the builder.environments[*].config objects
242
+ // that buildApp uses. Wrap builder.buildApp to install proxies on the live
243
+ // builder environments right before our build orchestration runs.
244
+ if (config.builder?.buildApp) {
245
+ const originalBuildApp = config.builder.buildApp;
246
+ config.builder.buildApp = async (builder) => {
247
+ installRolldownProxiesOnBuilder(builder);
248
+ return originalBuildApp(builder);
249
+ };
250
+ }
251
+ }
252
+ function installRolldownProxiesOnBuilder(builder) {
253
+ if (builder.config?.build?.rolldownOptions == null) {
254
+ builder.config.build.rolldownOptions = createRolldownOptionsProxy({}, builder.config.build.rollupOptions);
255
+ }
256
+ if (builder.environments) {
257
+ for (const env of Object.values(builder.environments)) {
258
+ const envBuild = env.config?.build;
259
+ if (envBuild?.rolldownOptions == null) {
260
+ envBuild.rolldownOptions = createRolldownOptionsProxy({}, envBuild.rollupOptions);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ function createRolldownOptionsProxy(rolldownOptions, rollupOptions) {
266
+ rollupOptions ??= {};
267
+ return new Proxy(rolldownOptions, {
268
+ get(_target, prop, _receiver) {
269
+ if (prop === "output") {
270
+ if (rollupOptions.output == null) {
271
+ rollupOptions.output = {};
272
+ }
273
+ return createOutputProxy(rolldownOptions.output, rollupOptions.output);
274
+ }
275
+ const value = Reflect.get(rollupOptions, prop, rollupOptions);
276
+ if (typeof value === "function") {
277
+ return value.bind(rollupOptions);
278
+ }
279
+ return value;
280
+ },
281
+ set(_target, prop, value, _receiver) {
282
+ if (prop === "output") {
283
+ rollupOptions.output = mirrorOutputOptions(value);
284
+ return true;
285
+ }
286
+ return Reflect.set(rollupOptions, prop, value, rollupOptions);
287
+ },
288
+ has(_target, prop) {
289
+ return Reflect.has(rollupOptions, prop);
290
+ },
291
+ ownKeys(_target) {
292
+ return Reflect.ownKeys(rollupOptions);
293
+ },
294
+ getOwnPropertyDescriptor(_target, prop) {
295
+ return Reflect.getOwnPropertyDescriptor(rollupOptions, prop);
296
+ },
297
+ });
298
+ }
299
+ function createOutputProxy(rolldownOutput, rollupOutput) {
300
+ if (Array.isArray(rolldownOutput)) {
301
+ rollupOutput = ensureArray(rollupOutput);
302
+ return new Proxy(rolldownOutput, {
303
+ get(_target, prop) {
304
+ if (prop === "length") {
305
+ return rollupOutput.length;
306
+ }
307
+ const index = Number(prop);
308
+ if (!Number.isNaN(index)) {
309
+ return createSingleOutputProxy(rolldownOutput[index], rollupOutput[index]);
310
+ }
311
+ const value = Reflect.get(rollupOutput, prop, rollupOutput);
312
+ if (typeof value === "function") {
313
+ return value.bind(rollupOutput);
314
+ }
315
+ return value;
316
+ },
317
+ set(_target, prop, value) {
318
+ if (prop === "length") {
319
+ rollupOutput.length = value;
320
+ return true;
321
+ }
322
+ const index = Number(prop);
323
+ if (!Number.isNaN(index)) {
324
+ rollupOutput[index] = mirrorSingleOutputOptions(value);
325
+ return true;
326
+ }
327
+ return Reflect.set(rollupOutput, prop, value, rollupOutput);
328
+ },
329
+ has(_target, prop) {
330
+ return Reflect.has(rollupOutput, prop);
331
+ },
332
+ ownKeys(_target) {
333
+ return Reflect.ownKeys(rollupOutput);
334
+ },
335
+ getOwnPropertyDescriptor(_target, prop) {
336
+ return Reflect.getOwnPropertyDescriptor(rollupOutput, prop);
337
+ },
338
+ });
339
+ }
340
+ return createSingleOutputProxy(rolldownOutput ?? {}, rollupOutput ?? {});
341
+ }
342
+ function ensureArray(value) {
343
+ if (value == null) {
344
+ return [];
345
+ }
346
+ return Array.isArray(value) ? value : [value];
347
+ }
348
+ function createSingleOutputProxy(rolldownSingle, rollupSingle) {
349
+ rollupSingle ??= {};
350
+ return new Proxy(rolldownSingle, {
351
+ get(_target, prop) {
352
+ if (prop === "codeSplitting") {
353
+ return rollupSingle.inlineDynamicImports === true ? false : true;
354
+ }
355
+ const value = Reflect.get(rollupSingle, prop, rollupSingle);
356
+ if (typeof value === "function") {
357
+ return value.bind(rollupSingle);
358
+ }
359
+ return value;
360
+ },
361
+ set(_target, prop, value) {
362
+ if (prop === "codeSplitting") {
363
+ rollupSingle.inlineDynamicImports = value === false;
364
+ return true;
365
+ }
366
+ return Reflect.set(rollupSingle, prop, value, rollupSingle);
367
+ },
368
+ has(_target, prop) {
369
+ return Reflect.has(rollupSingle, prop);
370
+ },
371
+ ownKeys(_target) {
372
+ return Reflect.ownKeys(rollupSingle);
373
+ },
374
+ getOwnPropertyDescriptor(_target, prop) {
375
+ return Reflect.getOwnPropertyDescriptor(rollupSingle, prop);
376
+ },
377
+ });
378
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { compatTransform } from "./viteCompat.mjs";
3
+ function getHookHandler(hook) {
4
+ if (typeof hook === "function") {
5
+ return hook;
6
+ }
7
+ if (hook && typeof hook === "object" && "handler" in hook) {
8
+ const handler = hook.handler;
9
+ if (typeof handler === "function") {
10
+ return handler;
11
+ }
12
+ }
13
+ throw new Error("Expected plugin hook to be registered");
14
+ }
15
+ function getCompatPlugin() {
16
+ const plugins = compatTransform([], { viteVersion: 7 });
17
+ const compatPlugin = plugins.find((plugin) => plugin?.name === "rwsdk:vite7-compat");
18
+ if (!compatPlugin) {
19
+ throw new Error("Expected Vite 7 compat plugin to be present");
20
+ }
21
+ return compatPlugin;
22
+ }
23
+ describe("viteCompat", () => {
24
+ it("translates optimizeDeps plugins added after configEnvironment", async () => {
25
+ const firstPlugin = {
26
+ name: "first",
27
+ resolveId(id) {
28
+ if (id === "first") {
29
+ return "first-result";
30
+ }
31
+ },
32
+ };
33
+ const latePlugin = {
34
+ name: "late",
35
+ resolveId(id) {
36
+ if (id === "late") {
37
+ return "\0late-result";
38
+ }
39
+ },
40
+ };
41
+ const environmentConfig = {
42
+ optimizeDeps: {
43
+ rolldownOptions: {
44
+ plugins: [firstPlugin],
45
+ },
46
+ },
47
+ };
48
+ const compatPlugin = getCompatPlugin();
49
+ const configEnvironment = getHookHandler(compatPlugin.configEnvironment);
50
+ const configResolved = getHookHandler(compatPlugin.configResolved);
51
+ await configEnvironment("worker", environmentConfig);
52
+ expect(environmentConfig.optimizeDeps.esbuildOptions?.plugins).toHaveLength(1);
53
+ environmentConfig.optimizeDeps.rolldownOptions.plugins.push(latePlugin);
54
+ await configResolved({ environments: { worker: environmentConfig } });
55
+ const esbuildPlugins = environmentConfig.optimizeDeps.esbuildOptions?.plugins;
56
+ expect(esbuildPlugins).toHaveLength(2);
57
+ if (!esbuildPlugins?.[1]) {
58
+ throw new Error("Expected late esbuild plugin to be translated");
59
+ }
60
+ const resolveCallbacks = [];
61
+ await esbuildPlugins[1].setup({
62
+ onResolve(_options, callback) {
63
+ resolveCallbacks.push(callback);
64
+ },
65
+ });
66
+ await expect(resolveCallbacks[0]({ path: "late" })).resolves.toEqual({
67
+ path: "late-result",
68
+ namespace: "late",
69
+ external: undefined,
70
+ });
71
+ });
72
+ });