diff --git a/packages/playground/css-sourcemap/__tests__/build.spec.ts b/packages/playground/css-sourcemap/__tests__/build.spec.ts new file mode 100644 index 00000000000000..e36c1f52d2c1f8 --- /dev/null +++ b/packages/playground/css-sourcemap/__tests__/build.spec.ts @@ -0,0 +1,13 @@ +import { isBuild } from 'testUtils' + +if (isBuild) { + test('should not output sourcemap warning (#4939)', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch('Sourcemap is likely to be incorrect') + }) + }) +} else { + test('this file only includes test for build', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/css-sourcemap/__tests__/serve.spec.ts b/packages/playground/css-sourcemap/__tests__/serve.spec.ts new file mode 100644 index 00000000000000..50c256298143ab --- /dev/null +++ b/packages/playground/css-sourcemap/__tests__/serve.spec.ts @@ -0,0 +1,214 @@ +import { fromComment } from 'convert-source-map' +import { URL } from 'url' +import { normalizePath } from 'vite' +import { isBuild, testDir } from 'testUtils' + +if (!isBuild) { + const root = normalizePath(testDir) + + const getStyleTagContentIncluding = async (content: string) => { + const styles = await page.$$('style') + for (const style of styles) { + const text = await style.textContent() + if (text.includes(content)) { + return text + } + } + throw new Error('Not found') + } + + const extractSourcemap = (content: string) => { + const lines = content.trim().split('\n') + return fromComment(lines[lines.length - 1]).toObject() + } + + const formatSourcemapForSnapshot = (map: any) => { + const m = { ...map } + delete m.file + delete m.names + m.sources = m.sources.map((source) => source.replace(root, '/root')) + return m + } + + test('linked css', async () => { + const res = await page.request.get( + new URL('./linked.css', page.url()).href, + { + headers: { + accept: 'text/css' + } + } + ) + const css = await res.text() + const lines = css.split('\n') + expect(lines[lines.length - 1].includes('/*')).toBe(false) // expect no sourcemap + }) + + test('linked css with import', async () => { + const res = await page.request.get( + new URL('./linked-with-import.css', page.url()).href, + { + headers: { + accept: 'text/css' + } + } + ) + const css = await res.text() + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/be-imported.css", + "/root/linked-with-import.css", + ], + "sourcesContent": Array [ + ".be-imported { + color: red; + } + ", + "@import '@/be-imported.css'; + + .linked-with-import { + color: red; + } + ", + ], + "version": 3, + } + `) + }) + + test('imported css', async () => { + const css = await getStyleTagContentIncluding('.imported ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACX,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACb,CAAC;", + "sources": Array [ + "/root/imported.css", + ], + "sourcesContent": Array [ + ".imported { + color: red; + } + ", + ], + "version": 3, + } + `) + }) + + test('imported css with import', async () => { + const css = await getStyleTagContentIncluding('.imported-with-import ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/be-imported.css", + "/root/imported-with-import.css", + ], + "sourcesContent": Array [ + ".be-imported { + color: red; + } + ", + "@import '@/be-imported.css'; + + .imported-with-import { + color: red; + } + ", + ], + "version": 3, + } + `) + }) + + test('imported sass', async () => { + const css = await getStyleTagContentIncluding('.imported-sass ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.sass", + ], + "sourcesContent": Array [ + ".imported + &-sass + color: red + ", + ], + "version": 3, + } + `) + }) + + test('imported sass module', async () => { + const css = await getStyleTagContentIncluding('._imported-sass-module_') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.module.sass", + ], + "sourcesContent": Array [ + ".imported + &-sass-module + color: red + ", + ], + "version": 3, + } + `) + }) + + test('imported less', async () => { + const css = await getStyleTagContentIncluding('.imported-less ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.less", + ], + "sourcesContent": Array [ + ".imported { + &-less { + color: @color; + } + } + ", + ], + "version": 3, + } + `) + }) + + test('imported stylus', async () => { + const css = await getStyleTagContentIncluding('.imported-stylus ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE,cAAM", + "sources": Array [ + "/root/imported.styl", + ], + "sourcesContent": Array [ + ".imported + &-stylus + color blue-red-mixed + ", + ], + "version": 3, + } + `) + }) +} else { + test('this file only includes test for serve', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/css-sourcemap/be-imported.css b/packages/playground/css-sourcemap/be-imported.css new file mode 100644 index 00000000000000..a29e5f77e3cb5d --- /dev/null +++ b/packages/playground/css-sourcemap/be-imported.css @@ -0,0 +1,3 @@ +.be-imported { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported-with-import.css b/packages/playground/css-sourcemap/imported-with-import.css new file mode 100644 index 00000000000000..6a1ed3c3772698 --- /dev/null +++ b/packages/playground/css-sourcemap/imported-with-import.css @@ -0,0 +1,5 @@ +@import '@/be-imported.css'; + +.imported-with-import { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported.css b/packages/playground/css-sourcemap/imported.css new file mode 100644 index 00000000000000..9c9b32924962dc --- /dev/null +++ b/packages/playground/css-sourcemap/imported.css @@ -0,0 +1,3 @@ +.imported { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported.less b/packages/playground/css-sourcemap/imported.less new file mode 100644 index 00000000000000..e71b15eb102441 --- /dev/null +++ b/packages/playground/css-sourcemap/imported.less @@ -0,0 +1,5 @@ +.imported { + &-less { + color: @color; + } +} diff --git a/packages/playground/css-sourcemap/imported.module.sass b/packages/playground/css-sourcemap/imported.module.sass new file mode 100644 index 00000000000000..448a5e7e31f75a --- /dev/null +++ b/packages/playground/css-sourcemap/imported.module.sass @@ -0,0 +1,3 @@ +.imported + &-sass-module + color: red diff --git a/packages/playground/css-sourcemap/imported.sass b/packages/playground/css-sourcemap/imported.sass new file mode 100644 index 00000000000000..06fa634d5dd4e9 --- /dev/null +++ b/packages/playground/css-sourcemap/imported.sass @@ -0,0 +1,3 @@ +.imported + &-sass + color: red diff --git a/packages/playground/css-sourcemap/imported.styl b/packages/playground/css-sourcemap/imported.styl new file mode 100644 index 00000000000000..83c7cf517acf4d --- /dev/null +++ b/packages/playground/css-sourcemap/imported.styl @@ -0,0 +1,3 @@ +.imported + &-stylus + color blue-red-mixed diff --git a/packages/playground/css-sourcemap/index.html b/packages/playground/css-sourcemap/index.html new file mode 100644 index 00000000000000..2fedceb8f2be44 --- /dev/null +++ b/packages/playground/css-sourcemap/index.html @@ -0,0 +1,35 @@ + + + +
+

CSS Sourcemap

+ +

<linked>: no import

+

<linked>: with import

+ +

<imported>: no import

+

<imported>: with import

+ +

<imported sass>

+

<imported sass> with module

+ +

<imported less> with string additionalData

+ +

<imported stylus>

+
+ + diff --git a/packages/playground/css-sourcemap/linked-with-import.css b/packages/playground/css-sourcemap/linked-with-import.css new file mode 100644 index 00000000000000..6f65d92441fa49 --- /dev/null +++ b/packages/playground/css-sourcemap/linked-with-import.css @@ -0,0 +1,5 @@ +@import '@/be-imported.css'; + +.linked-with-import { + color: red; +} diff --git a/packages/playground/css-sourcemap/linked.css b/packages/playground/css-sourcemap/linked.css new file mode 100644 index 00000000000000..e3b67c83872ac0 --- /dev/null +++ b/packages/playground/css-sourcemap/linked.css @@ -0,0 +1,3 @@ +.linked { + color: red; +} diff --git a/packages/playground/css-sourcemap/package.json b/packages/playground/css-sourcemap/package.json new file mode 100644 index 00000000000000..c29f18d4dee0d7 --- /dev/null +++ b/packages/playground/css-sourcemap/package.json @@ -0,0 +1,18 @@ +{ + "name": "test-css-sourcemap", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + }, + "devDependencies": { + "convert-source-map": "^1.8.0", + "less": "^4.1.2", + "magic-string": "^0.25.7", + "sass": "^1.43.4", + "stylus": "^0.55.0" + } +} diff --git a/packages/playground/css-sourcemap/vite.config.js b/packages/playground/css-sourcemap/vite.config.js new file mode 100644 index 00000000000000..2e70a4a0894406 --- /dev/null +++ b/packages/playground/css-sourcemap/vite.config.js @@ -0,0 +1,40 @@ +const MagicString = require('magic-string') + +/** + * @type {import('vite').UserConfig} + */ +module.exports = { + resolve: { + alias: { + '@': __dirname + } + }, + css: { + preprocessorOptions: { + less: { + additionalData: '@color: red;' + }, + styl: { + additionalData: (content, filename) => { + const ms = new MagicString(content, { filename }) + + const willBeReplaced = 'blue-red-mixed' + const start = content.indexOf(willBeReplaced) + ms.overwrite(start, start + willBeReplaced.length, 'purple') + + const map = ms.generateMap({ hires: true }) + map.file = filename + map.sources = [filename] + + return { + content: ms.toString(), + map + } + } + } + } + }, + build: { + sourcemap: true + } +} diff --git a/packages/playground/vue-sourcemap/Css.vue b/packages/playground/vue-sourcemap/Css.vue new file mode 100644 index 00000000000000..19668de8d33965 --- /dev/null +++ b/packages/playground/vue-sourcemap/Css.vue @@ -0,0 +1,23 @@ + + + + + + + diff --git a/packages/playground/vue-sourcemap/Less.vue b/packages/playground/vue-sourcemap/Less.vue new file mode 100644 index 00000000000000..f12a3e55f2111c --- /dev/null +++ b/packages/playground/vue-sourcemap/Less.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/playground/vue-sourcemap/Main.vue b/packages/playground/vue-sourcemap/Main.vue new file mode 100644 index 00000000000000..04ddf50071ccb3 --- /dev/null +++ b/packages/playground/vue-sourcemap/Main.vue @@ -0,0 +1,16 @@ + + + diff --git a/packages/playground/vue-sourcemap/Sass.vue b/packages/playground/vue-sourcemap/Sass.vue new file mode 100644 index 00000000000000..0fded031a52c72 --- /dev/null +++ b/packages/playground/vue-sourcemap/Sass.vue @@ -0,0 +1,8 @@ + + + diff --git a/packages/playground/vue-sourcemap/SassWithImport.vue b/packages/playground/vue-sourcemap/SassWithImport.vue new file mode 100644 index 00000000000000..7a00420a00bb3a --- /dev/null +++ b/packages/playground/vue-sourcemap/SassWithImport.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/playground/vue-sourcemap/__tests__/build.spec.ts b/packages/playground/vue-sourcemap/__tests__/build.spec.ts new file mode 100644 index 00000000000000..e36c1f52d2c1f8 --- /dev/null +++ b/packages/playground/vue-sourcemap/__tests__/build.spec.ts @@ -0,0 +1,13 @@ +import { isBuild } from 'testUtils' + +if (isBuild) { + test('should not output sourcemap warning (#4939)', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch('Sourcemap is likely to be incorrect') + }) + }) +} else { + test('this file only includes test for build', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/vue-sourcemap/__tests__/serve.spec.ts b/packages/playground/vue-sourcemap/__tests__/serve.spec.ts new file mode 100644 index 00000000000000..193b0afb9ba73f --- /dev/null +++ b/packages/playground/vue-sourcemap/__tests__/serve.spec.ts @@ -0,0 +1,283 @@ +import { fromComment } from 'convert-source-map' +import { normalizePath } from 'vite' +import { isBuild, testDir } from 'testUtils' + +if (!isBuild) { + const root = normalizePath(testDir) + + const getStyleTagContentIncluding = async (content: string) => { + const styles = await page.$$('style') + for (const style of styles) { + const text = await style.textContent() + if (text.includes(content)) { + return text + } + } + throw new Error('Not found') + } + + const extractSourcemap = (content: string) => { + const lines = content.trim().split('\n') + return fromComment(lines[lines.length - 1]).toObject() + } + + const formatSourcemapForSnapshot = (map: any) => { + const m = { ...map } + delete m.file + delete m.names + m.sources = m.sources.map((source) => source.replace(root, '/root')) + return m + } + + test('css', async () => { + const css = await getStyleTagContentIncluding('.css ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": ";AAOA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/Css.vue", + ], + "sourcesContent": Array [ + " + + + + + + + ", + ], + "version": 3, + } + `) + }) + + test('css module', async () => { + const css = await getStyleTagContentIncluding('._css-module_') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": ";AAaA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/Css.vue", + ], + "sourcesContent": Array [ + " + + + + + + + ", + ], + "version": 3, + } + `) + }) + + test('css scoped', async () => { + const css = await getStyleTagContentIncluding('.css-scoped[data-v-') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": ";AAmBA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/Css.vue", + ], + "sourcesContent": Array [ + " + + + + + + + ", + ], + "version": 3, + } + `) + }) + + test('sass', async () => { + const css = await getStyleTagContentIncluding('.sass ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAKA;EACE", + "sources": Array [ + "/root/Sass.vue", + ], + "sourcesContent": Array [ + " + + + ", + ], + "version": 3, + } + `) + }) + + test('sass with import', async () => { + const css = await getStyleTagContentIncluding('.sass-with-import ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA;EACE;;ACOF;EACE", + "sources": Array [ + "/root/sassWithImportImported.sass", + "/root/SassWithImport.vue", + ], + "sourcesContent": Array [ + ".sass-with-import-imported + color: red + ", + " + + + ", + ], + "version": 3, + } + `) + }) + + test('less with additionalData', async () => { + const css = await getStyleTagContentIncluding('.less ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAKA;EACE", + "sources": Array [ + "/root/Less.vue", + ], + "sourcesContent": Array [ + " + + + ", + ], + "version": 3, + } + `) + }) + + test('src imported', async () => { + const css = await getStyleTagContentIncluding('.src-import[data-v-') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/src-import/src-import.css", + ], + "sourcesContent": Array [ + ".src-import { + color: red; + } + ", + ], + "version": 3, + } + `) + }) + + test('src imported sass', async () => { + const css = await getStyleTagContentIncluding('.src-import-sass[data-v-') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAAA;EACE;;ACCF;EACE", + "sources": Array [ + "/root/src-import/src-import-imported.sass", + "/root/src-import/src-import.sass", + ], + "sourcesContent": Array [ + ".src-import-sass-imported + color: red + ", + "@import './src-import-imported' + + .src-import-sass + color: red + ", + ], + "version": 3, + } + `) + }) +} else { + test('this file only includes test for serve', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/vue-sourcemap/index.html b/packages/playground/vue-sourcemap/index.html new file mode 100644 index 00000000000000..57f325518a2c25 --- /dev/null +++ b/packages/playground/vue-sourcemap/index.html @@ -0,0 +1,7 @@ +
+ diff --git a/packages/playground/vue-sourcemap/package.json b/packages/playground/vue-sourcemap/package.json new file mode 100644 index 00000000000000..5672b5e3d9d57d --- /dev/null +++ b/packages/playground/vue-sourcemap/package.json @@ -0,0 +1,20 @@ +{ + "name": "test-vue-sourcemap", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + }, + "devDependencies": { + "@vitejs/plugin-vue": "workspace:*", + "convert-source-map": "^1.8.0", + "less": "^4.1.2", + "sass": "^1.43.4" + }, + "dependencies": { + "vue": "^3.2.31" + } +} diff --git a/packages/playground/vue-sourcemap/sassWithImportImported.sass b/packages/playground/vue-sourcemap/sassWithImportImported.sass new file mode 100644 index 00000000000000..8092b37048cbdd --- /dev/null +++ b/packages/playground/vue-sourcemap/sassWithImportImported.sass @@ -0,0 +1,2 @@ +.sass-with-import-imported + color: red diff --git a/packages/playground/vue-sourcemap/src-import/SrcImport.vue b/packages/playground/vue-sourcemap/src-import/SrcImport.vue new file mode 100644 index 00000000000000..406c6a6b45382d --- /dev/null +++ b/packages/playground/vue-sourcemap/src-import/SrcImport.vue @@ -0,0 +1,8 @@ + + + + diff --git a/packages/playground/vue-sourcemap/src-import/src-import-imported.sass b/packages/playground/vue-sourcemap/src-import/src-import-imported.sass new file mode 100644 index 00000000000000..2ed87d933e58a6 --- /dev/null +++ b/packages/playground/vue-sourcemap/src-import/src-import-imported.sass @@ -0,0 +1,2 @@ +.src-import-sass-imported + color: red diff --git a/packages/playground/vue-sourcemap/src-import/src-import.css b/packages/playground/vue-sourcemap/src-import/src-import.css new file mode 100644 index 00000000000000..da61ff0fb6cb27 --- /dev/null +++ b/packages/playground/vue-sourcemap/src-import/src-import.css @@ -0,0 +1,3 @@ +.src-import { + color: red; +} diff --git a/packages/playground/vue-sourcemap/src-import/src-import.sass b/packages/playground/vue-sourcemap/src-import/src-import.sass new file mode 100644 index 00000000000000..c7e0314fda541c --- /dev/null +++ b/packages/playground/vue-sourcemap/src-import/src-import.sass @@ -0,0 +1,4 @@ +@import './src-import-imported' + +.src-import-sass + color: red diff --git a/packages/playground/vue-sourcemap/vite.config.js b/packages/playground/vue-sourcemap/vite.config.js new file mode 100644 index 00000000000000..045410259fe590 --- /dev/null +++ b/packages/playground/vue-sourcemap/vite.config.js @@ -0,0 +1,18 @@ +const vuePlugin = require('@vitejs/plugin-vue') + +/** + * @type {import('vite').UserConfig} + */ +module.exports = { + css: { + preprocessorOptions: { + less: { + additionalData: '@color: red;' + } + } + }, + plugins: [vuePlugin()], + build: { + sourcemap: true + } +} diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index fb1bce3cd61132..28a15996363285 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -235,7 +235,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { descriptor, Number(query.index), options, - this + this, + filename ) } } diff --git a/packages/plugin-vue/src/style.ts b/packages/plugin-vue/src/style.ts index ad9d981412f52d..c124f3ec744e4f 100644 --- a/packages/plugin-vue/src/style.ts +++ b/packages/plugin-vue/src/style.ts @@ -1,6 +1,8 @@ import type { SFCDescriptor } from 'vue/compiler-sfc' -import type { TransformPluginContext } from 'rollup' +import type { ExistingRawSourceMap, TransformPluginContext } from 'rollup' import type { ResolvedOptions } from '.' +import type { RawSourceMap } from 'source-map' +import { formatPostcssSourceMap } from 'vite' // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export async function transformStyle( @@ -8,7 +10,8 @@ export async function transformStyle( descriptor: SFCDescriptor, index: number, options: ResolvedOptions, - pluginContext: TransformPluginContext + pluginContext: TransformPluginContext, + filename: string ) { const block = descriptor.styles[index] // vite already handles pre-processors and CSS module so this is only @@ -19,7 +22,14 @@ export async function transformStyle( id: `data-v-${descriptor.id}`, isProd: options.isProduction, source: code, - scoped: block.scoped + scoped: block.scoped, + postcssOptions: { + map: { + from: filename, + inline: false, + annotation: false + } + } }) if (result.errors.length) { @@ -36,8 +46,17 @@ export async function transformStyle( return null } + const map = result.map + ? formatPostcssSourceMap( + // version property of result.map is declared as string + // but actually it is a number + result.map as Omit as ExistingRawSourceMap, + filename + ) + : ({ mappings: '' } as any) + return { code: result.code, - map: result.map || ({ mappings: '' } as any) + map: map } } diff --git a/packages/vite/package.json b/packages/vite/package.json index d6952c40cace43..a46be406815e37 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -109,6 +109,7 @@ "resolve.exports": "^1.1.0", "rollup-plugin-license": "^2.6.1", "sirv": "^2.0.2", + "source-map-js": "^1.0.2", "source-map-support": "^0.5.21", "strip-ansi": "^6.0.1", "terser": "^5.12.1", diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 99567735b21757..f0f217c9be03d3 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -5,6 +5,7 @@ export { build } from './build' export { optimizeDeps } from './optimizer' export { send } from './server/send' export { createLogger, printHttpServerUrls } from './logger' +export { formatPostcssSourceMap } from './plugins/css' export { transformWithEsbuild } from './plugins/esbuild' export { resolvePackageEntry } from './plugins/resolve' export { diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index d513c88420b6ff..db8e0a2679f74f 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -11,17 +11,19 @@ import { isObject, normalizePath, processSrcSet, - parseRequest + parseRequest, + combineSourcemaps } from '../utils' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import postcssrc from 'postcss-load-config' import type { + ExistingRawSourceMap, NormalizedOutputOptions, OutputChunk, RenderedChunk, RollupError, - SourceMap + SourceMapInput } from 'rollup' import { dataToEsm } from '@rollup/pluginutils' import colors from 'picocolors' @@ -44,6 +46,8 @@ import type { Alias } from 'types/alias' import type { ModuleNode } from '../server/moduleGraph' import { transform, formatMessages } from 'esbuild' import { addToHTMLProxyTransformResult } from './html' +import { injectSourcesContent, getCodeWithSourcemap } from '../server/sourcemap' +import type { RawSourceMap } from '@ampproject/remapping' // const debug = createDebugger('vite:css') @@ -178,7 +182,8 @@ export function cssPlugin(config: ResolvedConfig): Plugin { const { code: css, modules, - deps + deps, + map } = await compileCSS( id, raw, @@ -245,8 +250,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin { return { code: css, - // TODO CSS source map - map: { mappings: '' } + map } } } @@ -296,12 +300,17 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { if (inlined) { return `export default ${JSON.stringify(css)}` } + + const sourcemap = this.getCombinedSourcemap() + await injectSourcesContent(sourcemap, cleanUrl(id), config.logger) + const cssContent = getCodeWithSourcemap('css', css, sourcemap) + return [ `import { updateStyle as __vite__updateStyle, removeStyle as __vite__removeStyle } from ${JSON.stringify( path.posix.join(config.base, CLIENT_PUBLIC_PATH) )}`, `const __vite__id = ${JSON.stringify(id)}`, - `const __vite__css = ${JSON.stringify(css)}`, + `const __vite__css = ${JSON.stringify(cssContent)}`, `__vite__updateStyle(__vite__id, __vite__css)`, // css modules exports change on edit so it can't self accept `${ @@ -590,7 +599,7 @@ async function compileCSS( server?: ViteDevServer ): Promise<{ code: string - map?: SourceMap + map?: SourceMapInput ast?: Postcss.Result modules?: Record deps?: Set @@ -612,10 +621,10 @@ async function compileCSS( !needInlineImport && !hasUrl ) { - return { code } + return { code, map: null } } - let map: SourceMap | undefined + let preprocessorMap: ExistingRawSourceMap | undefined let modules: Record | undefined const deps = new Set() @@ -657,7 +666,12 @@ async function compileCSS( } code = preprocessResult.code - map = preprocessResult.map as SourceMap + preprocessorMap = combineSourcemapsIfExists( + opts.filename, + preprocessResult.map, + preprocessResult.additionalMap + ) + if (preprocessResult.deps) { preprocessResult.deps.forEach((dep) => { // sometimes sass registers the file itself as a dep @@ -733,7 +747,7 @@ async function compileCSS( if (!postcssPlugins.length) { return { code, - map + map: preprocessorMap } } @@ -742,12 +756,14 @@ async function compileCSS( .default(postcssPlugins) .process(code, { ...postcssOptions, - to: id, - from: id, + to: cleanUrl(id), + from: cleanUrl(id), map: { inline: false, annotation: false, - prev: map + sourcesContent: false + // when "prev: preprocessorMap", the result map may include duplicate filename in `postcssResult.map.sources` + // prev: preprocessorMap, } }) @@ -791,15 +807,61 @@ async function compileCSS( } } + const rawPostcssMap = postcssResult.map.toJSON() + + const postcssMap = formatPostcssSourceMap( + // version property of rawPostcssMap is declared as string + // but actually it is a number + rawPostcssMap as Omit as ExistingRawSourceMap, + cleanUrl(id) + ) + return { ast: postcssResult, code: postcssResult.css, - map: postcssResult.map as any, + map: combineSourcemapsIfExists(cleanUrl(id), postcssMap, preprocessorMap), modules, deps } } +export function formatPostcssSourceMap( + rawMap: ExistingRawSourceMap, + file: string +): ExistingRawSourceMap { + const inputFileDir = path.dirname(file) + const sources = rawMap.sources + // remove from sources, to prevent source map to be combined incorrectly + .filter((source) => source !== '') + .map((source) => { + const cleanSource = cleanUrl(decodeURIComponent(source)) + return normalizePath(path.resolve(inputFileDir, cleanSource)) + }) + + return { + file, + mappings: rawMap.mappings, + names: rawMap.names, + sources, + version: rawMap.version + } +} + +function combineSourcemapsIfExists( + filename: string, + map1: ExistingRawSourceMap | undefined, + map2: ExistingRawSourceMap | undefined +): ExistingRawSourceMap | undefined { + return map1 && map2 + ? (combineSourcemaps(filename, [ + // type of version property of ExistingRawSourceMap is number + // but it is always 3 + map1 as RawSourceMap, + map2 as RawSourceMap + ]) as ExistingRawSourceMap) + : map1 +} + interface PostCSSConfigResult { options: Postcss.ProcessOptions plugins: Postcss.Plugin[] @@ -998,9 +1060,18 @@ AtImportHoistPlugin.postcss = true // Preprocessor support. This logic is largely replicated from @vue/compiler-sfc +type PreprocessorAdditionalDataResult = + | string + | { content: string; map?: ExistingRawSourceMap } + type PreprocessorAdditionalData = | string - | ((source: string, filename: string) => string | Promise) + | (( + source: string, + filename: string + ) => + | PreprocessorAdditionalDataResult + | Promise) type StylePreprocessorOptions = { [key: string]: any @@ -1027,7 +1098,8 @@ type SassStylePreprocessor = ( export interface StylePreprocessorResults { code: string - map?: object + map?: ExistingRawSourceMap | undefined + additionalMap?: ExistingRawSourceMap | undefined errors: RollupError[] deps: string[] } @@ -1092,12 +1164,20 @@ const scss: SassStylePreprocessor = async ( : importer.push(options.importer) } + const { content: data, map: additionalMap } = await getSource( + source, + options.filename, + options.additionalData + ) const finalOptions: Sass.Options = { ...options, - data: await getSource(source, options.filename, options.additionalData), + data, file: options.filename, outFile: options.filename, - importer + importer, + sourceMap: true, + omitSourceMapUrl: true, + sourceMapRoot: path.dirname(options.filename) } try { @@ -1111,9 +1191,14 @@ const scss: SassStylePreprocessor = async ( }) }) const deps = result.stats.includedFiles + const map: ExistingRawSourceMap | undefined = result.map + ? JSON.parse(result.map.toString()) + : undefined return { code: result.css.toString(), + map, + additionalMap, errors: [], deps } @@ -1203,13 +1288,21 @@ const less: StylePreprocessor = async (source, root, options, resolvers) => { options.alias, resolvers ) - source = await getSource(source, options.filename, options.additionalData) + const { content, map: additionalMap } = await getSource( + source, + options.filename, + options.additionalData + ) let result: Less.RenderOutput | undefined try { - result = await nodeLess.render(source, { + result = await nodeLess.render(content, { ...options, - plugins: [viteResolverPlugin, ...(options.plugins || [])] + plugins: [viteResolverPlugin, ...(options.plugins || [])], + sourceMap: { + outputSourceFiles: true, + sourceMapFileInline: false + } }) } catch (e) { const error = e as Less.RenderError @@ -1222,8 +1315,14 @@ const less: StylePreprocessor = async (source, root, options, resolvers) => { } return { code: '', errors: [normalizedError], deps: [] } } + + const map: ExistingRawSourceMap = JSON.parse(result.map) + delete map.sourcesContent + return { code: result.css.toString(), + map, + additionalMap, deps: result.imports, errors: [] } @@ -1305,7 +1404,7 @@ const styl: StylePreprocessor = async (source, root, options) => { const nodeStylus = loadPreprocessor(PreprocessLang.stylus, root) // Get source with preprocessor options.additionalData. Make sure a new line separator // is added to avoid any render error, as added stylus content may not have semi-colon separators - source = await getSource( + const { content, map: additionalMap } = await getSource( source, options.filename, options.additionalData, @@ -1317,32 +1416,77 @@ const styl: StylePreprocessor = async (source, root, options) => { path.resolve(dep) ) try { - const ref = nodeStylus(source, options) - - // if (map) ref.set('sourcemap', { inline: false, comment: false }) + const ref = nodeStylus(content, options) + ref.set('sourcemap', { + comment: false, + inline: false, + basePath: root + }) const result = ref.render() // Concat imports deps with computed deps const deps = [...ref.deps(), ...importsDeps] - return { code: result, errors: [], deps } + // @ts-expect-error sourcemap exists + const map: ExistingRawSourceMap = ref.sourcemap + + return { + code: result, + map: formatStylusSourceMap(map, root), + additionalMap, + errors: [], + deps + } } catch (e) { return { code: '', errors: [e], deps: [] } } } -function getSource( +function formatStylusSourceMap( + mapBefore: ExistingRawSourceMap, + root: string +): ExistingRawSourceMap { + const map = { ...mapBefore } + + const resolveFromRoot = (p: string) => normalizePath(path.resolve(root, p)) + + if (map.file) { + map.file = resolveFromRoot(map.file) + } + map.sources = map.sources.map(resolveFromRoot) + + return map +} + +async function getSource( source: string, filename: string, additionalData?: PreprocessorAdditionalData, sep: string = '' -): string | Promise { - if (!additionalData) return source +): Promise<{ content: string; map?: ExistingRawSourceMap }> { + if (!additionalData) return { content: source } + if (typeof additionalData === 'function') { - return additionalData(source, filename) + const newContent = await additionalData(source, filename) + if (typeof newContent === 'string') { + return { content: newContent } + } + return newContent + } + + const ms = new MagicString(source) + ms.appendLeft(0, sep) + ms.appendLeft(0, additionalData) + + const map = ms.generateMap({ hires: true }) + map.file = filename + map.sources = [filename] + + return { + content: ms.toString(), + map } - return additionalData + sep + source } const preProcessors = Object.freeze({ diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 2f9c171de5d2e5..2aab3e03097c99 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -51,7 +51,7 @@ import type { import * as acorn from 'acorn' import type { RawSourceMap } from '@ampproject/remapping' import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping' -import { combineSourcemaps } from '../utils' +import { cleanUrl, combineSourcemaps } from '../utils' import MagicString from 'magic-string' import type { FSWatcher } from 'chokidar' import { @@ -419,7 +419,7 @@ export async function createPluginContainer( if (!combinedMap) { combinedMap = m as SourceMap } else { - combinedMap = combineSourcemaps(this.filename, [ + combinedMap = combineSourcemaps(cleanUrl(this.filename), [ { ...(m as RawSourceMap), sourcesContent: combinedMap.sourcesContent diff --git a/packages/vite/src/node/server/send.ts b/packages/vite/src/node/server/send.ts index 58c63e4c9f39f2..a52d17d17458ba 100644 --- a/packages/vite/src/node/server/send.ts +++ b/packages/vite/src/node/server/send.ts @@ -1,8 +1,7 @@ import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'http' import getEtag from 'etag' import type { SourceMap } from 'rollup' - -const isDebug = process.env.DEBUG +import { getCodeWithSourcemap } from './sourcemap' const alias: Record = { js: 'application/javascript', @@ -54,25 +53,12 @@ export function send( // inject source map reference if (map && map.mappings) { - if (isDebug) { - content += `\n/*${JSON.stringify(map, null, 2).replace( - /\*\//g, - '*\\/' - )}*/\n` + if (type === 'js' || type === 'css') { + content = getCodeWithSourcemap(type, content.toString(), map) } - content += genSourceMapString(map) } res.statusCode = 200 res.end(content) return } - -function genSourceMapString(map: SourceMap | string | undefined) { - if (typeof map !== 'string') { - map = JSON.stringify(map) - } - return `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from( - map - ).toString('base64')}` -} diff --git a/packages/vite/src/node/server/sourcemap.ts b/packages/vite/src/node/server/sourcemap.ts index c6fc3b1f2579bf..68684a3c2d6f2a 100644 --- a/packages/vite/src/node/server/sourcemap.ts +++ b/packages/vite/src/node/server/sourcemap.ts @@ -2,6 +2,7 @@ import path from 'path' import { promises as fs } from 'fs' import type { Logger } from '../logger' import { createDebugger } from '../utils' +import type { SourceMap } from 'rollup' const isDebug = !!process.env.DEBUG const debug = createDebugger('vite:sourcemap', { @@ -57,3 +58,28 @@ export async function injectSourcesContent( isDebug && debug(`Missing sources:\n ` + missingSources.join(`\n `)) } } + +function genSourceMapUrl(map: SourceMap | string | undefined) { + if (typeof map !== 'string') { + map = JSON.stringify(map) + } + return `data:application/json;base64,${Buffer.from(map).toString('base64')}` +} + +export function getCodeWithSourcemap( + type: 'js' | 'css', + code: string, + map: SourceMap | null +) { + if (isDebug) { + code += `\n/*${JSON.stringify(map, null, 2).replace(/\*\//g, '*\\/')}*/\n` + } + + if (type === 'js') { + code += `\n//# sourceMappingURL=${genSourceMapUrl(map ?? undefined)}` + } else if (type === 'css') { + code += `\n/*# sourceMappingURL=${genSourceMapUrl(map ?? undefined)} */` + } + + return code +} diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index cca8df5750ec49..4e0f230fd7edfd 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -575,6 +575,26 @@ export async function processSrcSet( }, '') } +function escapeToLinuxLikePath(path: string) { + if (/^[A-Z]:/.test(path)) { + return path.replace(/^([A-Z]):\//, '/windows/$1/') + } + if (/^\/[^/]/.test(path)) { + return `/linux${path}` + } + return path +} + +function unescapeToLinuxLikePath(path: string) { + if (path.startsWith('/linux/')) { + return path.slice('/linux'.length) + } + if (path.startsWith('/windows/')) { + return path.replace(/^\/windows\/([A-Z])\//, '$1:/') + } + return path +} + // based on https://github.com/sveltejs/svelte/blob/abf11bb02b2afbd3e4cac509a0f70e318c306364/src/compiler/utils/mapped_code.ts#L221 const nullSourceMap: RawSourceMap = { names: [], @@ -593,6 +613,18 @@ export function combineSourcemaps( return { ...nullSourceMap } } + // hack for parse broken with normalized absolute paths on windows (C:/path/to/something). + // escape them to linux like paths + sourcemapList.forEach((sourcemap) => { + sourcemap.sources = sourcemap.sources.map((source) => + source ? escapeToLinuxLikePath(source) : null + ) + if (sourcemap.sourceRoot) { + sourcemap.sourceRoot = escapeToLinuxLikePath(sourcemap.sourceRoot) + } + }) + const escapedFilename = escapeToLinuxLikePath(filename) + // We don't declare type here so we can convert/fake/map as RawSourceMap let map //: SourceMap let mapIndex = 1 @@ -604,10 +636,10 @@ export function combineSourcemaps( map = remapping( sourcemapList[0], function loader(sourcefile) { - if (sourcefile === filename && sourcemapList[mapIndex]) { + if (sourcefile === escapedFilename && sourcemapList[mapIndex]) { return sourcemapList[mapIndex++] } else { - return { ...nullSourceMap } + return null } }, true @@ -617,6 +649,12 @@ export function combineSourcemaps( delete map.file } + // unescape the previous hack + map.sources = map.sources.map((source) => + source ? unescapeToLinuxLikePath(source) : source + ) + map.file = filename + return map as RawSourceMap } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8246238955fc1..5cbee016154079 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,6 +150,20 @@ importers: packages/playground/css-codesplit-cjs: specifiers: {} + packages/playground/css-sourcemap: + specifiers: + convert-source-map: ^1.8.0 + less: ^4.1.2 + magic-string: ^0.25.7 + sass: ^1.43.4 + stylus: ^0.55.0 + devDependencies: + convert-source-map: 1.8.0 + less: 4.1.2 + magic-string: 0.25.7 + sass: 1.45.1 + stylus: 0.55.0 + packages/playground/css/css-dep: specifiers: {} @@ -686,6 +700,21 @@ importers: devDependencies: '@vitejs/plugin-vue': link:../../plugin-vue + packages/playground/vue-sourcemap: + specifiers: + '@vitejs/plugin-vue': workspace:* + convert-source-map: ^1.8.0 + less: ^4.1.2 + sass: ^1.43.4 + vue: ^3.2.31 + dependencies: + vue: 3.2.31 + devDependencies: + '@vitejs/plugin-vue': link:../../plugin-vue + convert-source-map: 1.8.0 + less: 4.1.2 + sass: 1.45.1 + packages/playground/wasm: specifiers: {} @@ -829,6 +858,7 @@ importers: rollup: ^2.59.0 rollup-plugin-license: ^2.6.1 sirv: ^2.0.2 + source-map-js: ^1.0.2 source-map-support: ^0.5.21 strip-ansi: ^6.0.1 terser: ^5.12.1 @@ -901,6 +931,7 @@ importers: resolve.exports: 1.1.0 rollup-plugin-license: 2.6.1_rollup@2.62.0 sirv: 2.0.2 + source-map-js: 1.0.2 source-map-support: 0.5.21 strip-ansi: 6.0.1 terser: 5.12.1 @@ -2148,9 +2179,9 @@ packages: chalk: 4.1.2 dev: true - /@jridgewell/resolve-uri/3.0.3: - resolution: {integrity: sha512-fuIOnc81C5iRNevb/XPiM8Khp9bVjreydRQ37rt0C/dY0PAW1DRvEM3WrKX/5rStS5lbgwS0FCgqSndh9tvK5w==} - engines: {node: '>=10.0.0'} + /@jridgewell/resolve-uri/3.0.5: + resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==} + engines: {node: '>=6.0.0'} /@jridgewell/sourcemap-codec/1.4.10: resolution: {integrity: sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==} @@ -2158,7 +2189,7 @@ packages: /@jridgewell/trace-mapping/0.3.2: resolution: {integrity: sha512-9KzzH4kMjA2XmBRHfqG2/Vtl7s92l6uNDd0wW7frDE+EUvQFGqNXhWp0UGJjSkt3v2AYjzOZn1QO9XaTNJIt1Q==} dependencies: - '@jridgewell/resolve-uri': 3.0.3 + '@jridgewell/resolve-uri': 3.0.5 '@jridgewell/sourcemap-codec': 1.4.10 /@jridgewell/trace-mapping/0.3.4: @@ -2830,6 +2861,15 @@ packages: source-map: 0.6.1 dev: true + /@vue/compiler-core/3.2.31: + resolution: {integrity: sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==} + dependencies: + '@babel/parser': 7.17.0 + '@vue/shared': 3.2.31 + estree-walker: 2.0.2 + source-map: 0.6.1 + dev: false + /@vue/compiler-dom/3.2.26: resolution: {integrity: sha512-smBfaOW6mQDxcT3p9TKT6mE22vjxjJL50GFVJiI0chXYGU/xzC05QRGrW3HHVuJrmLTLx5zBhsZ2dIATERbarg==} dependencies: @@ -2843,6 +2883,13 @@ packages: '@vue/shared': 3.2.31 dev: true + /@vue/compiler-dom/3.2.31: + resolution: {integrity: sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==} + dependencies: + '@vue/compiler-core': 3.2.31 + '@vue/shared': 3.2.31 + dev: false + /@vue/compiler-sfc/3.2.26: resolution: {integrity: sha512-ePpnfktV90UcLdsDQUh2JdiTuhV0Skv2iYXxfNMOK/F3Q+2BO0AulcVcfoksOpTJGmhhfosWfMyEaEf0UaWpIw==} dependencies: @@ -2872,6 +2919,21 @@ packages: source-map: 0.6.1 dev: true + /@vue/compiler-sfc/3.2.31: + resolution: {integrity: sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==} + dependencies: + '@babel/parser': 7.17.0 + '@vue/compiler-core': 3.2.31 + '@vue/compiler-dom': 3.2.31 + '@vue/compiler-ssr': 3.2.31 + '@vue/reactivity-transform': 3.2.31 + '@vue/shared': 3.2.31 + estree-walker: 2.0.2 + magic-string: 0.25.7 + postcss: 8.4.6 + source-map: 0.6.1 + dev: false + /@vue/compiler-ssr/3.2.26: resolution: {integrity: sha512-2mywLX0ODc4Zn8qBoA2PDCsLEZfpUGZcyoFRLSOjyGGK6wDy2/5kyDOWtf0S0UvtoyVq95OTSGIALjZ4k2q/ag==} dependencies: @@ -2885,6 +2947,13 @@ packages: '@vue/shared': 3.2.31 dev: true + /@vue/compiler-ssr/3.2.31: + resolution: {integrity: sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==} + dependencies: + '@vue/compiler-dom': 3.2.31 + '@vue/shared': 3.2.31 + dev: false + /@vue/devtools-api/6.0.0-beta.21.1: resolution: {integrity: sha512-FqC4s3pm35qGVeXRGOjTsRzlkJjrBLriDS9YXbflHLsfA9FrcKzIyWnLXoNm+/7930E8rRakXuAc2QkC50swAw==} dev: false @@ -2908,6 +2977,16 @@ packages: magic-string: 0.25.7 dev: true + /@vue/reactivity-transform/3.2.31: + resolution: {integrity: sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==} + dependencies: + '@babel/parser': 7.17.0 + '@vue/compiler-core': 3.2.31 + '@vue/shared': 3.2.31 + estree-walker: 2.0.2 + magic-string: 0.25.7 + dev: false + /@vue/reactivity/3.2.26: resolution: {integrity: sha512-h38bxCZLW6oFJVDlCcAiUKFnXI8xP8d+eO0pcDxx+7dQfSPje2AO6M9S9QO6MrxQB7fGP0DH0dYQ8ksf6hrXKQ==} dependencies: @@ -2919,6 +2998,12 @@ packages: '@vue/shared': 3.2.31 dev: true + /@vue/reactivity/3.2.31: + resolution: {integrity: sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==} + dependencies: + '@vue/shared': 3.2.31 + dev: false + /@vue/runtime-core/3.2.26: resolution: {integrity: sha512-BcYi7qZ9Nn+CJDJrHQ6Zsmxei2hDW0L6AB4vPvUQGBm2fZyC0GXd/4nVbyA2ubmuhctD5RbYY8L+5GUJszv9mQ==} dependencies: @@ -2932,6 +3017,13 @@ packages: '@vue/shared': 3.2.31 dev: true + /@vue/runtime-core/3.2.31: + resolution: {integrity: sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==} + dependencies: + '@vue/reactivity': 3.2.31 + '@vue/shared': 3.2.31 + dev: false + /@vue/runtime-dom/3.2.26: resolution: {integrity: sha512-dY56UIiZI+gjc4e8JQBwAifljyexfVCkIAu/WX8snh8vSOt/gMSEGwPRcl2UpYpBYeyExV8WCbgvwWRNt9cHhQ==} dependencies: @@ -2947,6 +3039,14 @@ packages: csstype: 2.6.19 dev: true + /@vue/runtime-dom/3.2.31: + resolution: {integrity: sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==} + dependencies: + '@vue/runtime-core': 3.2.31 + '@vue/shared': 3.2.31 + csstype: 2.6.19 + dev: false + /@vue/server-renderer/3.2.26_vue@3.2.26: resolution: {integrity: sha512-Jp5SggDUvvUYSBIvYEhy76t4nr1vapY/FIFloWmQzn7UxqaHrrBpbxrqPcTrSgGrcaglj0VBp22BKJNre4aA1w==} peerDependencies: @@ -2966,6 +3066,16 @@ packages: vue: 3.2.31 dev: true + /@vue/server-renderer/3.2.31_vue@3.2.31: + resolution: {integrity: sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==} + peerDependencies: + vue: 3.2.31 + dependencies: + '@vue/compiler-ssr': 3.2.31 + '@vue/shared': 3.2.31 + vue: 3.2.31 + dev: false + /@vue/shared/3.2.26: resolution: {integrity: sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA==} @@ -2973,6 +3083,10 @@ packages: resolution: {integrity: sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==} dev: true + /@vue/shared/3.2.31: + resolution: {integrity: sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==} + dev: false + /@wessberg/stringutil/1.0.19: resolution: {integrity: sha512-9AZHVXWlpN8Cn9k5BC/O0Dzb9E9xfEMXzYrNunwvkUTvuK7xgQPVRZpLo+jWCOZ5r8oBa8NIrHuPEu1hzbb6bg==} engines: {node: '>=8.0.0'} @@ -5046,6 +5160,19 @@ packages: peerDependenciesMeta: debug: optional: true + dev: false + + /follow-redirects/1.14.6_debug@4.3.3: + resolution: {integrity: sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dependencies: + debug: 4.3.3 + dev: true /form-data/3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} @@ -5446,7 +5573,7 @@ packages: engines: {node: '>=8.0.0'} dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.14.6 + follow-redirects: 1.14.6_debug@4.3.3 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -6808,6 +6935,7 @@ packages: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} hasBin: true + requiresBuild: true dev: true /mime/3.0.0: @@ -8234,9 +8362,9 @@ packages: engines: {node: '>=8.9.0'} hasBin: true dependencies: - chokidar: 3.5.2 + chokidar: 3.5.3 immutable: 4.0.0 - source-map-js: 1.0.1 + source-map-js: 1.0.2 dev: true /sax/1.2.4: @@ -8448,17 +8576,13 @@ packages: smart-buffer: 4.2.0 dev: true - /source-map-js/1.0.1: - resolution: {integrity: sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==} - engines: {node: '>=0.10.0'} - dev: true - /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} /source-map-resolve/0.6.0: resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated dependencies: atob: 2.1.2 decode-uri-component: 0.2.0 @@ -9335,6 +9459,16 @@ packages: '@vue/shared': 3.2.31 dev: true + /vue/3.2.31: + resolution: {integrity: sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==} + dependencies: + '@vue/compiler-dom': 3.2.31 + '@vue/compiler-sfc': 3.2.31 + '@vue/runtime-dom': 3.2.31 + '@vue/server-renderer': 3.2.31_vue@3.2.31 + '@vue/shared': 3.2.31 + dev: false + /vuex/4.0.2_vue@3.2.26: resolution: {integrity: sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==} peerDependencies: diff --git a/scripts/jestPerTestSetup.ts b/scripts/jestPerTestSetup.ts index 9c15edf9f059bf..312d254a3b11d2 100644 --- a/scripts/jestPerTestSetup.ts +++ b/scripts/jestPerTestSetup.ts @@ -114,6 +114,8 @@ beforeAll(async () => { customLogger: createInMemoryLogger(serverLogs) } + setupConsoleWarnCollector(serverLogs) + global.serverLogs = serverLogs if (!isBuildTest) { @@ -263,3 +265,11 @@ function createInMemoryLogger(logs: string[]): Logger { return logger } + +function setupConsoleWarnCollector(logs: string[]) { + const warn = console.warn + console.warn = (...args) => { + serverLogs.push(args.join(' ')) + return warn.call(console, ...args) + } +}