diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index f16498c3bbf50c..b6eb4e5904c888 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -445,6 +445,21 @@ export async function createPluginContainer( } } } + } else if (err.loc) { + if (!err.frame) { + let code = err.pluginCode + if (err.loc.file) { + err.id = normalizePath(err.loc.file) + if (!code) { + try { + code = fs.readFileSync(err.loc.file, 'utf-8') + } catch {} + } + } + if (code) { + err.frame = generateCodeFrame(code, err.loc) + } + } } return err } diff --git a/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts b/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts index fc3e32bcd616c0..a051f40f83219b 100644 --- a/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts +++ b/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts @@ -33,12 +33,12 @@ async function withServe(base: string, fn: () => Promise) { const config = getConfig(base) const server = await createServer(config) await server.listen() - await new Promise((r) => setTimeout(r, 500)) try { await page.goto(server.resolvedUrls.local[0]) await fn() } finally { + await page.goto('about:blank') // move to a different page to avoid auto-refresh after server start await server.close() } } diff --git a/playground/file-delete-restore/App.jsx b/playground/file-delete-restore/App.jsx deleted file mode 100644 index de41c853cdc3ed..00000000000000 --- a/playground/file-delete-restore/App.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useState } from 'react' -import Child from './Child' - -function App() { - return ( -
- -
- ) -} - -export default App diff --git a/playground/file-delete-restore/Child.jsx b/playground/file-delete-restore/Child.jsx deleted file mode 100644 index fa6e1aefb6d829..00000000000000 --- a/playground/file-delete-restore/Child.jsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Child() { - return

Child state 1

-} diff --git a/playground/file-delete-restore/__tests__/file-delete-restore.spec.ts b/playground/file-delete-restore/__tests__/file-delete-restore.spec.ts deleted file mode 100644 index 90887f93461770..00000000000000 --- a/playground/file-delete-restore/__tests__/file-delete-restore.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { test } from 'vitest' -import { - addFile, - editFile, - isBuild, - page, - removeFile, - untilUpdated, -} from '~utils' - -test.runIf(isBuild)( - 'should hmr when file is deleted and restored', - async () => { - await untilUpdated(() => page.textContent('p'), 'Child state 1') - - editFile('Child.jsx', (code) => - code.replace('Child state 1', 'Child state 2'), - ) - - await untilUpdated(() => page.textContent('p'), 'Child state 2') - - editFile('App.jsx', (code) => - code - .replace(`import Child from './Child'`, '') - .replace(``, '

Child deleted

'), - ) - removeFile('Child.jsx') - await untilUpdated(() => page.textContent('p'), 'Child deleted') - - // restore Child.jsx - addFile( - 'Child.jsx', - ` export default function Child() { - return

Child state 1

- } - `, - ) - - // restore App.jsx - editFile( - 'App.jsx', - (code) => - `import { useState } from 'react' - import Child from './Child' - - function App() { - return ( -
- -
- ) - } - - export default App - `, - ) - - await untilUpdated(() => page.textContent('p'), 'Child state 1') - }, -) diff --git a/playground/file-delete-restore/index.html b/playground/file-delete-restore/index.html deleted file mode 100644 index 7417c442dfbf36..00000000000000 --- a/playground/file-delete-restore/index.html +++ /dev/null @@ -1,10 +0,0 @@ -
- diff --git a/playground/file-delete-restore/package.json b/playground/file-delete-restore/package.json deleted file mode 100644 index ce3246877e945f..00000000000000 --- a/playground/file-delete-restore/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@vitejs/test-file-delete-restore", - "private": true, - "version": "0.0.0", - "scripts": { - "dev": "vite", - "build": "vite build", - "debug": "node --inspect-brk ../../packages/vite/bin/vite", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@vitejs/plugin-react": "^3.0.0-alpha.0" - } -} diff --git a/playground/file-delete-restore/vite.config.js b/playground/file-delete-restore/vite.config.js deleted file mode 100644 index d47f7cfc6704c5..00000000000000 --- a/playground/file-delete-restore/vite.config.js +++ /dev/null @@ -1,12 +0,0 @@ -const react = require('@vitejs/plugin-react') - -/** - * @type {import('vite').UserConfig} - */ -module.exports = { - plugins: [react()], - build: { - // to make tests faster - minify: false, - }, -} diff --git a/playground/hmr/__tests__/hmr.spec.ts b/playground/hmr/__tests__/hmr.spec.ts index c7f8b79d75fa5c..b6c786efefba54 100644 --- a/playground/hmr/__tests__/hmr.spec.ts +++ b/playground/hmr/__tests__/hmr.spec.ts @@ -1,10 +1,12 @@ import { beforeAll, describe, expect, it, test } from 'vitest' import { + addFile, browserLogs, editFile, getBg, isBuild, page, + removeFile, untilBrowserLogAfter, untilUpdated, viteTestUrl, @@ -713,4 +715,63 @@ if (!isBuild) { await navigationPromise }, /500/) }) + + test('should hmr when file is deleted and restored', async () => { + await page.goto(viteTestUrl) + + const parentFile = 'file-delete-restore/parent.js' + const childFile = 'file-delete-restore/child.js' + + await untilUpdated( + () => page.textContent('.file-delete-restore'), + 'parent:child', + ) + + editFile(childFile, (code) => + code.replace("value = 'child'", "value = 'child1'"), + ) + await untilUpdated( + () => page.textContent('.file-delete-restore'), + 'parent:child1', + ) + + editFile(parentFile, (code) => + code.replace( + "export { value as childValue } from './child'", + "export const childValue = 'not-child'", + ), + ) + removeFile(childFile) + await untilUpdated( + () => page.textContent('.file-delete-restore'), + 'parent:not-child', + ) + + addFile( + childFile, + ` +import { rerender } from './runtime' + +export const value = 'child' + +if (import.meta.hot) { + import.meta.hot.accept((newMod) => { + if (!newMod) return + + rerender({ child: newMod.value }) + }) +} +`, + ) + editFile(parentFile, (code) => + code.replace( + "export const childValue = 'not-child'", + "export { value as childValue } from './child'", + ), + ) + await untilUpdated( + () => page.textContent('.file-delete-restore'), + 'parent:child', + ) + }) } diff --git a/playground/hmr/file-delete-restore/child.js b/playground/hmr/file-delete-restore/child.js new file mode 100644 index 00000000000000..704c7d8c7e98cc --- /dev/null +++ b/playground/hmr/file-delete-restore/child.js @@ -0,0 +1,11 @@ +import { rerender } from './runtime' + +export const value = 'child' + +if (import.meta.hot) { + import.meta.hot.accept((newMod) => { + if (!newMod) return + + rerender({ child: newMod.value }) + }) +} diff --git a/playground/hmr/file-delete-restore/index.js b/playground/hmr/file-delete-restore/index.js new file mode 100644 index 00000000000000..fa4908a32662ac --- /dev/null +++ b/playground/hmr/file-delete-restore/index.js @@ -0,0 +1,4 @@ +import { render } from './runtime' +import { childValue, parentValue } from './parent' + +render({ parent: parentValue, child: childValue }) diff --git a/playground/hmr/file-delete-restore/parent.js b/playground/hmr/file-delete-restore/parent.js new file mode 100644 index 00000000000000..050bfa6d49b4c0 --- /dev/null +++ b/playground/hmr/file-delete-restore/parent.js @@ -0,0 +1,12 @@ +import { rerender } from './runtime' + +export const parentValue = 'parent' +export { value as childValue } from './child' + +if (import.meta.hot) { + import.meta.hot.accept((newMod) => { + if (!newMod) return + + rerender({ child: newMod.childValue, parent: newMod.parentValue }) + }) +} diff --git a/playground/hmr/file-delete-restore/runtime.js b/playground/hmr/file-delete-restore/runtime.js new file mode 100644 index 00000000000000..c038eed173cdbf --- /dev/null +++ b/playground/hmr/file-delete-restore/runtime.js @@ -0,0 +1,16 @@ +let state = {} + +export const render = (newState) => { + state = newState + apply() +} + +export const rerender = (updates) => { + state = { ...state, ...updates } + apply() +} + +const apply = () => { + document.querySelector('.file-delete-restore').textContent = + Object.values(state).join(':') +} diff --git a/playground/hmr/hmr.ts b/playground/hmr/hmr.ts index 600685dce40a54..a9c1e332d529e3 100644 --- a/playground/hmr/hmr.ts +++ b/playground/hmr/hmr.ts @@ -3,6 +3,7 @@ import { virtual } from 'virtual:file' import { foo as depFoo, nestedFoo } from './hmrDep' import './importing-updated' import './invalidation/parent' +import './file-delete-restore' export const foo = 1 text('.app', foo) diff --git a/playground/hmr/index.html b/playground/hmr/index.html index 3ddd29be711a67..9aadeaae6f3ff8 100644 --- a/playground/hmr/index.html +++ b/playground/hmr/index.html @@ -30,3 +30,4 @@
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 475c1a9717078b..2c5b6dfd424e3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -483,17 +483,6 @@ importers: slash5: /slash/5.0.0 vue: 3.2.45 - playground/file-delete-restore: - specifiers: - '@vitejs/plugin-react': ^3.0.0-alpha.0 - react: ^18.2.0 - react-dom: ^18.2.0 - dependencies: - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - devDependencies: - '@vitejs/plugin-react': 3.0.0-alpha.2 - playground/fs-serve: specifiers: {} @@ -1268,13 +1257,6 @@ packages: jsesc: 2.5.2 dev: true - /@babel/helper-annotate-as-pure/7.18.6: - resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.20.5 - dev: true - /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} engines: {node: '>=6.9.0'} @@ -1331,11 +1313,6 @@ packages: - supports-color dev: true - /@babel/helper-plugin-utils/7.20.2: - resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-simple-access/7.20.2: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} @@ -1390,60 +1367,6 @@ packages: dependencies: '@babel/types': 7.20.5 - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.5: - resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.20.5: - resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.5 - dev: true - - /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.5: - resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.5: - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.20.5: - resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 - '@babel/types': 7.20.5 - dev: true - /@babel/runtime/7.20.6: resolution: {integrity: sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==} engines: {node: '>=6.9.0'} @@ -2319,23 +2242,6 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-react/3.0.0-alpha.2: - resolution: {integrity: sha512-VObQvgDSWuEyxsCm/7gnMAzO82v7/JW1Yki6gvgFishqY4TMVHtm+3k7m6ci5zEBI22d+vfXh54mOpVHE+VgKA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^3.0.0 - dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.5 - '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.20.5 - '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.5 - '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.5 - magic-string: 0.26.7 - react-refresh: 0.14.0 - transitivePeerDependencies: - - supports-color - dev: true - /@vitejs/plugin-vue/3.2.0_vp6yl3plkfvihwzjgzhs7aemmy: resolution: {integrity: sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -7032,11 +6938,6 @@ packages: scheduler: 0.23.0 dev: false - /react-refresh/0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} - engines: {node: '>=0.10.0'} - dev: true - /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'}