Skip to content

Commit 38a655f

Browse files
authored
feat: css sourcemap support during dev (#7173)
1 parent 68d76c9 commit 38a655f

40 files changed

+1210
-70
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { isBuild } from 'testUtils'
2+
3+
if (isBuild) {
4+
test('should not output sourcemap warning (#4939)', () => {
5+
serverLogs.forEach((log) => {
6+
expect(log).not.toMatch('Sourcemap is likely to be incorrect')
7+
})
8+
})
9+
} else {
10+
test('this file only includes test for build', () => {
11+
expect(true).toBe(true)
12+
})
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { fromComment } from 'convert-source-map'
2+
import { URL } from 'url'
3+
import { normalizePath } from 'vite'
4+
import { isBuild, testDir } from 'testUtils'
5+
6+
if (!isBuild) {
7+
const root = normalizePath(testDir)
8+
9+
const getStyleTagContentIncluding = async (content: string) => {
10+
const styles = await page.$$('style')
11+
for (const style of styles) {
12+
const text = await style.textContent()
13+
if (text.includes(content)) {
14+
return text
15+
}
16+
}
17+
throw new Error('Not found')
18+
}
19+
20+
const extractSourcemap = (content: string) => {
21+
const lines = content.trim().split('\n')
22+
return fromComment(lines[lines.length - 1]).toObject()
23+
}
24+
25+
const formatSourcemapForSnapshot = (map: any) => {
26+
const m = { ...map }
27+
delete m.file
28+
delete m.names
29+
m.sources = m.sources.map((source) => source.replace(root, '/root'))
30+
return m
31+
}
32+
33+
test('linked css', async () => {
34+
const res = await page.request.get(
35+
new URL('./linked.css', page.url()).href,
36+
{
37+
headers: {
38+
accept: 'text/css'
39+
}
40+
}
41+
)
42+
const css = await res.text()
43+
const lines = css.split('\n')
44+
expect(lines[lines.length - 1].includes('/*')).toBe(false) // expect no sourcemap
45+
})
46+
47+
test('linked css with import', async () => {
48+
const res = await page.request.get(
49+
new URL('./linked-with-import.css', page.url()).href,
50+
{
51+
headers: {
52+
accept: 'text/css'
53+
}
54+
}
55+
)
56+
const css = await res.text()
57+
const map = extractSourcemap(css)
58+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
59+
Object {
60+
"mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ",
61+
"sources": Array [
62+
"/root/be-imported.css",
63+
"/root/linked-with-import.css",
64+
],
65+
"sourcesContent": Array [
66+
".be-imported {
67+
color: red;
68+
}
69+
",
70+
"@import '@/be-imported.css';
71+
72+
.linked-with-import {
73+
color: red;
74+
}
75+
",
76+
],
77+
"version": 3,
78+
}
79+
`)
80+
})
81+
82+
test('imported css', async () => {
83+
const css = await getStyleTagContentIncluding('.imported ')
84+
const map = extractSourcemap(css)
85+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
86+
Object {
87+
"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;",
88+
"sources": Array [
89+
"/root/imported.css",
90+
],
91+
"sourcesContent": Array [
92+
".imported {
93+
color: red;
94+
}
95+
",
96+
],
97+
"version": 3,
98+
}
99+
`)
100+
})
101+
102+
test('imported css with import', async () => {
103+
const css = await getStyleTagContentIncluding('.imported-with-import ')
104+
const map = extractSourcemap(css)
105+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
106+
Object {
107+
"mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ",
108+
"sources": Array [
109+
"/root/be-imported.css",
110+
"/root/imported-with-import.css",
111+
],
112+
"sourcesContent": Array [
113+
".be-imported {
114+
color: red;
115+
}
116+
",
117+
"@import '@/be-imported.css';
118+
119+
.imported-with-import {
120+
color: red;
121+
}
122+
",
123+
],
124+
"version": 3,
125+
}
126+
`)
127+
})
128+
129+
test('imported sass', async () => {
130+
const css = await getStyleTagContentIncluding('.imported-sass ')
131+
const map = extractSourcemap(css)
132+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
133+
Object {
134+
"mappings": "AACE;EACE",
135+
"sources": Array [
136+
"/root/imported.sass",
137+
],
138+
"sourcesContent": Array [
139+
".imported
140+
&-sass
141+
color: red
142+
",
143+
],
144+
"version": 3,
145+
}
146+
`)
147+
})
148+
149+
test('imported sass module', async () => {
150+
const css = await getStyleTagContentIncluding('._imported-sass-module_')
151+
const map = extractSourcemap(css)
152+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
153+
Object {
154+
"mappings": "AACE;EACE",
155+
"sources": Array [
156+
"/root/imported.module.sass",
157+
],
158+
"sourcesContent": Array [
159+
".imported
160+
&-sass-module
161+
color: red
162+
",
163+
],
164+
"version": 3,
165+
}
166+
`)
167+
})
168+
169+
test('imported less', async () => {
170+
const css = await getStyleTagContentIncluding('.imported-less ')
171+
const map = extractSourcemap(css)
172+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
173+
Object {
174+
"mappings": "AACE;EACE",
175+
"sources": Array [
176+
"/root/imported.less",
177+
],
178+
"sourcesContent": Array [
179+
".imported {
180+
&-less {
181+
color: @color;
182+
}
183+
}
184+
",
185+
],
186+
"version": 3,
187+
}
188+
`)
189+
})
190+
191+
test('imported stylus', async () => {
192+
const css = await getStyleTagContentIncluding('.imported-stylus ')
193+
const map = extractSourcemap(css)
194+
expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
195+
Object {
196+
"mappings": "AACE;EACE,cAAM",
197+
"sources": Array [
198+
"/root/imported.styl",
199+
],
200+
"sourcesContent": Array [
201+
".imported
202+
&-stylus
203+
color blue-red-mixed
204+
",
205+
],
206+
"version": 3,
207+
}
208+
`)
209+
})
210+
} else {
211+
test('this file only includes test for serve', () => {
212+
expect(true).toBe(true)
213+
})
214+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.be-imported {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import '@/be-imported.css';
2+
3+
.imported-with-import {
4+
color: red;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.imported {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.imported {
2+
&-less {
3+
color: @color;
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.imported
2+
&-sass-module
3+
color: red
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.imported
2+
&-sass
3+
color: red
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.imported
2+
&-stylus
3+
color blue-red-mixed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<link rel="stylesheet" href="./linked.css" />
2+
<link rel="stylesheet" href="./linked-with-import.css" />
3+
4+
<div class="wrapper">
5+
<h1>CSS Sourcemap</h1>
6+
7+
<p class="linked">&lt;linked&gt;: no import</p>
8+
<p class="linked-with-import">&lt;linked&gt;: with import</p>
9+
10+
<p class="imported">&lt;imported&gt;: no import</p>
11+
<p class="imported-with-import">&lt;imported&gt;: with import</p>
12+
13+
<p class="imported-sass">&lt;imported sass&gt;</p>
14+
<p class="imported-sass-module">&lt;imported sass&gt; with module</p>
15+
16+
<p class="imported-less">&lt;imported less&gt; with string additionalData</p>
17+
18+
<p class="imported-stylus">&lt;imported stylus&gt;</p>
19+
</div>
20+
21+
<script type="module">
22+
import './imported.css'
23+
import './imported-with-import.css'
24+
25+
import './imported.sass'
26+
import sassModule from './imported.module.sass'
27+
28+
document
29+
.querySelector('.imported-sass-module')
30+
.classList.add(sassModule['imported-sass-module'])
31+
32+
import './imported.less'
33+
34+
import './imported.styl'
35+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import '@/be-imported.css';
2+
3+
.linked-with-import {
4+
color: red;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.linked {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "test-css-sourcemap",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"debug": "node --inspect-brk ../../vite/bin/vite",
9+
"preview": "vite preview"
10+
},
11+
"devDependencies": {
12+
"convert-source-map": "^1.8.0",
13+
"less": "^4.1.2",
14+
"magic-string": "^0.25.7",
15+
"sass": "^1.43.4",
16+
"stylus": "^0.55.0"
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const MagicString = require('magic-string')
2+
3+
/**
4+
* @type {import('vite').UserConfig}
5+
*/
6+
module.exports = {
7+
resolve: {
8+
alias: {
9+
'@': __dirname
10+
}
11+
},
12+
css: {
13+
preprocessorOptions: {
14+
less: {
15+
additionalData: '@color: red;'
16+
},
17+
styl: {
18+
additionalData: (content, filename) => {
19+
const ms = new MagicString(content, { filename })
20+
21+
const willBeReplaced = 'blue-red-mixed'
22+
const start = content.indexOf(willBeReplaced)
23+
ms.overwrite(start, start + willBeReplaced.length, 'purple')
24+
25+
const map = ms.generateMap({ hires: true })
26+
map.file = filename
27+
map.sources = [filename]
28+
29+
return {
30+
content: ms.toString(),
31+
map
32+
}
33+
}
34+
}
35+
}
36+
},
37+
build: {
38+
sourcemap: true
39+
}
40+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<p class="css">&lt;css&gt;</p>
3+
<p :class="$style['css-module']">&lt;css&gt; module</p>
4+
<p class="css-scoped">&lt;css&gt; scoped</p>
5+
</template>
6+
7+
<style>
8+
.css {
9+
color: red;
10+
}
11+
</style>
12+
13+
<style module>
14+
.css-module {
15+
color: red;
16+
}
17+
</style>
18+
19+
<style scoped>
20+
.css-scoped {
21+
color: red;
22+
}
23+
</style>

0 commit comments

Comments
 (0)