From 0f7ae2facf433139ddf370ad34afff82ef932b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 16 Aug 2023 23:48:43 +0900 Subject: [PATCH 01/20] fix(glob): trigger HMR for glob in a package (#14117) --- .../vite/src/node/plugins/importMetaGlob.ts | 6 +++-- .../glob-import/__tests__/glob-import.spec.ts | 23 +++++++++++++++++++ .../glob-import/import-meta-glob-pkg/index.js | 4 ++++ .../import-meta-glob-pkg/package.json | 5 ++++ playground/glob-import/index.html | 6 +++++ playground/glob-import/package.json | 3 +++ playground/glob-import/pkg-pages/foo.js | 1 + pnpm-lock.yaml | 13 ++++++++++- 8 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 playground/glob-import/import-meta-glob-pkg/index.js create mode 100644 playground/glob-import/import-meta-glob-pkg/package.json create mode 100644 playground/glob-import/pkg-pages/foo.js diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 0daf6b04358b30..60172e0de3a143 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -58,8 +58,10 @@ export function getAffectedGlobModules( (!affirmed.length || affirmed.some((glob) => isMatch(file, glob))) && (!negated.length || negated.every((glob) => isMatch(file, glob))), ) - ) - modules.push(...(server.moduleGraph.getModulesByFile(id) || [])) + ) { + const mod = server.moduleGraph.getModuleById(id) + if (mod) modules.push(mod) + } } modules.forEach((i) => { if (i?.file) server.moduleGraph.onFileChange(i.file) diff --git a/playground/glob-import/__tests__/glob-import.spec.ts b/playground/glob-import/__tests__/glob-import.spec.ts index 931edfd99138de..8e73892219e192 100644 --- a/playground/glob-import/__tests__/glob-import.spec.ts +++ b/playground/glob-import/__tests__/glob-import.spec.ts @@ -11,6 +11,7 @@ import { page, removeFile, untilBrowserLogAfter, + untilUpdated, viteTestUrl, withRetry, } from '~utils' @@ -131,6 +132,12 @@ test('unassigned import processes', async () => { ) }) +test('import glob in package', async () => { + expect(await page.textContent('.in-package')).toBe( + JSON.stringify(['/pkg-pages/foo.js']), + ) +}) + if (!isBuild) { test('hmr for adding/removing files', async () => { const resultElement = page.locator('.result') @@ -190,6 +197,22 @@ if (!isBuild) { response = await request.catch(() => ({ status: () => -1 })) expect(response.status()).toBe(-1) }) + + test('hmr for adding/removing files in package', async () => { + const resultElement = page.locator('.in-package') + + addFile('pkg-pages/bar.js', '// empty') + await untilUpdated( + () => resultElement.textContent(), + JSON.stringify(['/pkg-pages/foo.js', '/pkg-pages/bar.js'].sort()), + ) + + removeFile('pkg-pages/bar.js') + await untilUpdated( + () => resultElement.textContent(), + JSON.stringify(['/pkg-pages/foo.js']), + ) + }) } test('tree-shake eager css', async () => { diff --git a/playground/glob-import/import-meta-glob-pkg/index.js b/playground/glob-import/import-meta-glob-pkg/index.js new file mode 100644 index 00000000000000..44705cf18f9f22 --- /dev/null +++ b/playground/glob-import/import-meta-glob-pkg/index.js @@ -0,0 +1,4 @@ +export const g = import.meta.glob('/pkg-pages/*.js') +document.querySelector('.in-package').textContent = JSON.stringify( + Object.keys(g).sort(), +) diff --git a/playground/glob-import/import-meta-glob-pkg/package.json b/playground/glob-import/import-meta-glob-pkg/package.json new file mode 100644 index 00000000000000..7138de851543cf --- /dev/null +++ b/playground/glob-import/import-meta-glob-pkg/package.json @@ -0,0 +1,5 @@ +{ + "name": "@vitejs/test-import-meta-glob-pkg", + "type": "module", + "main": "./index.js" +} diff --git a/playground/glob-import/index.html b/playground/glob-import/index.html index b726965ff62067..3b72430668e14d 100644 --- a/playground/glob-import/index.html +++ b/playground/glob-import/index.html @@ -23,6 +23,8 @@

Escape alias glob


 

Sub imports


+

In package

+

 
 
 
 
+
+
 
diff --git a/playground/glob-import/package.json b/playground/glob-import/package.json
index 7d9104ab431c28..d71d01109270f1 100644
--- a/playground/glob-import/package.json
+++ b/playground/glob-import/package.json
@@ -11,5 +11,8 @@
     "build": "vite build",
     "debug": "node --inspect-brk ../../packages/vite/bin/vite",
     "preview": "vite preview"
+  },
+  "dependencies": {
+    "@vitejs/test-import-meta-glob-pkg": "file:./import-meta-glob-pkg"
   }
 }
diff --git a/playground/glob-import/pkg-pages/foo.js b/playground/glob-import/pkg-pages/foo.js
new file mode 100644
index 00000000000000..8b1a393741c96c
--- /dev/null
+++ b/playground/glob-import/pkg-pages/foo.js
@@ -0,0 +1 @@
+// empty
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b2abd4ecceef5b..e3ad26d4ccdb3e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -670,7 +670,13 @@ importers:
 
   playground/fs-serve: {}
 
-  playground/glob-import: {}
+  playground/glob-import:
+    dependencies:
+      '@vitejs/test-import-meta-glob-pkg':
+        specifier: file:./import-meta-glob-pkg
+        version: file:playground/glob-import/import-meta-glob-pkg
+
+  playground/glob-import/import-meta-glob-pkg: {}
 
   playground/hmr: {}
 
@@ -10838,6 +10844,11 @@ packages:
       vue: 3.3.4
     dev: false
 
+  file:playground/glob-import/import-meta-glob-pkg:
+    resolution: {directory: playground/glob-import/import-meta-glob-pkg, type: directory}
+    name: '@vitejs/test-import-meta-glob-pkg'
+    dev: false
+
   file:playground/import-assertion/import-assertion-dep:
     resolution: {directory: playground/import-assertion/import-assertion-dep, type: directory}
     name: '@vitejs/test-import-assertion-dep'

From 917ff3ff3478d5c44cb089860a57af80ce024595 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BF=A0=20/=20green?= 
Date: Thu, 17 Aug 2023 03:04:06 +0900
Subject: [PATCH 02/20] fix: ws never connects after restarting server if
 server.hmr.server is set (#14127)

---
 packages/vite/src/node/server/ws.ts | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts
index 4a298cac1eebd2..7df59370f9e1d8 100644
--- a/packages/vite/src/node/server/ws.ts
+++ b/packages/vite/src/node/server/ws.ts
@@ -1,9 +1,10 @@
 import path from 'node:path'
-import type { Server } from 'node:http'
+import type { IncomingMessage, Server } from 'node:http'
 import { STATUS_CODES, createServer as createHttpServer } from 'node:http'
 import type { ServerOptions as HttpsServerOptions } from 'node:https'
 import { createServer as createHttpsServer } from 'node:https'
 import type { Socket } from 'node:net'
+import type { Duplex } from 'node:stream'
 import colors from 'picocolors'
 import type { WebSocket as WebSocketRaw } from 'ws'
 import { WebSocketServer as WebSocketServerRaw_ } from 'ws'
@@ -104,6 +105,11 @@ export function createWebSocketServer(
   // TODO: the main server port may not have been chosen yet as it may use the next available
   const portsAreCompatible = !hmrPort || hmrPort === config.server.port
   const wsServer = hmrServer || (portsAreCompatible && server)
+  let hmrServerWsListener: (
+    req: InstanceType,
+    socket: Duplex,
+    head: Buffer,
+  ) => void
   const customListeners = new Map>>()
   const clientsMap = new WeakMap()
   const port = hmrPort || 24678
@@ -116,7 +122,7 @@ export function createWebSocketServer(
       hmrBase = path.posix.join(hmrBase, hmrPath)
     }
     wss = new WebSocketServerRaw({ noServer: true })
-    wsServer.on('upgrade', (req, socket, head) => {
+    hmrServerWsListener = (req, socket, head) => {
       if (
         req.headers['sec-websocket-protocol'] === HMR_HEADER &&
         req.url === hmrBase
@@ -125,7 +131,8 @@ export function createWebSocketServer(
           wss.emit('connection', ws, req)
         })
       }
-    })
+    }
+    wsServer.on('upgrade', hmrServerWsListener)
   } else {
     // http server request handler keeps the same with
     // https://github.com/websockets/ws/blob/45e17acea791d865df6b255a55182e9c42e5877a/lib/websocket-server.js#L88-L96
@@ -273,6 +280,11 @@ export function createWebSocketServer(
     },
 
     close() {
+      // should remove listener if hmr.server is set
+      // otherwise the old listener swallows all WebSocket connections
+      if (hmrServerWsListener && wsServer) {
+        wsServer.off('upgrade', hmrServerWsListener)
+      }
       return new Promise((resolve, reject) => {
         wss.clients.forEach((client) => {
           client.terminate()

From 65df270ae08dcab7aeada4b5d87372aaab1bd13d Mon Sep 17 00:00:00 2001
From: btea <2356281422@qq.com>
Date: Sat, 19 Aug 2023 00:10:44 +0800
Subject: [PATCH 03/20] fix: initWasm options should be optional (#14152)

---
 packages/vite/client.d.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/vite/client.d.ts b/packages/vite/client.d.ts
index b73389ec373f1f..0f2c3db286d82f 100644
--- a/packages/vite/client.d.ts
+++ b/packages/vite/client.d.ts
@@ -221,7 +221,7 @@ declare module '*.txt' {
 // wasm?init
 declare module '*.wasm?init' {
   const initWasm: (
-    options: WebAssembly.Imports,
+    options?: WebAssembly.Imports,
   ) => Promise
   export default initWasm
 }

From eda46c35b311df424c1aa28627de621c35023b3c Mon Sep 17 00:00:00 2001
From: btea <2356281422@qq.com>
Date: Tue, 22 Aug 2023 16:04:28 +0800
Subject: [PATCH 04/20] fix: if host is specified check whether it is valid
 (#14013)

---
 packages/vite/src/node/cli.ts | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts
index df858f403f80c5..f6d3c9b6d6fd15 100644
--- a/packages/vite/src/node/cli.ts
+++ b/packages/vite/src/node/cli.ts
@@ -102,6 +102,16 @@ function cleanOptions(
   return ret
 }
 
+/**
+ * host may be a number (like 0), should convert to string
+ */
+const convertHost = (v: any) => {
+  if (typeof v === 'number') {
+    return String(v)
+  }
+  return v
+}
+
 cli
   .option('-c, --config ', `[string] use specified config file`)
   .option('--base ', `[string] public base path (default: /)`)
@@ -116,7 +126,7 @@ cli
   .command('[root]', 'start dev server') // default command
   .alias('serve') // the command is called 'serve' in Vite's API
   .alias('dev') // alias to align with the script name
-  .option('--host [host]', `[string] specify hostname`)
+  .option('--host [host]', `[string] specify hostname`, { type: [convertHost] })
   .option('--port ', `[number] specify port`)
   .option('--https', `[boolean] use TLS + HTTP/2`)
   .option('--open [path]', `[boolean | string] open browser on startup`)
@@ -306,7 +316,7 @@ cli
 // preview
 cli
   .command('preview [root]', 'locally preview production build')
-  .option('--host [host]', `[string] specify hostname`)
+  .option('--host [host]', `[string] specify hostname`, { type: [convertHost] })
   .option('--port ', `[number] specify port`)
   .option('--strictPort', `[boolean] exit if specified port is already in use`)
   .option('--https', `[boolean] use TLS + HTTP/2`)

From 76dd2f9b3ef91ece0c895ccfc2bedb3ea067421c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=AE=8B=E9=93=84=E8=BF=90=20=28Alan=20Song=29?=
 
Date: Thu, 24 Aug 2023 20:50:38 -0700
Subject: [PATCH 05/20] fix: use string manipulation instead of regex to inject
 esbuild helpers (#14094)

---
 packages/vite/src/node/plugins/esbuild.ts | 39 +++++++++++++----------
 playground/lib/__tests__/lib.spec.ts      |  4 ++-
 playground/lib/src/main.js                |  3 ++
 playground/lib/vite.config.js             |  1 +
 4 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts
index d2bf848437a915..591e2883453fe3 100644
--- a/packages/vite/src/node/plugins/esbuild.ts
+++ b/packages/vite/src/node/plugins/esbuild.ts
@@ -28,10 +28,9 @@ import { searchForWorkspaceRoot } from '../server/searchRoot'
 
 const debug = createDebugger('vite:esbuild')
 
-const INJECT_HELPERS_IIFE_RE =
-  /^(.*?)((?:const|var)\s+\S+\s*=\s*function\s*\([^)]*\)\s*\{\s*"use strict";)/s
-const INJECT_HELPERS_UMD_RE =
-  /^(.*?)(\(function\([^)]*\)\s*\{.+?amd.+?function\([^)]*\)\s*\{\s*"use strict";)/s
+// IIFE content looks like `var MyLib = function() {`. Spaces are removed when minified
+const IIFE_BEGIN_RE =
+  /(const|var)\s+\S+\s*=\s*function\(\)\s*\{.*"use strict";/s
 
 const validExtensionRE = /\.\w+$/
 const jsxExtensionsRE = /\.(?:j|t)sx\b/
@@ -333,22 +332,30 @@ export const buildEsbuildPlugin = (config: ResolvedConfig): Plugin => {
       if (config.build.lib) {
         // #7188, esbuild adds helpers out of the UMD and IIFE wrappers, and the
         // names are minified potentially causing collision with other globals.
-        // We use a regex to inject the helpers inside the wrappers.
+        // We inject the helpers inside the wrappers.
+        // e.g. turn:
+        //     (function(){ /*actual content/* })()
+        // into:
+        //    (function(){  /*actual content/* })()
+        // Not using regex because it's too hard to rule out performance issues like #8738 #8099 #10900 #14065
+        // Instead, using plain string index manipulation (indexOf, slice) which is simple and performant
         // We don't need to create a MagicString here because both the helpers and
         // the headers don't modify the sourcemap
-        const injectHelpers =
-          opts.format === 'umd'
-            ? INJECT_HELPERS_UMD_RE
-            : opts.format === 'iife'
-            ? INJECT_HELPERS_IIFE_RE
-            : undefined
-        if (injectHelpers) {
-          res.code = res.code.replace(
-            injectHelpers,
-            (_, helpers, header) => header + helpers,
-          )
+        const esbuildCode = res.code
+        const contentIndex =
+          opts.format === 'iife'
+            ? esbuildCode.match(IIFE_BEGIN_RE)?.index || 0
+            : opts.format === 'umd'
+            ? esbuildCode.indexOf(`(function(`) // same for minified or not
+            : 0
+        if (contentIndex > 0) {
+          const esbuildHelpers = esbuildCode.slice(0, contentIndex)
+          res.code = esbuildCode
+            .slice(contentIndex)
+            .replace(`"use strict";`, `"use strict";` + esbuildHelpers)
         }
       }
+
       return res
     },
   }
diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts
index b203535e0154da..8232a2fd100649 100644
--- a/playground/lib/__tests__/lib.spec.ts
+++ b/playground/lib/__tests__/lib.spec.ts
@@ -33,7 +33,9 @@ describe.runIf(isBuild)('build', () => {
       'dist/nominify/my-lib-custom-filename.iife.js',
     )
     // esbuild helpers are injected inside of the IIFE wrapper
-    expect(code).toMatch(/^var MyLib=function\(\)\{"use strict";/)
+    // esbuild has a bug that injects some statements before `"use strict"`: https://github.com/evanw/esbuild/issues/3322
+    // remove the `.*?` part once it's fixed
+    expect(code).toMatch(/^var MyLib=function\(\)\{.*?"use strict";/)
     expect(noMinifyCode).toMatch(
       /^var MyLib\s*=\s*function\(\)\s*\{.*?"use strict";/s,
     )
diff --git a/playground/lib/src/main.js b/playground/lib/src/main.js
index 59c8e897cb0789..8be8ec37e635ee 100644
--- a/playground/lib/src/main.js
+++ b/playground/lib/src/main.js
@@ -10,3 +10,6 @@ export default function myLib(sel) {
   // make sure umd helper has been moved to the right position
   console.log(`amd function(){ "use strict"; }`)
 }
+
+// For triggering unhandled global esbuild helpers in previous regex-based implementation for injection
+;(function () {})()?.foo
diff --git a/playground/lib/vite.config.js b/playground/lib/vite.config.js
index 6b4395624dc27a..84612ba1f65306 100644
--- a/playground/lib/vite.config.js
+++ b/playground/lib/vite.config.js
@@ -7,6 +7,7 @@ export default defineConfig({
     supported: {
       // Force esbuild inject helpers to test regex
       'object-rest-spread': false,
+      'optional-chain': false,
     },
   },
   build: {

From 8c35aad95c1203ef4afa4fe896e4d58334180b07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BF=A0=20/=20green?= 
Date: Tue, 29 Aug 2023 22:22:41 +0900
Subject: [PATCH 06/20] fix: handle sourcemap correctly when multiple line
 import exists (#14232)

---
 .../vite/src/node/plugins/importAnalysis.ts   | 23 +++++++++++++---
 .../__tests__/js-sourcemap.spec.ts            | 26 +++++++++++++++++++
 playground/js-sourcemap/importee-pkg/index.js |  2 ++
 .../js-sourcemap/importee-pkg/package.json    |  6 +++++
 playground/js-sourcemap/index.html            |  1 +
 playground/js-sourcemap/package.json          |  3 +++
 .../js-sourcemap/with-multiline-import.ts     |  6 +++++
 pnpm-lock.yaml                                | 13 +++++++++-
 8 files changed, 75 insertions(+), 5 deletions(-)
 create mode 100644 playground/js-sourcemap/importee-pkg/index.js
 create mode 100644 playground/js-sourcemap/importee-pkg/package.json
 create mode 100644 playground/js-sourcemap/with-multiline-import.ts

diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts
index c33cd519fb4da7..620fa38175d19d 100644
--- a/packages/vite/src/node/plugins/importAnalysis.ts
+++ b/packages/vite/src/node/plugins/importAnalysis.ts
@@ -850,16 +850,17 @@ export function interopNamedImports(
     se: expEnd,
     d: dynamicIndex,
   } = importSpecifier
+  const exp = source.slice(expStart, expEnd)
   if (dynamicIndex > -1) {
     // rewrite `import('package')` to expose the default directly
     str.overwrite(
       expStart,
       expEnd,
-      `import('${rewrittenUrl}').then(m => m.default && m.default.__esModule ? m.default : ({ ...m.default, default: m.default }))`,
+      `import('${rewrittenUrl}').then(m => m.default && m.default.__esModule ? m.default : ({ ...m.default, default: m.default }))` +
+        getLineBreaks(exp),
       { contentOnly: true },
     )
   } else {
-    const exp = source.slice(expStart, expEnd)
     const rawUrl = source.slice(start, end)
     const rewritten = transformCjsImport(
       exp,
@@ -870,14 +871,28 @@ export function interopNamedImports(
       config,
     )
     if (rewritten) {
-      str.overwrite(expStart, expEnd, rewritten, { contentOnly: true })
+      str.overwrite(expStart, expEnd, rewritten + getLineBreaks(exp), {
+        contentOnly: true,
+      })
     } else {
       // #1439 export * from '...'
-      str.overwrite(start, end, rewrittenUrl, { contentOnly: true })
+      str.overwrite(
+        start,
+        end,
+        rewrittenUrl + getLineBreaks(source.slice(start, end)),
+        {
+          contentOnly: true,
+        },
+      )
     }
   }
 }
 
+// get line breaks to preserve line count for not breaking source maps
+function getLineBreaks(str: string) {
+  return str.includes('\n') ? '\n'.repeat(str.split('\n').length - 1) : ''
+}
+
 type ImportNameSpecifier = { importedName: string; localName: string }
 
 /**
diff --git a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts
index ce67f19e904521..2b8044f40fa32e 100644
--- a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts
+++ b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts
@@ -36,6 +36,32 @@ if (!isBuild) {
     `)
   })
 
+  test('multiline import', async () => {
+    const res = await page.request.get(
+      new URL('./with-multiline-import.ts', page.url()).href,
+    )
+    const multi = await res.text()
+    const map = extractSourcemap(multi)
+    expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
+      {
+        "mappings": "AACA;AAAA,EACE;AAAA,OACK;AAEP,QAAQ,IAAI,yBAAyB,GAAG;",
+        "sources": [
+          "with-multiline-import.ts",
+        ],
+        "sourcesContent": [
+          "// prettier-ignore
+      import {
+        foo
+      } from '@vitejs/test-importee-pkg'
+
+      console.log('with-multiline-import', foo)
+      ",
+        ],
+        "version": 3,
+      }
+    `)
+  })
+
   test('should not output missing source file warning', () => {
     serverLogs.forEach((log) => {
       expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/)
diff --git a/playground/js-sourcemap/importee-pkg/index.js b/playground/js-sourcemap/importee-pkg/index.js
new file mode 100644
index 00000000000000..a96b15202fba44
--- /dev/null
+++ b/playground/js-sourcemap/importee-pkg/index.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import/no-commonjs
+exports.foo = 'foo'
diff --git a/playground/js-sourcemap/importee-pkg/package.json b/playground/js-sourcemap/importee-pkg/package.json
new file mode 100644
index 00000000000000..2bc76d5bb50b39
--- /dev/null
+++ b/playground/js-sourcemap/importee-pkg/package.json
@@ -0,0 +1,6 @@
+{
+  "name": "@vitejs/test-importee-pkg",
+  "private": true,
+  "version": "0.0.0",
+  "main": "./index.js"
+}
diff --git a/playground/js-sourcemap/index.html b/playground/js-sourcemap/index.html
index bda409a5cc9693..f669bf4fc102aa 100644
--- a/playground/js-sourcemap/index.html
+++ b/playground/js-sourcemap/index.html
@@ -6,3 +6,4 @@ 

JS Sourcemap

+ diff --git a/playground/js-sourcemap/package.json b/playground/js-sourcemap/package.json index 6381b13c9b09e9..b002697756a24c 100644 --- a/playground/js-sourcemap/package.json +++ b/playground/js-sourcemap/package.json @@ -8,5 +8,8 @@ "build": "vite build", "debug": "node --inspect-brk ../../packages/vite/bin/vite", "preview": "vite preview" + }, + "dependencies": { + "@vitejs/test-importee-pkg": "file:importee-pkg" } } diff --git a/playground/js-sourcemap/with-multiline-import.ts b/playground/js-sourcemap/with-multiline-import.ts new file mode 100644 index 00000000000000..5bf8aa53214384 --- /dev/null +++ b/playground/js-sourcemap/with-multiline-import.ts @@ -0,0 +1,6 @@ +// prettier-ignore +import { + foo +} from '@vitejs/test-importee-pkg' + +console.log('with-multiline-import', foo) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3ad26d4ccdb3e..f68bc2bd12eb42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -692,7 +692,13 @@ importers: playground/import-assertion/import-assertion-dep: {} - playground/js-sourcemap: {} + playground/js-sourcemap: + dependencies: + '@vitejs/test-importee-pkg': + specifier: file:importee-pkg + version: file:playground/js-sourcemap/importee-pkg + + playground/js-sourcemap/importee-pkg: {} playground/json: devDependencies: @@ -10854,6 +10860,11 @@ packages: name: '@vitejs/test-import-assertion-dep' dev: false + file:playground/js-sourcemap/importee-pkg: + resolution: {directory: playground/js-sourcemap/importee-pkg, type: directory} + name: '@vitejs/test-importee-pkg' + dev: false + file:playground/json/json-module: resolution: {directory: playground/json/json-module, type: directory} name: '@vitejs/test-json-module' From 18374151e59f6526e88eff63c3e481885b1cd252 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sun, 3 Sep 2023 19:19:46 +0100 Subject: [PATCH 07/20] fix(vite): precisely check if files are in dirs (#14241) --- packages/vite/src/node/build.ts | 9 +++++++-- packages/vite/src/node/config.ts | 5 +++-- packages/vite/src/node/plugins/asset.ts | 9 +++++++-- .../vite/src/node/plugins/importAnalysis.ts | 5 +++-- .../src/node/plugins/importAnalysisBuild.ts | 3 ++- packages/vite/src/node/plugins/preAlias.ts | 3 ++- packages/vite/src/node/plugins/reporter.ts | 12 +++++++++--- packages/vite/src/node/plugins/resolve.ts | 9 +++++++-- packages/vite/src/node/server/hmr.ts | 14 +++++++++++--- .../vite/src/node/server/middlewares/base.ts | 4 ++-- .../src/node/server/middlewares/static.ts | 5 +++-- .../src/node/server/middlewares/transform.ts | 5 +++-- packages/vite/src/node/ssr/ssrExternal.ts | 6 +++++- packages/vite/src/node/utils.ts | 19 +++++++++++++------ 14 files changed, 77 insertions(+), 31 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index dfaab8362784c5..ec9173e18afed4 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -35,6 +35,7 @@ import { joinUrlSegments, normalizePath, requireResolveFromRootWithFallback, + withTrailingSlash, } from './utils' import { manifestPlugin } from './plugins/manifest' import type { Logger } from './logger' @@ -714,7 +715,7 @@ function prepareOutDir( for (const outDir of nonDuplicateDirs) { if ( fs.existsSync(outDir) && - !normalizePath(outDir).startsWith(config.root + '/') + !normalizePath(outDir).startsWith(withTrailingSlash(config.root)) ) { // warn if outDir is outside of root config.logger.warn( @@ -1240,5 +1241,9 @@ export const toOutputFilePathInHtml = toOutputFilePathWithoutRuntime function areSeparateFolders(a: string, b: string) { const na = normalizePath(a) const nb = normalizePath(b) - return na !== nb && !na.startsWith(nb + '/') && !nb.startsWith(na + '/') + return ( + na !== nb && + !na.startsWith(withTrailingSlash(nb)) && + !nb.startsWith(withTrailingSlash(na)) + ) } diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index f73ae2ae703e1c..b85788422672c1 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -39,6 +39,7 @@ import { mergeConfig, normalizeAlias, normalizePath, + withTrailingSlash, } from './utils' import { createPluginHookUtils, @@ -680,7 +681,7 @@ export async function resolveConfig( ), inlineConfig, root: resolvedRoot, - base: resolvedBase.endsWith('/') ? resolvedBase : resolvedBase + '/', + base: withTrailingSlash(resolvedBase), rawBase: resolvedBase, resolve: resolveOptions, publicDir: resolvedPublicDir, @@ -856,7 +857,7 @@ assetFileNames isn't equal for every build.rollupOptions.output. A single patter ) { resolved.logger.warn( colors.yellow(` -(!) Experimental legacy.buildSsrCjsExternalHeuristics and ssr.format: 'cjs' are going to be removed in Vite 5. +(!) Experimental legacy.buildSsrCjsExternalHeuristics and ssr.format: 'cjs' are going to be removed in Vite 5. Find more information and give feedback at https://github.com/vitejs/vite/discussions/13816. `), ) diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 2c298a94c31f5a..d16130b58c01b9 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -23,6 +23,7 @@ import { joinUrlSegments, normalizePath, removeLeadingSlash, + withTrailingSlash, } from '../utils' import { FS_PREFIX } from '../constants' @@ -229,7 +230,11 @@ export function checkPublicFile( return } const publicFile = path.join(publicDir, cleanUrl(url)) - if (!publicFile.startsWith(publicDir)) { + if ( + !normalizePath(publicFile).startsWith( + withTrailingSlash(normalizePath(publicDir)), + ) + ) { // can happen if URL starts with '../' return } @@ -257,7 +262,7 @@ function fileToDevUrl(id: string, config: ResolvedConfig) { if (checkPublicFile(id, config)) { // in public dir, keep the url as-is rtn = id - } else if (id.startsWith(config.root)) { + } else if (id.startsWith(withTrailingSlash(config.root))) { // in project root, infer short public path rtn = '/' + path.posix.relative(config.root, id) } else { diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 620fa38175d19d..814aa9187a9ce0 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -44,6 +44,7 @@ import { timeFrom, transformStableResult, unwrapId, + withTrailingSlash, wrapId, } from '../utils' import { getDepOptimizationConfig } from '../config' @@ -335,7 +336,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // normalize all imports into resolved URLs // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'` - if (resolved.id.startsWith(root + '/')) { + if (resolved.id.startsWith(withTrailingSlash(root))) { // in root: infer short absolute path from root url = resolved.id.slice(root.length) } else if ( @@ -672,7 +673,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { config.logger.error(e.message, { error: e }) }) } - } else if (!importer.startsWith(clientDir)) { + } else if (!importer.startsWith(withTrailingSlash(clientDir))) { if (!isInNodeModules(importer)) { // check @vite-ignore which suppresses dynamic import warning const hasViteIgnore = hasViteIgnoreRE.test( diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 1d046eb49f581e..1889790463dae8 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -16,6 +16,7 @@ import { isInNodeModules, moduleListContains, numberToPos, + withTrailingSlash, } from '../utils' import type { Plugin } from '../plugin' import { getDepOptimizationConfig } from '../config' @@ -271,7 +272,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { // normalize all imports into resolved URLs // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'` - if (resolved.id.startsWith(root + '/')) { + if (resolved.id.startsWith(withTrailingSlash(root))) { // in root: infer short absolute path from root url = resolved.id.slice(root.length) } else { diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index 931db79d231bf7..cd86bac770d823 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -14,6 +14,7 @@ import { isInNodeModules, isOptimizable, moduleListContains, + withTrailingSlash, } from '../utils' import { getDepsOptimizer } from '../optimizer' import { tryOptimizedResolve } from './resolve' @@ -114,7 +115,7 @@ function matches(pattern: string | RegExp, importee: string) { if (importee === pattern) { return true } - return importee.startsWith(pattern + '/') + return importee.startsWith(withTrailingSlash(pattern)) } function getAliasPatterns( diff --git a/packages/vite/src/node/plugins/reporter.ts b/packages/vite/src/node/plugins/reporter.ts index cbcb0409d1e3f0..74ffe11246e8ad 100644 --- a/packages/vite/src/node/plugins/reporter.ts +++ b/packages/vite/src/node/plugins/reporter.ts @@ -4,7 +4,12 @@ import { promisify } from 'node:util' import colors from 'picocolors' import type { Plugin } from 'rollup' import type { ResolvedConfig } from '../config' -import { isDefined, isInNodeModules, normalizePath } from '../utils' +import { + isDefined, + isInNodeModules, + normalizePath, + withTrailingSlash, +} from '../utils' import { LogLevels } from '../logger' const groups = [ @@ -243,9 +248,10 @@ export function buildReporterPlugin(config: ResolvedConfig): Plugin { group.name === 'JS' && entry.size / 1000 > chunkLimit if (isLarge) hasLargeChunks = true const sizeColor = isLarge ? colors.yellow : colors.dim - let log = colors.dim(relativeOutDir + '/') + let log = colors.dim(withTrailingSlash(relativeOutDir)) log += - !config.build.lib && entry.name.startsWith(assetsDir) + !config.build.lib && + entry.name.startsWith(withTrailingSlash(assetsDir)) ? colors.dim(assetsDir) + group.color( entry.name diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 63841d398c5256..26c30b9a9c9edc 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -36,6 +36,7 @@ import { safeRealpathSync, slash, tryStatSync, + withTrailingSlash, } from '../utils' import { optimizedDepInfoFromFile, optimizedDepInfoFromId } from '../optimizer' import type { DepsOptimizer } from '../optimizer' @@ -228,7 +229,11 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin { // URL // /foo -> /fs-root/foo - if (asSrc && id[0] === '/' && (rootInRoot || !id.startsWith(root))) { + if ( + asSrc && + id[0] === '/' && + (rootInRoot || !id.startsWith(withTrailingSlash(root))) + ) { const fsPath = path.resolve(root, id.slice(1)) if ((res = tryFsResolve(fsPath, options))) { debug?.(`[url] ${colors.cyan(id)} -> ${colors.dim(res)}`) @@ -939,7 +944,7 @@ export async function tryOptimizedResolve( } // match by src to correctly identify if id belongs to nested dependency - if (optimizedData.src.startsWith(idPkgDir)) { + if (optimizedData.src.startsWith(withTrailingSlash(idPkgDir))) { return depsOptimizer.getOptimizedDepId(optimizedData) } } diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index e72b1ebdf05338..f708b1114ed3cd 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -5,7 +5,13 @@ import colors from 'picocolors' import type { Update } from 'types/hmrPayload' import type { RollupError } from 'rollup' import { CLIENT_DIR } from '../constants' -import { createDebugger, normalizePath, unique, wrapId } from '../utils' +import { + createDebugger, + normalizePath, + unique, + withTrailingSlash, + wrapId, +} from '../utils' import type { ViteDevServer } from '..' import { isCSSRequest } from '../plugins/css' import { getAffectedGlobModules } from '../plugins/importMetaGlob' @@ -38,7 +44,9 @@ export interface HmrContext { } export function getShortName(file: string, root: string): string { - return file.startsWith(root + '/') ? path.posix.relative(root, file) : file + return file.startsWith(withTrailingSlash(root)) + ? path.posix.relative(root, file) + : file } export async function handleHMRUpdate( @@ -81,7 +89,7 @@ export async function handleHMRUpdate( debugHmr?.(`[file change] ${colors.dim(shortFile)}`) // (dev only) the client itself cannot be hot updated. - if (file.startsWith(normalizedClientDir)) { + if (file.startsWith(withTrailingSlash(normalizedClientDir))) { ws.send({ type: 'full-reload', path: '*', diff --git a/packages/vite/src/node/server/middlewares/base.ts b/packages/vite/src/node/server/middlewares/base.ts index cba4582486f8a9..c6af2302aef3d9 100644 --- a/packages/vite/src/node/server/middlewares/base.ts +++ b/packages/vite/src/node/server/middlewares/base.ts @@ -1,6 +1,6 @@ import type { Connect } from 'dep-types/connect' import type { ViteDevServer } from '..' -import { joinUrlSegments, stripBase } from '../../utils' +import { joinUrlSegments, stripBase, withTrailingSlash } from '../../utils' // this middleware is only active when (base !== '/') @@ -36,7 +36,7 @@ export function baseMiddleware({ } else if (req.headers.accept?.includes('text/html')) { // non-based page visit const redirectPath = - url + '/' !== base ? joinUrlSegments(base, url) : base + withTrailingSlash(url) !== base ? joinUrlSegments(base, url) : base res.writeHead(404, { 'Content-Type': 'text/html', }) diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index d1e7073179415b..8acc9e681b4e37 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -19,6 +19,7 @@ import { removeLeadingSlash, shouldServeFile, slash, + withTrailingSlash, } from '../../utils' const knownJavascriptExtensionRE = /\.[tj]sx?$/ @@ -118,7 +119,7 @@ export function serveStaticMiddleware( } if (redirectedPathname) { // dir is pre-normalized to posix style - if (redirectedPathname.startsWith(dir)) { + if (redirectedPathname.startsWith(withTrailingSlash(dir))) { redirectedPathname = redirectedPathname.slice(dir.length) } } @@ -129,7 +130,7 @@ export function serveStaticMiddleware( resolvedPathname[resolvedPathname.length - 1] === '/' && fileUrl[fileUrl.length - 1] !== '/' ) { - fileUrl = fileUrl + '/' + fileUrl = withTrailingSlash(fileUrl) } if (!ensureServingAccess(fileUrl, server, res, next)) { return diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index 737fb92f7d0fbc..f85b12a46202f4 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -16,6 +16,7 @@ import { removeImportQuery, removeTimestampQuery, unwrapId, + withTrailingSlash, } from '../../utils' import { send } from '../send' import { ERR_LOAD_URL, transformRequest } from '../transformRequest' @@ -129,10 +130,10 @@ export function transformMiddleware( // check if public dir is inside root dir const publicDir = normalizePath(server.config.publicDir) const rootDir = normalizePath(server.config.root) - if (publicDir.startsWith(rootDir)) { + if (publicDir.startsWith(withTrailingSlash(rootDir))) { const publicPath = `${publicDir.slice(rootDir.length)}/` // warn explicit public paths - if (url.startsWith(publicPath)) { + if (url.startsWith(withTrailingSlash(publicPath))) { let warning: string if (isImportRequest(url)) { diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts index 9a5bc28b39b9ee..99001f3614f330 100644 --- a/packages/vite/src/node/ssr/ssrExternal.ts +++ b/packages/vite/src/node/ssr/ssrExternal.ts @@ -13,6 +13,7 @@ import { isInNodeModules, lookupFile, normalizePath, + withTrailingSlash, } from '../utils' import type { Logger, ResolvedConfig } from '..' import { resolvePackageData } from '../packages' @@ -340,7 +341,10 @@ export function cjsShouldExternalizeForSSR( } // deep imports, check ext before externalizing - only externalize // extension-less imports and explicit .js imports - if (id.startsWith(e + '/') && (!path.extname(id) || id.endsWith('.js'))) { + if ( + id.startsWith(withTrailingSlash(e)) && + (!path.extname(id) || id.endsWith('.js')) + ) { return true } }) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 27a619947c0b0d..66d071dddc7bbd 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -123,7 +123,9 @@ export function moduleListContains( moduleList: string[] | undefined, id: string, ): boolean | undefined { - return moduleList?.some((m) => m === id || id.startsWith(m + '/')) + return moduleList?.some( + (m) => m === id || id.startsWith(withTrailingSlash(m)), + ) } export function isOptimizable( @@ -221,6 +223,13 @@ export function fsPathFromUrl(url: string): string { return fsPathFromId(cleanUrl(url)) } +export function withTrailingSlash(path: string): string { + if (path[path.length - 1] !== '/') { + return `${path}/` + } + return path +} + /** * Check if dir is a parent of file * @@ -231,9 +240,7 @@ export function fsPathFromUrl(url: string): string { * @returns true if dir is a parent of file */ export function isParentDirectory(dir: string, file: string): boolean { - if (dir[dir.length - 1] !== '/') { - dir = `${dir}/` - } + dir = withTrailingSlash(dir) return ( file.startsWith(dir) || (isCaseInsensitiveFS && file.toLowerCase().startsWith(dir.toLowerCase())) @@ -644,7 +651,7 @@ export function ensureWatchedFile( if ( file && // only need to watch if out of root - !file.startsWith(root + '/') && + !file.startsWith(withTrailingSlash(root)) && // some rollup plugins use null bytes for private resolved Ids !file.includes('\0') && fs.existsSync(file) @@ -1222,7 +1229,7 @@ export function stripBase(path: string, base: string): string { if (path === base) { return '/' } - const devBase = base[base.length - 1] === '/' ? base : base + '/' + const devBase = withTrailingSlash(base) return path.startsWith(devBase) ? path.slice(devBase.length - 1) : path } From e6c89d78f9481f0a2a2e7b845f80f2d033a2553d Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Mon, 4 Sep 2023 02:23:34 +0800 Subject: [PATCH 08/20] fix: add source map to Web Workers (fix #14216) (#14217) --- packages/vite/src/node/plugins/worker.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 202c8723f45218..ef98a4f2eddb23 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -274,9 +274,15 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { injectEnv = module?.transformResult?.code || '' } } - return { - code: injectEnv + raw, + if (injectEnv) { + const s = new MagicString(raw) + s.prepend(injectEnv) + return { + code: s.toString(), + map: s.generateMap({ hires: 'boundary' }), + } } + return } if ( query == null || From c1db72a1d3c43655fa0ff35b8ddb36c3221d231d Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Mon, 4 Sep 2023 17:53:34 +0800 Subject: [PATCH 09/20] fix(cli): convert special base (#14283) --- packages/vite/src/node/cli.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts index f6d3c9b6d6fd15..dbc8280a14876d 100644 --- a/packages/vite/src/node/cli.ts +++ b/packages/vite/src/node/cli.ts @@ -112,9 +112,21 @@ const convertHost = (v: any) => { return v } +/** + * base may be a number (like 0), should convert to empty string + */ +const convertBase = (v: any) => { + if (v === 0) { + return '' + } + return v +} + cli .option('-c, --config ', `[string] use specified config file`) - .option('--base ', `[string] public base path (default: /)`) + .option('--base ', `[string] public base path (default: /)`, { + type: [convertBase], + }) .option('-l, --logLevel ', `[string] info | warn | error | silent`) .option('--clearScreen', `[boolean] allow/disable clear screen when logging`) .option('-d, --debug [feat]', `[string | boolean] show debug logs`) From be9201e131d684c11ad417128038c8d8e3245af0 Mon Sep 17 00:00:00 2001 From: sun0day Date: Wed, 6 Sep 2023 02:27:52 +0800 Subject: [PATCH 10/20] fix(css): remove pure css chunk sourcemap (#14290) --- packages/vite/src/node/plugins/css.ts | 1 + .../__tests__/lib-entry/lib-entry.spec.ts | 8 ++++++++ playground/css-sourcemap/index.js | 1 + playground/css-sourcemap/vite.config-lib-entry.js | 13 +++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts create mode 100644 playground/css-sourcemap/index.js create mode 100644 playground/css-sourcemap/vite.config-lib-entry.js diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2b8d40b2bb20bd..36ca03fe5a676a 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -789,6 +789,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { pureCssChunkNames.forEach((fileName) => { removedPureCssFiles.set(fileName, bundle[fileName] as RenderedChunk) delete bundle[fileName] + delete bundle[`${fileName}.map`] }) } diff --git a/playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts b/playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts new file mode 100644 index 00000000000000..d2fa5f7d2c31e7 --- /dev/null +++ b/playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts @@ -0,0 +1,8 @@ +import { describe, expect, test } from 'vitest' +import { findAssetFile, isBuild } from '~utils' + +describe.runIf(isBuild)('css lib entry', () => { + test('remove useless js sourcemap', async () => { + expect(findAssetFile('linked.js.map', 'lib-entry', './')).toBe('') + }) +}) diff --git a/playground/css-sourcemap/index.js b/playground/css-sourcemap/index.js new file mode 100644 index 00000000000000..c05f09558ddaf0 --- /dev/null +++ b/playground/css-sourcemap/index.js @@ -0,0 +1 @@ +export default 'hello' diff --git a/playground/css-sourcemap/vite.config-lib-entry.js b/playground/css-sourcemap/vite.config-lib-entry.js new file mode 100644 index 00000000000000..600b7414a48b75 --- /dev/null +++ b/playground/css-sourcemap/vite.config-lib-entry.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + cssCodeSplit: true, + sourcemap: true, + outDir: 'dist/lib-entry', + lib: { + entry: ['./index.js', './linked.css'], + formats: ['es'], + }, + }, +}) From d8529f9b91482d67f7341b61b2586cac1cb00014 Mon Sep 17 00:00:00 2001 From: Yuhei Yasuda Date: Wed, 6 Sep 2023 23:08:23 +0900 Subject: [PATCH 11/20] fix: include `vite/types/*` in exports field (#14296) --- packages/vite/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/vite/package.json b/packages/vite/package.json index f183caa6ddd103..56b1f7258a8e2c 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -28,6 +28,9 @@ "types": "./client.d.ts" }, "./dist/client/*": "./dist/client/*", + "./types/*": { + "types": "./types/*" + }, "./package.json": "./package.json" }, "files": [ From 4680cbab32b4e9df8053634f3bf6738d68ba3b80 Mon Sep 17 00:00:00 2001 From: anyesu Date: Thu, 7 Sep 2023 02:21:02 +0800 Subject: [PATCH 12/20] fix(optimizer): define crawlDeps after scanProcessing and optimizationResult are complete (fix #14284) (#14285) --- packages/vite/src/node/optimizer/optimizer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/optimizer/optimizer.ts b/packages/vite/src/node/optimizer/optimizer.ts index 84f1575e1ec755..edb4af5232b995 100644 --- a/packages/vite/src/node/optimizer/optimizer.ts +++ b/packages/vite/src/node/optimizer/optimizer.ts @@ -619,8 +619,6 @@ async function createDepsOptimizer( return } - const crawlDeps = Object.keys(metadata.discovered) - // Await for the scan+optimize step running in the background // It normally should be over by the time crawling of user code ended await depsOptimizer.scanProcessing @@ -630,6 +628,7 @@ async function createDepsOptimizer( optimizationResult = undefined currentlyProcessing = false + const crawlDeps = Object.keys(metadata.discovered) const scanDeps = Object.keys(result.metadata.optimized) if (scanDeps.length === 0 && crawlDeps.length === 0) { @@ -680,6 +679,7 @@ async function createDepsOptimizer( runOptimizer(result) } } else { + const crawlDeps = Object.keys(metadata.discovered) currentlyProcessing = false if (crawlDeps.length === 0) { From 50d2e4acee7b1a467221bf584abaa89e5cc23a6d Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 8 Sep 2023 15:07:46 +0800 Subject: [PATCH 13/20] fix(css): reset render cache on renderStart (#14326) --- packages/vite/src/node/plugins/css.ts | 2 +- playground/lib/__tests__/serve.ts | 6 +++ playground/lib/src/main-multiple-output.js | 6 +++ playground/lib/src/sub-multiple-output.js | 6 +++ playground/lib/vite.multiple-output.config.js | 39 +++++++++++++++++++ 5 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 playground/lib/src/main-multiple-output.js create mode 100644 playground/lib/src/sub-multiple-output.js create mode 100644 playground/lib/vite.multiple-output.config.js diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 36ca03fe5a676a..f1ae186932d4ee 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -406,7 +406,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { return { name: 'vite:css-post', - buildStart() { + renderStart() { // Ensure new caches for every build (i.e. rebuilding in watch mode) pureCssChunks = new Set() outputToExtractedCSSMap = new Map() diff --git a/playground/lib/__tests__/serve.ts b/playground/lib/__tests__/serve.ts index 44e1728ffd6086..e07efe526eaf00 100644 --- a/playground/lib/__tests__/serve.ts +++ b/playground/lib/__tests__/serve.ts @@ -61,6 +61,12 @@ export async function serve(): Promise<{ close(): Promise }> { configFile: path.resolve(__dirname, '../vite.dyimport.config.js'), }) + await build({ + root: rootDir, + logLevel: 'warn', // output esbuild warns + configFile: path.resolve(__dirname, '../vite.multiple-output.config.js'), + }) + await build({ root: rootDir, logLevel: 'warn', // output esbuild warns diff --git a/playground/lib/src/main-multiple-output.js b/playground/lib/src/main-multiple-output.js new file mode 100644 index 00000000000000..78e22283ccbc1b --- /dev/null +++ b/playground/lib/src/main-multiple-output.js @@ -0,0 +1,6 @@ +// import file to test css build handling +import './index.css' + +export default async function message(sel) { + document.querySelector(sel).textContent = 'success' +} diff --git a/playground/lib/src/sub-multiple-output.js b/playground/lib/src/sub-multiple-output.js new file mode 100644 index 00000000000000..78e22283ccbc1b --- /dev/null +++ b/playground/lib/src/sub-multiple-output.js @@ -0,0 +1,6 @@ +// import file to test css build handling +import './index.css' + +export default async function message(sel) { + document.querySelector(sel).textContent = 'success' +} diff --git a/playground/lib/vite.multiple-output.config.js b/playground/lib/vite.multiple-output.config.js new file mode 100644 index 00000000000000..e986221ad6ca9b --- /dev/null +++ b/playground/lib/vite.multiple-output.config.js @@ -0,0 +1,39 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +const root = process.env.VITEST + ? path.resolve(__dirname, '../../playground-temp/lib') + : __dirname + +export default defineConfig({ + build: { + lib: { + // set multiple entrypoint to trigger css chunking + entry: { + main: path.resolve(__dirname, 'src/main-multiple-output.js'), + sub: path.resolve(__dirname, 'src/sub-multiple-output.js'), + }, + name: 'MyLib', + }, + outDir: 'dist/multiple-output', + rollupOptions: { + // due to playground-temp, the `dir` needs to be relative to the resolvedRoot + output: [ + { + dir: path.resolve(root, 'dist/multiple-output/es'), + format: 'es', + entryFileNames: 'index.mjs', + assetFileNames: 'assets/mylib.css', + }, + { + dir: path.resolve(root, 'dist/multiple-output/cjs'), + format: 'cjs', + entryFileNames: 'index.cjs', + assetFileNames: 'assets/mylib.css', + }, + ], + }, + cssCodeSplit: true, + }, + cacheDir: 'node_modules/.vite-multiple-output', +}) From e1c365c2bd9caee60c0576e9011acd5cc94ba794 Mon Sep 17 00:00:00 2001 From: sun0day Date: Sun, 10 Sep 2023 14:47:25 +0800 Subject: [PATCH 14/20] fix(manifest): preserve pure css chunk assets (#14297) --- packages/vite/src/node/plugins/css.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index f1ae186932d4ee..933cb507a3150c 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -769,11 +769,15 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // chunks instead. chunk.imports = chunk.imports.filter((file) => { if (pureCssChunkNames.includes(file)) { - const { importedCss } = (bundle[file] as OutputChunk) - .viteMetadata! + const { importedCss, importedAssets } = ( + bundle[file] as OutputChunk + ).viteMetadata! importedCss.forEach((file) => chunk.viteMetadata!.importedCss.add(file), ) + importedAssets.forEach((file) => + chunk.viteMetadata!.importedAssets.add(file), + ) return false } return true From 4b1c92d6228f9869da76958d3c2cd12586ce84ab Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Mon, 18 Sep 2023 17:09:22 +0800 Subject: [PATCH 15/20] feat: improve deno and bun support (#14379) --- packages/vite/src/node/config.ts | 9 +++-- packages/vite/src/node/ssr/ssrModuleLoader.ts | 2 +- packages/vite/src/node/utils.ts | 40 ++++++++----------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index b85788422672c1..123e206ccb1e3f 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -33,6 +33,7 @@ import { dynamicImport, isBuiltin, isExternalUrl, + isNodeBuiltin, isObject, lookupFile, mergeAlias, @@ -1082,13 +1083,15 @@ async function bundleConfigFile( if ( kind === 'entry-point' || path.isAbsolute(id) || - isBuiltin(id) + isNodeBuiltin(id) ) { return } - // partial deno support as `npm:` does not work with esbuild - if (id.startsWith('npm:')) { + // With the `isNodeBuiltin` check above, this check captures if the builtin is a + // non-node built-in, which esbuild doesn't know how to handle. In that case, we + // externalize it so the non-node runtime handles it instead. + if (isBuiltin(id)) { return { external: true } } diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index 3e498809f32829..129d91b5f0a428 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -268,7 +268,7 @@ async function nodeImport( resolveOptions: InternalResolveOptionsWithOverrideConditions, ) { let url: string - if (id.startsWith('node:') || id.startsWith('data:') || isBuiltin(id)) { + if (id.startsWith('data:') || isBuiltin(id)) { url = id } else { const resolved = tryNodeResolve( diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 66d071dddc7bbd..a53ee86e614e67 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -88,31 +88,25 @@ export const flattenId = (id: string): string => export const normalizeId = (id: string): string => id.replace(replaceNestedIdRE, ' > ') -//TODO: revisit later to see if the edge case that "compiling using node v12 code to be run in node v16 in the server" is what we intend to support. -const builtins = new Set([ - ...builtinModules, - 'assert/strict', - 'diagnostics_channel', - 'dns/promises', - 'fs/promises', - 'path/posix', - 'path/win32', - 'readline/promises', - 'stream/consumers', - 'stream/promises', - 'stream/web', - 'timers/promises', - 'util/types', - 'wasi', -]) - +// Supported by Node, Deno, Bun const NODE_BUILTIN_NAMESPACE = 'node:' +// Supported by Deno +const NPM_BUILTIN_NAMESPACE = 'npm:' +// Supported by Bun +const BUN_BUILTIN_NAMESPACE = 'bun:' +// Some runtimes like Bun injects namespaced modules here, which is not a node builtin +const nodeBuiltins = builtinModules.filter((id) => !id.includes(':')) + +// TODO: Use `isBuiltin` from `node:module`, but Deno doesn't support it export function isBuiltin(id: string): boolean { - return builtins.has( - id.startsWith(NODE_BUILTIN_NAMESPACE) - ? id.slice(NODE_BUILTIN_NAMESPACE.length) - : id, - ) + if (process.versions.deno && id.startsWith(NPM_BUILTIN_NAMESPACE)) return true + if (process.versions.bun && id.startsWith(BUN_BUILTIN_NAMESPACE)) return true + return isNodeBuiltin(id) +} + +export function isNodeBuiltin(id: string): boolean { + if (id.startsWith(NODE_BUILTIN_NAMESPACE)) return true + return nodeBuiltins.includes(id) } export function isInNodeModules(id: string): boolean { From cf605519d8ab02523cf918609f0705e30a5741d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Mon, 18 Sep 2023 18:09:35 +0900 Subject: [PATCH 16/20] fix: handle errors during `hasWorkspacePackageJSON` function (#14394) --- packages/vite/src/node/server/searchRoot.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/server/searchRoot.ts b/packages/vite/src/node/server/searchRoot.ts index fb8c9c4cfc215a..edb7a76946266e 100644 --- a/packages/vite/src/node/server/searchRoot.ts +++ b/packages/vite/src/node/server/searchRoot.ts @@ -27,8 +27,12 @@ function hasWorkspacePackageJSON(root: string): boolean { if (!isFileReadable(path)) { return false } - const content = JSON.parse(fs.readFileSync(path, 'utf-8')) || {} - return !!content.workspaces + try { + const content = JSON.parse(fs.readFileSync(path, 'utf-8')) || {} + return !!content.workspaces + } catch { + return false + } } function hasRootFile(root: string): boolean { From e6213c14d5e3d3eacfbadff2f906c6246fe9c103 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 26 Sep 2023 16:18:16 +0800 Subject: [PATCH 17/20] fix(import-analysis): preserve importedUrls import order (#14465) --- .../vite/src/node/plugins/importAnalysis.ts | 7 ++++-- .../__tests__/module-graph.spec.ts | 12 ++++++++++ playground/module-graph/empty.js | 0 .../module-graph/imported-urls-order.js | 7 ++++++ playground/module-graph/index.html | 10 ++++++++ playground/module-graph/package.json | 12 ++++++++++ playground/module-graph/vite.config.ts | 23 +++++++++++++++++++ pnpm-lock.yaml | 2 ++ 8 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 playground/module-graph/__tests__/module-graph.spec.ts create mode 100644 playground/module-graph/empty.js create mode 100644 playground/module-graph/imported-urls-order.js create mode 100644 playground/module-graph/index.html create mode 100644 playground/module-graph/package.json create mode 100644 playground/module-graph/vite.config.ts diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 814aa9187a9ce0..ced27abf1529a1 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -278,7 +278,6 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { let needQueryInjectHelper = false let s: MagicString | undefined const str = () => s || (s = new MagicString(source)) - const importedUrls = new Set() let isPartiallySelfAccepting = false const importedBindings = enablePartialAccept ? new Map>() @@ -411,6 +410,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { return [url, resolved.id] } + const orderedImportedUrls = new Array(imports.length) const orderedAcceptedUrls = new Array | undefined>( imports.length, ) @@ -640,7 +640,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const hmrUrl = unwrapId(stripBase(url, base)) const isLocalImport = !isExternalUrl(hmrUrl) && !isDataUrl(hmrUrl) if (isLocalImport) { - importedUrls.add(hmrUrl) + orderedImportedUrls[index] = hmrUrl } if (enablePartialAccept && importedBindings) { @@ -718,6 +718,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { }), ) + const importedUrls = new Set( + orderedImportedUrls.filter(Boolean) as string[], + ) const acceptedUrls = mergeAcceptedUrls(orderedAcceptedUrls) const acceptedExports = mergeAcceptedUrls(orderedAcceptedExports) diff --git a/playground/module-graph/__tests__/module-graph.spec.ts b/playground/module-graph/__tests__/module-graph.spec.ts new file mode 100644 index 00000000000000..bfabd53f289724 --- /dev/null +++ b/playground/module-graph/__tests__/module-graph.spec.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' +import { isServe, page, viteServer } from '~utils' + +test.runIf(isServe)('importedUrls order is preserved', async () => { + const el = page.locator('.imported-urls-order') + expect(await el.textContent()).toBe('[success]') + const mod = await viteServer.moduleGraph.getModuleByUrl( + '/imported-urls-order.js', + ) + const importedModuleIds = [...mod.importedModules].map((m) => m.url) + expect(importedModuleIds).toEqual(['\x00virtual:slow-module', '/empty.js']) +}) diff --git a/playground/module-graph/empty.js b/playground/module-graph/empty.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/playground/module-graph/imported-urls-order.js b/playground/module-graph/imported-urls-order.js new file mode 100644 index 00000000000000..9ccf4527e6665d --- /dev/null +++ b/playground/module-graph/imported-urls-order.js @@ -0,0 +1,7 @@ +import { msg } from 'virtual:slow-module' +import './empty.js' + +export default msg + +// This module tests that the import order is preserved in this module's `importedUrls` property +// as the imports can be processed in parallel diff --git a/playground/module-graph/index.html b/playground/module-graph/index.html new file mode 100644 index 00000000000000..663a7d7ed0066a --- /dev/null +++ b/playground/module-graph/index.html @@ -0,0 +1,10 @@ +
+ + diff --git a/playground/module-graph/package.json b/playground/module-graph/package.json new file mode 100644 index 00000000000000..35e0799c262738 --- /dev/null +++ b/playground/module-graph/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vitejs/test-hmr", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/playground/module-graph/vite.config.ts b/playground/module-graph/vite.config.ts new file mode 100644 index 00000000000000..53e07ff3bfd483 --- /dev/null +++ b/playground/module-graph/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from 'vite' +import type { Plugin } from 'vite' + +export default defineConfig({ + plugins: [slowModulePlugin()], +}) + +function slowModulePlugin(): Plugin { + return { + name: 'slow-module', + resolveId(id) { + if (id === 'virtual:slow-module') { + return '\0virtual:slow-module' + } + }, + async load(id) { + if (id === '\0virtual:slow-module') { + await new Promise((resolve) => setTimeout(resolve, 500)) + return `export const msg = '[success]'` + } + }, + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f68bc2bd12eb42..83017836b874ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -740,6 +740,8 @@ importers: playground/minify/dir/module: {} + playground/module-graph: {} + playground/multiple-entrypoints: devDependencies: fast-glob: From ae7b4cd3de0d3036686ee26915079bce5f4641e2 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 28 Sep 2023 15:30:24 +0800 Subject: [PATCH 18/20] fix(analysis): warnings for dynamic imports that use static template literals (#14458) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- .../vite/src/node/plugins/importAnalysis.ts | 17 ++++++++++++++--- playground/dynamic-import/index.html | 2 ++ playground/dynamic-import/nested/index.js | 4 ++++ playground/dynamic-import/nested/static.js | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 playground/dynamic-import/nested/static.js diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index ced27abf1529a1..45e8a38788353c 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -82,6 +82,8 @@ export const hasViteIgnoreRE = /\/\*\s*@vite-ignore\s*\*\// const cleanUpRawUrlRE = /\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm const urlIsStringRE = /^(?:'.*'|".*"|`.*`)$/ +const templateLiteralRE = /^\s*`(.*)`\s*$/ + interface UrlPosition { url: string start: number @@ -426,12 +428,13 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { ss: expStart, se: expEnd, d: dynamicIndex, - // #2083 User may use escape path, - // so use imports[index].n to get the unescaped string - n: specifier, a: assertIndex, } = importSpecifier + // #2083 User may use escape path, + // so use imports[index].n to get the unescaped string + let specifier = importSpecifier.n + const rawUrl = source.slice(start, end) // check import.meta usage @@ -469,6 +472,14 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { hasEnv = true } return + } else if (templateLiteralRE.test(rawUrl)) { + // If the import has backticks but isn't transformed as a glob import + // (as there's nothing to glob), check if it's simply a plain string. + // If so, we can replace the specifier as a plain string to prevent + // an incorrect "cannot be analyzed" warning. + if (!(rawUrl.includes('${') && rawUrl.includes('}'))) { + specifier = rawUrl.replace(templateLiteralRE, '$1') + } } const isDynamicImport = dynamicIndex > -1 diff --git a/playground/dynamic-import/index.html b/playground/dynamic-import/index.html index 31cf128ddd12d1..afc5c03f8b862a 100644 --- a/playground/dynamic-import/index.html +++ b/playground/dynamic-import/index.html @@ -35,6 +35,8 @@
+
+
diff --git a/playground/dynamic-import/nested/index.js b/playground/dynamic-import/nested/index.js index 180dd6b5ad3b6b..25a6426e3f7ee1 100644 --- a/playground/dynamic-import/nested/index.js +++ b/playground/dynamic-import/nested/index.js @@ -131,4 +131,8 @@ import(`../nested/nested/${base}.js`).then((mod) => { text('.dynamic-import-nested-self', mod.self) }) +import(`../nested/static.js`).then((mod) => { + text('.dynamic-import-static', mod.self) +}) + console.log('index.js') diff --git a/playground/dynamic-import/nested/static.js b/playground/dynamic-import/nested/static.js new file mode 100644 index 00000000000000..02dd476388a6e4 --- /dev/null +++ b/playground/dynamic-import/nested/static.js @@ -0,0 +1 @@ +export const self = 'dynamic-import-static' From 56c5f4fa01ec11d71aab4802ddc929cbdd0a6a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Costa?= Date: Mon, 2 Oct 2023 01:51:40 -0700 Subject: [PATCH 19/20] fix(resolve): support submodules of optional peer deps (#14489) --- packages/vite/src/node/plugins/resolve.ts | 6 ++++-- .../optimize-deps/__tests__/optimize-deps.spec.ts | 13 +++++++++++++ .../dep-with-optional-peer-dep-submodule/index.js | 7 +++++++ .../package.json | 15 +++++++++++++++ playground/optimize-deps/index.html | 13 +++++++++++++ playground/optimize-deps/package.json | 1 + pnpm-lock.yaml | 15 +++++++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 playground/optimize-deps/dep-with-optional-peer-dep-submodule/index.js create mode 100644 playground/optimize-deps/dep-with-optional-peer-dep-submodule/package.json diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 26c30b9a9c9edc..5932b720875bef 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -730,9 +730,11 @@ export function tryNodeResolve( ) { const mainPkg = findNearestMainPackageData(basedir, packageCache)?.data if (mainPkg) { + const pkgName = getNpmPackageName(id) if ( - mainPkg.peerDependencies?.[id] && - mainPkg.peerDependenciesMeta?.[id]?.optional + pkgName != null && + mainPkg.peerDependencies?.[pkgName] && + mainPkg.peerDependenciesMeta?.[pkgName]?.optional ) { return { id: `${optionalPeerDepId}:${id}:${mainPkg.name}`, diff --git a/playground/optimize-deps/__tests__/optimize-deps.spec.ts b/playground/optimize-deps/__tests__/optimize-deps.spec.ts index 1e54aa1bd700b5..a42e8838288e9e 100644 --- a/playground/optimize-deps/__tests__/optimize-deps.spec.ts +++ b/playground/optimize-deps/__tests__/optimize-deps.spec.ts @@ -108,6 +108,19 @@ test('dep with optional peer dep', async () => { } }) +test('dep with optional peer dep submodule', async () => { + expect( + await page.textContent('.dep-with-optional-peer-dep-submodule'), + ).toMatch(`[success]`) + if (isServe) { + expect(browserErrors.map((error) => error.message)).toEqual( + expect.arrayContaining([ + 'Could not resolve "foobar/baz" imported by "@vitejs/test-dep-with-optional-peer-dep-submodule". Is it installed?', + ]), + ) + } +}) + test('dep with css import', async () => { expect(await getColor('.dep-linked-include')).toBe('red') }) diff --git a/playground/optimize-deps/dep-with-optional-peer-dep-submodule/index.js b/playground/optimize-deps/dep-with-optional-peer-dep-submodule/index.js new file mode 100644 index 00000000000000..d2ace777ebf930 --- /dev/null +++ b/playground/optimize-deps/dep-with-optional-peer-dep-submodule/index.js @@ -0,0 +1,7 @@ +export function callItself() { + return '[success]' +} + +export async function callPeerDepSubmodule() { + return await import('foobar/baz') +} diff --git a/playground/optimize-deps/dep-with-optional-peer-dep-submodule/package.json b/playground/optimize-deps/dep-with-optional-peer-dep-submodule/package.json new file mode 100644 index 00000000000000..82dcdff5dea262 --- /dev/null +++ b/playground/optimize-deps/dep-with-optional-peer-dep-submodule/package.json @@ -0,0 +1,15 @@ +{ + "name": "@vitejs/test-dep-with-optional-peer-dep-submodule", + "private": true, + "version": "0.0.0", + "main": "index.js", + "type": "module", + "peerDependencies": { + "foobar": "0.0.0" + }, + "peerDependenciesMeta": { + "foobar": { + "optional": true + } + } +} diff --git a/playground/optimize-deps/index.html b/playground/optimize-deps/index.html index 6cf34f1f4c3469..af31bcff8a4d29 100644 --- a/playground/optimize-deps/index.html +++ b/playground/optimize-deps/index.html @@ -65,6 +65,9 @@

Import from dependency with dynamic import

Import from dependency with optional peer dep

+

Import from dependency with optional peer dep submodule

+
+

Externalize known non-js files in optimize included dep

@@ -205,6 +208,16 @@

Pre bundle css modules require

callPeerDep() + +