diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts
index caf0a42a8f..d3dd8d0a0b 100644
--- a/src/compiler/compile/Component.ts
+++ b/src/compiler/compile/Component.ts
@@ -29,8 +29,11 @@ import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red';
import { is_reserved_keyword } from './utils/reserved_keywords';
-import remapping from '@ampproject/remapping';
+import { combine_sourcemaps, sourcemap_add_tostring_tourl, combine_sourcemaps_map_stats } from '../utils/string_with_sourcemap';
import Element from './nodes/Element';
+import { RawSourceMap } from '@ampproject/remapping/dist/types/types';
+import { encode as encode_mappings, decode as decode_mappings } from 'sourcemap-codec';
+
interface ComponentOptions {
namespace?: string;
@@ -318,12 +321,19 @@ export default class Component {
css = compile_options.customElement
? { code: null, map: null }
- : result.css;
+ : result.css; // css.map.mappings are decoded
js = print(program, {
sourceMapSource: compile_options.filename
});
+ // TODO remove workaround
+ // js.map.mappings should be decoded
+ // https://github.com/Rich-Harris/code-red/issues/50
+ if (js.map && typeof (js.map as any).mappings == 'string') {
+ (js.map as any).mappings = decode_mappings((js.map as any).mappings);
+ }
+
js.map.sources = [
compile_options.filename ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) : null
];
@@ -332,34 +342,59 @@ export default class Component {
this.source
];
+ // combine sourcemaps
+ const map_stats: combine_sourcemaps_map_stats = {
+ sourcemapWarnLoss: 0, // segment loss is usually high, so we ignore
+ sourcemapEncodedWarn: true // TODO config
+ // property `result` is set by combine_sourcemaps
+ };
+
if (compile_options.sourcemap) {
if (js.map) {
- const pre_remap_sources = js.map.sources;
- js.map = remapping([js.map, compile_options.sourcemap], () => null, true);
- // remapper can remove our source if it isn't used (no segments map back to it). It is still handy to have a source
- // so we add it back
- if (js.map.sources && js.map.sources.length == 0) {
- js.map.sources = pre_remap_sources;
+ js.map = combine_sourcemaps(
+ this.file,
+ [
+ js.map, // idx 1: internal
+ compile_options.sourcemap // idx 0: external: svelte.preprocess, etc
+ ],
+ map_stats
+ ) as RawSourceMap;
+ sourcemap_add_tostring_tourl(js.map);
+ if (map_stats.result && map_stats.result.maps_encoded && map_stats.result.maps_encoded.length > 0) {
+ console.log('warning. svelte.compile received encoded script sourcemaps (index '+
+ map_stats.result.maps_encoded.join(', ')+'). '+
+ 'this is slow. make your sourcemap-generators return decoded mappings '+
+ 'or disable this warning with svelte.compile(_, _, { sourcemapEncodedWarn: false })'
+ );
}
- Object.defineProperties(js.map, {
- toString: {
- enumerable: false,
- value: function toString() {
- return JSON.stringify(this);
- }
- },
- toUrl: {
- enumerable: false,
- value: function toUrl() {
- return 'data:application/json;charset=utf-8;base64,' + btoa(this.toString());
- }
- }
- });
}
if (css.map) {
- css.map = remapping([css.map, compile_options.sourcemap], () => null, true);
+ css.map = combine_sourcemaps(
+ this.file,
+ [
+ css.map, // idx 1: internal
+ compile_options.sourcemap // idx 0: external: svelte.preprocess, etc
+ ]
+ ) as RawSourceMap;
+ sourcemap_add_tostring_tourl(css.map);
+ if (map_stats.result && map_stats.result.maps_encoded && map_stats.result.maps_encoded.length > 0) {
+ console.log('warning. svelte.compile received encoded style sourcemaps (index '+
+ map_stats.result.maps_encoded.join(', ')+'). '+
+ 'this is slow. make your sourcemap-generators return decoded mappings '+
+ 'or disable this warning with svelte.compile(_, _, { sourcemapEncodedWarn: false })'
+ );
+ }
}
}
+
+ // encode mappings only once, after all sourcemaps are combined
+ if (js.map && typeof(js.map.mappings) == 'object') {
+ (js.map as RawSourceMap).mappings = encode_mappings(js.map.mappings);
+ }
+ if (css.map && typeof(css.map.mappings) == 'object') {
+ (css.map as RawSourceMap).mappings = encode_mappings(css.map.mappings);
+ }
+
}
return {
diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts
index dc464d7df8..9d18d64274 100644
--- a/src/compiler/compile/css/Stylesheet.ts
+++ b/src/compiler/compile/css/Stylesheet.ts
@@ -410,7 +410,7 @@ export default class Stylesheet {
return {
code: code.toString(),
- map: code.generateMap({
+ map: code.generateDecodedMap({
includeContent: true,
source: this.filename,
file
diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts
index ab4ee904e8..523141b57b 100644
--- a/src/compiler/compile/render_dom/index.ts
+++ b/src/compiler/compile/render_dom/index.ts
@@ -30,8 +30,12 @@ export default function dom(
}
const css = component.stylesheet.render(options.filename, !options.customElement);
+
+ // TODO fix css.map.toUrl - stylesheet.render returns decoded mappings, map.toUrl needs encoded mappings
+ // TODO use combined css.map? see compile/Component.ts
const styles = component.stylesheet.has_styles && options.dev
- ? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
+ //? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
+ ? `${css.code}\n/*# sourceMappingURL=TODO_FIXME */`
: css.code;
const add_css = component.get_unique_name('add_css');
@@ -467,12 +471,15 @@ export default function dom(
}
if (options.customElement) {
+ // TODO use combined css.map? see compile/Component.ts
+ // TODO css.map.toUrl needs encoded mappings
+ // ${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
const declaration = b`
class ${name} extends @SvelteElement {
constructor(options) {
super();
- ${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
+ ${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
@init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts
index ff45abd78b..62e2d18314 100644
--- a/src/compiler/compile/render_ssr/index.ts
+++ b/src/compiler/compile/render_ssr/index.ts
@@ -137,6 +137,7 @@ export default function ssr(
main
].filter(Boolean);
+ // TODO use combined css.map? see compile/Component.ts
const js = b`
${css.code ? b`
const #css = {
diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts
index 7843cf8938..263f25d7d7 100644
--- a/src/compiler/interfaces.ts
+++ b/src/compiler/interfaces.ts
@@ -1,5 +1,7 @@
import { Node, Program } from "estree";
-import { SourceMap } from 'magic-string';
+
+// eslint-disable-next-line import/named
+import { DecodedSourceMap } from 'magic-string';
interface BaseNode {
start: number;
@@ -166,5 +168,5 @@ export interface Var {
export interface CssResult {
code: string;
- map: SourceMap;
+ map: DecodedSourceMap;
}
diff --git a/src/compiler/preprocess/index.ts b/src/compiler/preprocess/index.ts
index c27c658919..30f4d08f1b 100644
--- a/src/compiler/preprocess/index.ts
+++ b/src/compiler/preprocess/index.ts
@@ -1,8 +1,7 @@
-import remapping from '@ampproject/remapping';
-import { SourceMapInput, SourceMapLoader, RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
+import { SourceMapInput, RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { decode as decode_mappings } from 'sourcemap-codec';
import { getLocator } from 'locate-character';
-import { StringWithSourcemap, sourcemap_add_offset } from '../utils/string_with_sourcemap';
+import { StringWithSourcemap, sourcemap_add_offset, combine_sourcemaps, combine_sourcemaps_map_stats } from '../utils/string_with_sourcemap';
export interface Processed {
code: string;
@@ -113,10 +112,17 @@ function get_replacement(
export default async function preprocess(
source: string,
preprocessor: PreprocessorGroup | PreprocessorGroup[],
- options?: { filename?: string }
+ options?: {
+ filename?: string,
+ sourcemapWarnLoss?: number, // default 0.5
+ sourcemapEncodedWarn?: boolean // default true
+ }
) {
// @ts-ignore todo: doublecheck
const filename = (options && options.filename) || preprocessor.filename; // legacy
+ const sourcemapWarnLoss = (options && options.sourcemapWarnLoss != undefined) ? options.sourcemapWarnLoss : 0.5;
+ const sourcemapEncodedWarn = (options && options.sourcemapEncodedWarn != undefined) ? options.sourcemapEncodedWarn : true;
+
const dependencies = [];
const preprocessors = preprocessor
@@ -130,7 +136,9 @@ export default async function preprocess(
// sourcemap_list is sorted in reverse order from last map (index 0) to first map (index -1)
// so we use sourcemap_list.unshift() to add new maps
// https://github.com/ampproject/remapping#multiple-transformations-of-a-file
- const sourcemap_list: (DecodedSourceMap | RawSourceMap)[] = [];
+ const sourcemap_list: Array = [];
+
+ // TODO keep track: what preprocessor generated what sourcemap? to make debugging easier = detect low-resolution sourcemaps in fn combine_mappings
for (const fn of markup) {
@@ -150,6 +158,8 @@ export default async function preprocess(
);
}
+ // TODO run script and style in parallel
+
for (const fn of script) {
const get_location = getLocator(source);
const res = await replace_async(
@@ -216,37 +226,41 @@ export default async function preprocess(
sourcemap_list.unshift(res.map);
}
- let map: RawSourceMap;
- let map_idx = 0;
- try {
- map =
- sourcemap_list.length == 0
- ? null
- : sourcemap_list.slice(0, -1).find(m => m.sources.length !== 1) === undefined
- ? remapping( // use array interface
- sourcemap_list,
- () => null,
- true // skip optional field `sourcesContent`
- )
- : remapping( // use loader interface
- sourcemap_list[map_idx++],
- function loader(sourcefile) {
- if (sourcefile === filename)
- return sourcemap_list[map_idx++] || null;
- // bundle file = branch node
- else return null; // source file = leaf node
- } as SourceMapLoader
- );
- } catch (error) {
- throw { ...error, message: error.message +
- '\n\ncould not combine sourcemaps:\n' +
- JSON.stringify(sourcemap_list.map(m => {
- return { ...m, mappings: JSON.stringify(m.mappings).slice(0, 100)+' ....'};
- }), null, 2)
- };
+ const map_stats: combine_sourcemaps_map_stats = {
+ sourcemapWarnLoss,
+ sourcemapEncodedWarn
+ // property `result` is set by combine_sourcemaps
+ };
+
+ const map: DecodedSourceMap = combine_sourcemaps(
+ filename,
+ sourcemap_list,
+ map_stats,
+ true // explicitly decode mappings
+ // TODO remove this, when `remapping` allows to return decoded mappings, so we skip the unnecessary encode + decode steps
+ ) as DecodedSourceMap;
+
+ // TODO better than console.log?
+
+ if (map_stats.result && map_stats.result.segments_lost) {
+ const { segment_loss_per_map, segments_per_map } = map_stats.result;
+ console.log('warning. svelte.preprocess seems to receive low-resolution sourcemaps. '+
+ 'relative segment loss per combine_sourcemaps step: '+
+ segment_loss_per_map.map(f => f.toFixed(2)).join(' -> ')+
+ '. absolute number of segments per sourcemap: '+
+ segments_per_map.join(' -> ')+
+ '. make your preprocessors return high-resolution sourcemaps '+
+ 'or increase the tolerated loss with svelte.preprocess(_, _, { sourcemapWarnLoss: 0.8 })'
+ );
}
- if (map && !map.file) delete map.file; // skip optional field `file`
+ if (map_stats.result && map_stats.result.maps_encoded && map_stats.result.maps_encoded.length > 0) {
+ console.log('warning. svelte.preprocess received encoded sourcemaps (index '+
+ map_stats.result.maps_encoded.join(', ')+'). '+
+ 'this is slow. make your sourcemap-generators return decoded mappings '+
+ 'or disable this warning with svelte.preprocess(_, _, { sourcemapEncodedWarn: false })'
+ );
+ }
return {
// TODO return separated output, in future version where svelte.compile supports it:
diff --git a/src/compiler/utils/string_with_sourcemap.ts b/src/compiler/utils/string_with_sourcemap.ts
index 9dcc51afe3..0e08436092 100644
--- a/src/compiler/utils/string_with_sourcemap.ts
+++ b/src/compiler/utils/string_with_sourcemap.ts
@@ -1,4 +1,6 @@
-import { DecodedSourceMap, SourceMapSegment } from '@ampproject/remapping/dist/types/types';
+import { DecodedSourceMap, RawSourceMap, SourceMapSegment, SourceMapLoader } from '@ampproject/remapping/dist/types/types';
+import remapping from '@ampproject/remapping';
+import { decode as decode_mappings } from 'sourcemap-codec';
type SourceLocation = {
line: number;
@@ -180,3 +182,152 @@ export class StringWithSourcemap {
return new StringWithSourcemap(source, map);
}
}
+
+export type combine_sourcemaps_map_stats = {
+ sourcemapEncodedWarn?: boolean,
+ sourcemapWarnLoss?: number,
+ result?: {
+ maps_encoded?: number[],
+ segments_lost?: boolean
+ segment_loss_per_map?: number[],
+ segments_per_map?: number[],
+ }
+};
+
+export function combine_sourcemaps(
+ filename: string,
+ sourcemap_list: Array,
+ map_stats?: combine_sourcemaps_map_stats,
+ do_decode_mappings?: boolean
+): (RawSourceMap | DecodedSourceMap) {
+ if (sourcemap_list.length == 0) return null;
+
+ if (map_stats) {
+ map_stats.result = {};
+ const { result } = map_stats;
+
+ const last_map_idx = sourcemap_list.length - 1;
+
+ // TODO allow to set options per preprocessor -> extend preprocessor config object
+ // some sourcemap-generators produce ultra-high-resolution sourcemaps (1 token = 1 character), so a high segment loss can be tolerable
+
+ // sourcemapEncodedWarn: show warning
+ // if preprocessors return sourcemaps with encoded mappings
+ // we need decoded mappings, so that is a waste of time
+
+ if (map_stats.sourcemapEncodedWarn) {
+ result.maps_encoded = [];
+
+ for (let map_idx = last_map_idx; map_idx >= 0; map_idx--) {
+ const map = sourcemap_list[map_idx];
+ if (typeof(map) == 'string') {
+ sourcemap_list[map_idx] = JSON.parse(map);
+ }
+ if (typeof(map.mappings) == 'string') {
+ result.maps_encoded.push(last_map_idx - map_idx); // chronological index
+ }
+ }
+ }
+
+ // sourcemapWarnLoss: show warning if source files were lost
+ // disable warning with `svelte.preprocess(_, _, { sourcemapWarnLoss: false })`
+ // value 1 : never warn
+ // value 0.8: seldom warn
+ // value 0.5: average warn
+ // value 0.2: often warn
+ // value 0 : nonsense -> never warn
+ // -Infinity <= loss <= 1 and 0 < sourcemapWarnLoss <= 1
+
+ if (map_stats.sourcemapWarnLoss) {
+
+ // guess if segments were lost because of lowres sourcemaps
+ // assert: typeof(result) == 'object'
+ result.segments_per_map = [];
+ result.segment_loss_per_map = [];
+ result.segments_lost = false;
+
+ let last_num_segments;
+
+ for (let map_idx = last_map_idx; map_idx >= 0; map_idx--) {
+
+ const map = sourcemap_list[map_idx];
+ if (typeof(map) == 'string') {
+ sourcemap_list[map_idx] = JSON.parse(map);
+ }
+ if (typeof(map.mappings) == 'string') {
+ // do this before remapping to avoid double decoding
+ // remapping does not mutate its input data
+ map.mappings = decode_mappings(map.mappings);
+ }
+ let num_segments = 0;
+ for (const line of map.mappings) {
+ num_segments += line.length;
+ }
+ // get relative loss, compared to last map
+ const loss = map_idx == last_map_idx
+ ? 0 : (last_num_segments - num_segments) / last_num_segments;
+ if (loss > map_stats.sourcemapWarnLoss) {
+ result.segments_lost = true;
+ }
+
+ // chronological index
+ result.segment_loss_per_map.push(loss);
+ result.segments_per_map.push(num_segments);
+
+ last_num_segments = num_segments;
+ }
+ }
+
+ }
+
+ let map_idx = 1;
+ const map: RawSourceMap =
+ sourcemap_list.slice(0, -1)
+ .find(m => m.sources.length !== 1) === undefined
+
+ ? remapping( // use array interface
+ // only the oldest sourcemap can have multiple sources
+ sourcemap_list,
+ () => null,
+ true // skip optional field `sourcesContent`
+ )
+
+ : remapping( // use loader interface
+ sourcemap_list[0], // last map
+ function loader(sourcefile) {
+ if (sourcefile === filename && sourcemap_list[map_idx]) {
+ return sourcemap_list[map_idx++]; // idx 1, 2, ...
+ // bundle file = branch node
+ }
+ else return null; // source file = leaf node
+ } as SourceMapLoader,
+ true
+ );
+
+ if (!map.file) delete map.file; // skip optional field `file`
+
+ if (do_decode_mappings) {
+ // explicitly decode mappings
+ // TODO remove this, when `remapping` allows to return decoded mappings, so we skip the unnecessary encode + decode steps
+ (map as unknown as DecodedSourceMap).mappings = decode_mappings(map.mappings);
+ }
+
+ return map;
+}
+
+export function sourcemap_add_tostring_tourl(map) {
+ Object.defineProperties(map, {
+ toString: {
+ enumerable: false,
+ value: function toString() {
+ return JSON.stringify(this);
+ }
+ },
+ toUrl: {
+ enumerable: false,
+ value: function toUrl() {
+ return 'data:application/json;charset=utf-8;base64,' + btoa(this.toString());
+ }
+ }
+ });
+}
diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.js
index ca0bdbfca3..b8ba4aaa2a 100644
--- a/test/sourcemaps/index.js
+++ b/test/sourcemaps/index.js
@@ -6,6 +6,7 @@ import { loadConfig, svelte } from "../helpers.js";
// https://github.com/mozilla/source-map/issues/400
import { SourceMapConsumer } from "source-map";
import { getLocator } from "locate-character";
+import { encode as encode_mappings, decode as decode_mappings } from 'sourcemap-codec';
describe("sourcemaps", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@@ -44,6 +45,13 @@ describe("sourcemaps", () => {
return test({ assert, input, preprocessed });
}
+ // preprocessed.map.mappings should be decoded
+ // to avoid unnecessary encode + decode steps
+ if (preprocessed.map) {
+ assert.equal(typeof preprocessed.map.mappings, 'object', 'preprocessed.map.mappings should be decoded');
+ assert.equal(Array.isArray(preprocessed.map.mappings), true, 'preprocessed.map.mappings should be decoded');
+ }
+
const { js, css } = svelte.compile(
preprocessed.code, {
filename: "input.svelte",
@@ -60,7 +68,11 @@ describe("sourcemaps", () => {
fs.writeFileSync(`${outputBase}.svelte`, preprocessed.code);
if (preprocessed.map) {
- fs.writeFileSync(`${outputBase}.svelte.map`, JSON.stringify(preprocessed.map, null, 2));
+ fs.writeFileSync(
+ `${outputBase}.svelte.map`,
+ // TODO encode mappings for output - svelte.preprocess returns decoded mappings
+ JSON.stringify(preprocessed.map, null, 2)
+ );
}
fs.writeFileSync(
`${outputBase}.js`,
@@ -92,26 +104,26 @@ describe("sourcemaps", () => {
);
};
+ // stupid workaround (unnecessary encode + decode steps)
+ // TODO find a SourceMapConsumer who also consumes decoded mappings
+ if (preprocessed.map) {
+ preprocessed.map.mappings = encode_mappings(preprocessed.map.mappings);
+ }
+
// use locate_1 with mapConsumer:
// lines are one-based, columns are zero-based
- if (preprocessed.map) {
- preprocessed.mapConsumer = await new SourceMapConsumer(preprocessed.map);
- preprocessed.locate = getLocator(preprocessed.code);
- preprocessed.locate_1 = getLocator(preprocessed.code, { offsetLine: 1 });
- }
+ preprocessed.mapConsumer = preprocessed.map && await new SourceMapConsumer(preprocessed.map);
+ preprocessed.locate = getLocator(preprocessed.code);
+ preprocessed.locate_1 = getLocator(preprocessed.code, { offsetLine: 1 });
- if (js.map) {
- js.mapConsumer = await new SourceMapConsumer(js.map);
- js.locate = getLocator(js.code);
- js.locate_1 = getLocator(js.code, { offsetLine: 1 });
- }
+ js.mapConsumer = js.map && await new SourceMapConsumer(js.map);
+ js.locate = getLocator(js.code);
+ js.locate_1 = getLocator(js.code, { offsetLine: 1 });
- if (css.map) {
- css.mapConsumer = await new SourceMapConsumer(css.map);
- css.locate = getLocator(css.code);
- css.locate_1 = getLocator(css.code, { offsetLine: 1 });
- }
+ css.mapConsumer = css.map && await new SourceMapConsumer(css.map);
+ css.locate = getLocator(css.code || '');
+ css.locate_1 = getLocator(css.code || '', { offsetLine: 1 });
test({ assert, input, preprocessed, js, css });
});
diff --git a/test/sourcemaps/samples/decoded-sourcemap/_config.js b/test/sourcemaps/samples/decoded-sourcemap/_config.js
index bc0db984b2..fc4d2a03c9 100644
--- a/test/sourcemaps/samples/decoded-sourcemap/_config.js
+++ b/test/sourcemaps/samples/decoded-sourcemap/_config.js
@@ -19,6 +19,9 @@ function result(src, filename) {
}
export default {
+
+ js_map_sources: [], // test component has no scripts
+
preprocess: {
markup: ({ content, filename }) => {
const src = new MagicString(content);
diff --git a/test/sourcemaps/samples/detect-lowres-sourcemaps/_config.js b/test/sourcemaps/samples/detect-lowres-sourcemaps/_config.js
new file mode 100644
index 0000000000..b11594dd85
--- /dev/null
+++ b/test/sourcemaps/samples/detect-lowres-sourcemaps/_config.js
@@ -0,0 +1,54 @@
+import MagicString from 'magic-string';
+
+// TODO move util fns to test index.js
+
+function result(filename, src, extraOptions = {}) {
+ return {
+ code: src.toString(),
+ map: src.generateDecodedMap({
+ source: filename,
+ hires: true,
+ includeContent: false,
+ ...extraOptions
+ })
+ };
+}
+
+function replace_all(src, search, replace) {
+ let idx = src.original.indexOf(search);
+ if (idx == -1) throw new Error('search not found in src');
+ do {
+ src.overwrite(idx, idx + search.length, replace);
+ } while ((idx = src.original.indexOf(search, idx + 1)) != -1);
+}
+
+function replace_first(src, search, replace) {
+ const idx = src.original.indexOf(search);
+ if (idx == -1) throw new Error('search not found in src');
+ src.overwrite(idx, idx + search.length, replace);
+}
+
+export default {
+
+ preprocess_options: {
+ sourcemapLossWarn: 0.9 // warn often
+ },
+
+ js_map_sources: [], // test component has no scripts
+
+ preprocess: [
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_all(src, 'replace_me', 'done_replace');
+ return result(filename, src, { hires: true });
+ } },
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_first(src, 'done_replace', 'version_3');
+ // return low-resolution sourcemap
+ // this should make previous mappings unreachable
+ return result(filename, src, { hires: false });
+ } }
+ ]
+
+};
diff --git a/test/sourcemaps/samples/detect-lowres-sourcemaps/input.svelte b/test/sourcemaps/samples/detect-lowres-sourcemaps/input.svelte
new file mode 100644
index 0000000000..2b3afd881b
--- /dev/null
+++ b/test/sourcemaps/samples/detect-lowres-sourcemaps/input.svelte
@@ -0,0 +1,10 @@
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
+replace_me
diff --git a/test/sourcemaps/samples/detect-lowres-sourcemaps/test.js b/test/sourcemaps/samples/detect-lowres-sourcemaps/test.js
new file mode 100644
index 0000000000..0f63efb358
--- /dev/null
+++ b/test/sourcemaps/samples/detect-lowres-sourcemaps/test.js
@@ -0,0 +1,10 @@
+export function test({ assert, preprocessed, js }) {
+
+ assert.equal(preprocessed.error, undefined);
+
+ // TODO can we automate this test?
+ // we need the output of console.log
+ // to test the warning message.
+ // or use a different method for warnings?
+
+}
diff --git a/test/sourcemaps/samples/preprocessed-markup/_config.js b/test/sourcemaps/samples/preprocessed-markup/_config.js
index f515cc6d12..cb3eb90e01 100644
--- a/test/sourcemaps/samples/preprocessed-markup/_config.js
+++ b/test/sourcemaps/samples/preprocessed-markup/_config.js
@@ -8,7 +8,7 @@ export default {
src.overwrite(idx, idx+"baritone".length, "bar");
return {
code: src.toString(),
- map: src.generateMap({
+ map: src.generateDecodedMap({
source: filename,
includeContent: false
})
diff --git a/test/sourcemaps/samples/preprocessed-multiple/_config.js b/test/sourcemaps/samples/preprocessed-multiple/_config.js
index c587a71bbd..d4c1e6cdbe 100644
--- a/test/sourcemaps/samples/preprocessed-multiple/_config.js
+++ b/test/sourcemaps/samples/preprocessed-multiple/_config.js
@@ -11,7 +11,7 @@ export default {
src.overwrite(css_idx, css_idx + "--bazitone".length, "--baz");
return {
code: src.toString(),
- map: src.generateMap({
+ map: src.generateDecodedMap({
source: filename,
hires: true,
includeContent: false
@@ -24,7 +24,7 @@ export default {
src.prependLeft(idx, " ");
return {
code: src.toString(),
- map: src.generateMap({
+ map: src.generateDecodedMap({
source: filename,
hires: true,
includeContent: false
@@ -37,7 +37,7 @@ export default {
src.prependLeft(idx, " ");
return {
code: src.toString(),
- map: src.generateMap({
+ map: src.generateDecodedMap({
source: filename,
hires: true,
includeContent: false
diff --git a/test/sourcemaps/samples/sourcemap-names/_config.js b/test/sourcemaps/samples/sourcemap-names/_config.js
index 96c9698132..35c7badb29 100644
--- a/test/sourcemaps/samples/sourcemap-names/_config.js
+++ b/test/sourcemaps/samples/sourcemap-names/_config.js
@@ -10,7 +10,7 @@ function replace(search, replace, content, src, options = { storeName: true }) {
function result(src, filename) {
return {
code: src.toString(),
- map: src.generateMap({
+ map: src.generateDecodedMap({
source: filename,
hires: true,
includeContent: false
diff --git a/test/sourcemaps/samples/sourcemap-sources/_config.js b/test/sourcemaps/samples/sourcemap-sources/_config.js
index 2c3e66c3b4..999fb20dfe 100644
--- a/test/sourcemaps/samples/sourcemap-sources/_config.js
+++ b/test/sourcemaps/samples/sourcemap-sources/_config.js
@@ -4,8 +4,8 @@ function add(bundle, filename, source) {
bundle.addSource({
filename,
content: new MagicString(source),
- separator: '\n',
- //separator: '', // ERROR. probably a bug in magic-string
+ separator: '\n'
+ //separator: '' // ERROR. probably a bug in magic-string
});
}
@@ -15,7 +15,7 @@ function result(bundle, filename) {
map: bundle.generateMap({
file: filename,
includeContent: false,
- hires: false
+ hires: true // required for remapping
})
};
}
@@ -26,7 +26,7 @@ export default {
'foo.js',
'bar.js',
'foo2.js',
- 'bar2.js',
+ 'bar2.js'
],
preprocess: [
{
@@ -34,8 +34,8 @@ export default {
const bundle = new Bundle();
add(bundle, filename, content);
- add(bundle, 'foo.js', 'var answer = 42;');
- add(bundle, 'bar.js', 'console.log(answer);');
+ add(bundle, 'foo.js', 'var answer = 42; // foo.js\n');
+ add(bundle, 'bar.js', 'console.log(answer); // bar.js\n');
return result(bundle, filename);
}
@@ -45,8 +45,8 @@ export default {
const bundle = new Bundle();
add(bundle, filename, content);
- add(bundle, 'foo2.js', 'var answer2 = 84;');
- add(bundle, 'bar2.js', 'console.log(answer2);');
+ add(bundle, 'foo2.js', 'var answer2 = 84; // foo2.js\n');
+ add(bundle, 'bar2.js', 'console.log(answer2); // bar2.js\n');
return result(bundle, filename);
}
diff --git a/test/sourcemaps/samples/sourcemap-sources/test.js b/test/sourcemaps/samples/sourcemap-sources/test.js
index 98fb64fc6a..78a4c80a17 100644
--- a/test/sourcemaps/samples/sourcemap-sources/test.js
+++ b/test/sourcemaps/samples/sourcemap-sources/test.js
@@ -5,12 +5,12 @@ export function test({ assert, preprocessed, js }) {
// sourcemap stores location only for 'answer = 42;'
// not for 'var answer = 42;'
[
- [js, 'foo.js', 'answer = 42;'],
- [js, 'bar.js', 'console.log(answer);'],
- [js, 'foo2.js', 'answer2 = 84;'],
- [js, 'bar2.js', 'console.log(answer2);'],
+ [js, 'foo.js', 'answer = 42;', 4],
+ [js, 'bar.js', 'console.log(answer);', 0],
+ [js, 'foo2.js', 'answer2 = 84;', 4],
+ [js, 'bar2.js', 'console.log(answer2);', 0]
]
- .forEach(([where, sourcefile, content]) => {
+ .forEach(([where, sourcefile, content, column]) => {
assert.deepEqual(
where.mapConsumer.originalPositionFor(
@@ -20,7 +20,7 @@ export function test({ assert, preprocessed, js }) {
source: sourcefile,
name: null,
line: 1,
- column: 0
+ column
},
`failed to locate "${content}" from "${sourcefile}"`
);
diff --git a/test/sourcemaps/samples/warn-on-encoded-mappings/_config.js b/test/sourcemaps/samples/warn-on-encoded-mappings/_config.js
new file mode 100644
index 0000000000..d33573e27d
--- /dev/null
+++ b/test/sourcemaps/samples/warn-on-encoded-mappings/_config.js
@@ -0,0 +1,58 @@
+import MagicString from 'magic-string';
+
+// TODO move util fns to test index.js
+
+function result(filename, src, options = {}) {
+ const map_fn = options.encodeMappings ? src.generateMap : src.generateDecodedMap;
+ delete options.encodeMappings;
+ return {
+ code: src.toString(),
+ map: map_fn.apply(src, [{
+ source: filename,
+ hires: true,
+ includeContent: false,
+ ...options
+ }])
+ };
+}
+
+function replace_all(src, search, replace) {
+ let idx = src.original.indexOf(search);
+ if (idx == -1) throw new Error('search not found in src');
+ do {
+ src.overwrite(idx, idx + search.length, replace);
+ } while ((idx = src.original.indexOf(search, idx + 1)) != -1);
+}
+
+export default {
+
+ js_map_sources: [], // test component has no scripts
+
+ preprocess: [
+ // preprocessor 0
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_all(src, 'replace_me', 'version_1');
+ return result(filename, src, { encodeMappings: true });
+ } },
+ // 1
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_all(src, 'version_1', 'version_2');
+ return result(filename, src);
+ } },
+ // 2
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_all(src, 'version_2', 'version_3');
+ return result(filename, src, { encodeMappings: true });
+ } },
+ // 3
+ { markup: ({ content, filename }) => {
+ const src = new MagicString(content);
+ replace_all(src, 'version_3', 'version_4');
+ return result(filename, src);
+ } }
+ ]
+
+};
diff --git a/test/sourcemaps/samples/warn-on-encoded-mappings/input.svelte b/test/sourcemaps/samples/warn-on-encoded-mappings/input.svelte
new file mode 100644
index 0000000000..ac8f9b30ab
--- /dev/null
+++ b/test/sourcemaps/samples/warn-on-encoded-mappings/input.svelte
@@ -0,0 +1,2 @@
+replace_me
+replace_me
diff --git a/test/sourcemaps/samples/warn-on-encoded-mappings/test.js b/test/sourcemaps/samples/warn-on-encoded-mappings/test.js
new file mode 100644
index 0000000000..9e25f21e55
--- /dev/null
+++ b/test/sourcemaps/samples/warn-on-encoded-mappings/test.js
@@ -0,0 +1,13 @@
+export function test({ assert, preprocessed, js }) {
+
+ assert.equal(preprocessed.error, undefined);
+
+ // TODO can we automate this test?
+ // we need the output of console.log
+ // to test the warning message.
+ // or use a different method for warnings?
+
+ // expected warning message:
+ // warning. svelte.preprocess received encoded sourcemaps (index 0, 2). [....]
+
+}