11import assert from "node:assert" ;
22import * as fs from "node:fs" ;
3+ import { builtinModules } from "node:module" ;
34import * as path from "node:path" ;
45import { createMiddleware } from "@hattip/adapter-node" ;
56import MagicString from "magic-string" ;
@@ -16,11 +17,10 @@ import {
1617 getPreviewMiniflareOptions ,
1718} from "./miniflare-options" ;
1819import {
19- getNodeCompatAliases ,
20+ getNodeCompatEntries ,
2021 getNodeCompatExternals ,
2122 injectGlobalCode ,
2223 isNodeCompat ,
23- maybeStripNodeJsVirtualPrefix ,
2424 resolveNodeJSImport ,
2525} from "./node-js-compat" ;
2626import { resolvePluginConfig } from "./plugin-config" ;
@@ -407,67 +407,68 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
407407 // Skip this whole plugin if we are in preview mode
408408 return ! env . isPreview ;
409409 } ,
410- config ( ) {
411- // Configure Vite with the Node.js polyfill aliases
412- // We have to do this across the whole Vite config because it is not possible to do it per Environment.
413- // These aliases are to virtual modules that then get Environment specific handling in the resolveId hook.
414- return {
415- resolve : {
416- alias : getNodeCompatAliases ( ) ,
417- } ,
418- } ;
419- } ,
420- configEnvironment ( environmentName ) {
421- // Ignore Node.js external modules when building environments that use Node.js compat.
422- const workerConfig = getWorkerConfig ( environmentName ) ;
423- if ( isNodeCompat ( workerConfig ) ) {
410+ configEnvironment ( name ) {
411+ // Only configure this environment if it is a Worker using Node.js compatibility.
412+ if ( isNodeCompat ( getWorkerConfig ( name ) ) ) {
424413 return {
425- build : {
426- rollupOptions : {
427- external : getNodeCompatExternals ( ) ,
428- } ,
414+ resolve : {
415+ builtins : getNodeCompatExternals ( ) ,
416+ } ,
417+ optimizeDeps : {
418+ // This is a list of dependency entry-points that should be pre-bundled.
419+ // In this case we provide a list of all the possible polyfills so that they are pre-bundled,
420+ // ready ahead the first request to the dev server.
421+ // Without this the dependency optimizer will try to bundle them on-the-fly in the middle of the first request,
422+ // which can potentially cause problems if it leads to previous pre-bundling to become stale and needing to be reloaded.
423+ include : [ ...getNodeCompatEntries ( ) ] ,
424+ // This is a list of module specifiers that the dependency optimizer should not follow when doing import analysis.
425+ // In this case we provide a list of all the Node.js modules, both those built-in to workerd and those that will be polyfilled.
426+ // Obviously we don't want/need the optimizer to try to process modules that are built-in;
427+ // But also we want to avoid following the ones that are polyfilled since the dependency-optimizer import analyzer does not
428+ // resolve these imports using our `resolveId()` hook causing the optimization step to fail.
429+ exclude : [
430+ ...builtinModules ,
431+ ...builtinModules . map ( ( m ) => `node:${ m } ` ) ,
432+ ] ,
429433 } ,
430434 } ;
431435 }
432436 } ,
437+ applyToEnvironment ( environment ) {
438+ // Only run this plugin's runtime hooks if it is a Worker using Node.js compatibility.
439+ return isNodeCompat ( getWorkerConfig ( environment . name ) ) ;
440+ } ,
441+ // We need the resolver from this plugin to run before built-in ones, otherwise Vite's built-in
442+ // resolver will try to externalize the Node.js module imports (e.g. `perf_hooks` and `node:tty`)
443+ // rather than allowing the resolve hook here to alias then to polyfills.
444+ enforce : "pre" ,
433445 async resolveId ( source , importer , options ) {
434- // Handle the virtual modules that come from Node.js compat aliases.
435- const strippedSource = maybeStripNodeJsVirtualPrefix ( source ) ;
436- if ( ! strippedSource ) {
437- return ;
446+ // See if we can map the `source` to a Node.js compat alias.
447+ const result = resolveNodeJSImport ( source ) ;
448+ if ( ! result ) {
449+ // The source is not a Node.js compat alias so just pass it through
450+ return this . resolve ( source , importer , options ) ;
438451 }
439452
440- const workerConfig = getWorkerConfig ( this . environment . name ) ;
441- if ( ! isNodeCompat ( workerConfig ) ) {
442- // We are not in Node.js compat mode, so we must not try to apply any unenv aliases.
443- // Just resolve the module id as normal.
444- return this . resolve ( strippedSource , importer , options ) ;
445- }
446-
447- // Resolve this id following any unenv aliases.
448- const { unresolved, resolved } = resolveNodeJSImport ( strippedSource ) ;
449-
450- if ( this . environment . mode === "dev" && this . environment . depsOptimizer ) {
451- // Make sure the dependency optimizer is aware of this aliased import
452- const optimized =
453- this . environment . depsOptimizer . registerMissingImport (
454- unresolved ,
455- resolved
456- ) . id ;
457- // We must pass the id from the depsOptimizer through the rest of the
458- // resolving pipeline to ensure that the optimized version gets used.
459- return this . resolve ( optimized , importer , options ) ;
453+ if ( this . environment . mode === "dev" ) {
454+ assert (
455+ this . environment . depsOptimizer ,
456+ "depsOptimizer is required in dev mode"
457+ ) ;
458+ // We are in dev mode (rather than build).
459+ // Node.js compat polyfill modules have already been pre-bundled,
460+ // so we can use the unresolved path to the polyfill
461+ // and let the dependency optimizer resolve the path to the pre-bundled version.
462+ return this . resolve ( result . unresolved , importer , options ) ;
460463 }
461464
462- return this . resolve ( resolved , importer , options ) ;
465+ // We are in build mode so return the absolute path to the polyfill.
466+ return this . resolve ( result . resolved , importer , options ) ;
463467 } ,
464468 async transform ( code , id ) {
465469 // Inject the Node.js compat globals into the entry module for Node.js compat environments.
466470 const workerConfig = getWorkerConfig ( this . environment . name ) ;
467- if ( ! isNodeCompat ( workerConfig ) ) {
468- return ;
469- }
470-
471+ assert ( workerConfig , "Expected a worker config" ) ;
471472 const resolvedId = await this . resolve ( workerConfig . main ) ;
472473 if ( id === resolvedId ?. id ) {
473474 return injectGlobalCode ( id , code ) ;
0 commit comments