Skip to content

Commit e4f8218

Browse files
authored
Merge pull request #15 from rollup/css-sourcemaps
generate CSS sourcemaps
2 parents c0dc482 + 57d1483 commit e4f8218

File tree

8 files changed

+828
-107
lines changed

8 files changed

+828
-107
lines changed

package-lock.json

+627-101
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@
3131
"homepage": "https://github.com/rollup/rollup-plugin-svelte#README",
3232
"devDependencies": {
3333
"eslint": "^3.10.2",
34+
"locate-character": "^2.0.1",
3435
"mocha": "^3.1.2",
3536
"rollup": "^0.41.5",
3637
"rollup-plugin-buble": "^0.15.0",
37-
"rollup-watch": "^3.2.2"
38+
"rollup-watch": "^3.2.2",
39+
"sander": "^0.6.0",
40+
"source-map": "^0.5.6",
41+
"svelte": "^1.25.1"
3842
},
3943
"dependencies": {
4044
"require-relative": "^0.8.7",
4145
"rollup-pluginutils": "^2.0.1",
42-
"svelte": "^1.11.2"
46+
"sourcemap-codec": "^1.3.1"
4347
}
4448
}

src/index.js

+94-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from 'path';
33
import relative from 'require-relative';
44
import { compile } from 'svelte';
55
import { createFilter } from 'rollup-pluginutils';
6+
import { encode, decode } from 'sourcemap-codec';
67

78
function sanitize(input) {
89
return path
@@ -51,6 +52,59 @@ function exists(file) {
5152
}
5253
}
5354

55+
function mkdirp(dir) {
56+
const parent = path.dirname(dir);
57+
if (parent === dir) return;
58+
59+
mkdirp(parent);
60+
61+
try {
62+
fs.mkdirSync(dir);
63+
} catch (err) {
64+
if (err.code !== 'EEXIST') throw err;
65+
}
66+
}
67+
68+
class CssWriter {
69+
constructor (code, map) {
70+
this.code = code;
71+
this.map = {
72+
version: 3,
73+
file: null,
74+
sources: map.sources,
75+
sourcesContent: map.sourcesContent,
76+
names: [],
77+
mappings: map.mappings
78+
};
79+
}
80+
81+
write(dest, map) {
82+
dest = path.resolve(dest);
83+
mkdirp(path.dirname(dest));
84+
85+
const basename = path.basename(dest);
86+
87+
if (map !== false) {
88+
fs.writeFileSync(dest, `${this.code}\n/*# sourceMappingURL=${basename}.map */`);
89+
fs.writeFileSync(`${dest}.map`, JSON.stringify({
90+
version: 3,
91+
file: basename,
92+
sources: this.map.sources.map(source => path.relative(path.dirname(dest), source)),
93+
sourcesContent: this.map.sourcesContent,
94+
names: [],
95+
mappings: this.map.mappings
96+
}, null, ' '));
97+
} else {
98+
fs.writeFileSync(dest, this.code);
99+
}
100+
}
101+
102+
toString() {
103+
console.log('[DEPRECATION] As of rollup-plugin-svelte@3, the argument to the `css` function is an object, not a string — use `css.write(file)`. Consult the documentation for more information: https://github.com/rollup/rollup-plugin-svelte'); // eslint-disable-line no-console
104+
return this.code;
105+
}
106+
}
107+
54108
export default function svelte(options = {}) {
55109
const filter = createFilter(options.include, options.exclude);
56110

@@ -140,7 +194,12 @@ export default function svelte(options = {}) {
140194
})
141195
);
142196

143-
if (css) cssLookup.set(id, compiled.css);
197+
if (css) {
198+
cssLookup.set(id, {
199+
code: compiled.css,
200+
map: compiled.cssMap
201+
});
202+
}
144203

145204
return {
146205
code: compiled.code,
@@ -153,12 +212,43 @@ export default function svelte(options = {}) {
153212
// write out CSS file. TODO would be nice if there was a
154213
// a more idiomatic way to do this in Rollup
155214
let result = '';
215+
216+
const mappings = [];
217+
const sources = [];
218+
const sourcesContent = [];
219+
156220
for (let chunk of cssLookup.values()) {
157-
result += chunk || '';
221+
if (!chunk.code) continue;
222+
223+
result += chunk.code + '\n';
224+
225+
if (chunk.map) {
226+
const i = sources.length;
227+
sources.push(chunk.map.sources[0]);
228+
sourcesContent.push(chunk.map.sourcesContent[0]);
229+
230+
const decoded = decode(chunk.map.mappings);
231+
232+
if (i > 0) {
233+
decoded.forEach(line => {
234+
line.forEach(segment => {
235+
segment[1] = i;
236+
});
237+
});
238+
}
239+
240+
mappings.push(...decoded);
241+
}
158242
}
159243

160-
css(result);
244+
const writer = new CssWriter(result, {
245+
sources,
246+
sourcesContent,
247+
mappings: encode(mappings)
248+
});
249+
250+
css(writer);
161251
}
162252
}
163253
};
164-
}
254+
}

test/sourcemap-test/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<link rel='stylesheet' href='dist/bundle.css'>
2+
<body><script src='dist/bundle.js'></script></body>

test/sourcemap-test/src/Bar.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<p class='bar'>blue</p>
2+
3+
<style>
4+
.bar {
5+
color: blue;
6+
}
7+
</style>

test/sourcemap-test/src/Foo.html

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<p class='foo'>red</p>
2+
<Bar/>
3+
4+
<style>
5+
.foo {
6+
color: red;
7+
}
8+
</style>
9+
10+
<script>
11+
import Bar from './Bar.html';
12+
13+
export default {
14+
components: { Bar }
15+
};
16+
</script>

test/sourcemap-test/src/main.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Foo from './Foo.html';
2+
3+
new Foo({
4+
target: document.body
5+
});

test/test.js

+71
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
const path = require('path');
2+
const sander = require('sander');
23
const assert = require('assert');
4+
const rollup = require('rollup');
5+
const { SourceMapConsumer } = require('source-map');
6+
const { getLocator } = require('locate-character');
37

48
const plugin = require('../dist/rollup-plugin-svelte.cjs.js');
59

@@ -41,4 +45,71 @@ describe('rollup-plugin-svelte', () => {
4145
const compiled = transform('', 'test.html');
4246
assert.deepEqual(Object.keys(compiled), ['code', 'map']);
4347
});
48+
49+
it('generates a CSS sourcemap', () => {
50+
sander.rimrafSync('test/sourcemap-test/dist');
51+
sander.mkdirSync('test/sourcemap-test/dist');
52+
53+
return rollup.rollup({
54+
entry: 'test/sourcemap-test/src/main.js',
55+
plugins: [
56+
plugin({
57+
cascade: false,
58+
css: css => {
59+
css.write('test/sourcemap-test/dist/bundle.css');
60+
61+
const smc = new SourceMapConsumer(css.map);
62+
const locator = getLocator(css.code);
63+
64+
const generatedFooLoc = locator('.foo');
65+
const originalFooLoc = smc.originalPositionFor({
66+
line: generatedFooLoc.line + 1,
67+
column: generatedFooLoc.column
68+
});
69+
70+
assert.deepEqual(
71+
{
72+
source: originalFooLoc.source.replace(/\//g, path.sep),
73+
line: originalFooLoc.line,
74+
column: originalFooLoc.column,
75+
name: originalFooLoc.name
76+
},
77+
{
78+
source: path.resolve('test/sourcemap-test/src/Foo.html'),
79+
line: 5,
80+
column: 1,
81+
name: null
82+
}
83+
);
84+
85+
const generatedBarLoc = locator('.bar');
86+
const originalBarLoc = smc.originalPositionFor({
87+
line: generatedBarLoc.line + 1,
88+
column: generatedBarLoc.column
89+
});
90+
91+
assert.deepEqual(
92+
{
93+
source: originalBarLoc.source.replace(/\//g, path.sep),
94+
line: originalBarLoc.line,
95+
column: originalBarLoc.column,
96+
name: originalBarLoc.name
97+
},
98+
{
99+
source: path.resolve('test/sourcemap-test/src/Bar.html'),
100+
line: 4,
101+
column: 1,
102+
name: null
103+
}
104+
);
105+
}
106+
})
107+
]
108+
}).then(bundle => {
109+
return bundle.write({
110+
format: 'iife',
111+
dest: 'test/sourcemap-test/dist/bundle.js'
112+
});
113+
});
114+
});
44115
});

0 commit comments

Comments
 (0)