fix tests, use decoded mappings, show warnings

pull/5428/head
Milan Hauth 5 years ago
parent a0eb41f68d
commit 18003d6487

@ -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 check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red'; import { print, x, b } from 'code-red';
import { is_reserved_keyword } from './utils/reserved_keywords'; 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 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 { interface ComponentOptions {
namespace?: string; namespace?: string;
@ -318,12 +321,19 @@ export default class Component {
css = compile_options.customElement css = compile_options.customElement
? { code: null, map: null } ? { code: null, map: null }
: result.css; : result.css; // css.map.mappings are decoded
js = print(program, { js = print(program, {
sourceMapSource: compile_options.filename 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 = [ js.map.sources = [
compile_options.filename ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) : null compile_options.filename ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) : null
]; ];
@ -332,34 +342,59 @@ export default class Component {
this.source 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 (compile_options.sourcemap) {
if (js.map) { if (js.map) {
const pre_remap_sources = js.map.sources; js.map = combine_sourcemaps(
js.map = remapping([js.map, compile_options.sourcemap], () => null, true); this.file,
// 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 js.map, // idx 1: internal
if (js.map.sources && js.map.sources.length == 0) { compile_options.sourcemap // idx 0: external: svelte.preprocess, etc
js.map.sources = pre_remap_sources; ],
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) { 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 { return {

@ -410,7 +410,7 @@ export default class Stylesheet {
return { return {
code: code.toString(), code: code.toString(),
map: code.generateMap({ map: code.generateDecodedMap({
includeContent: true, includeContent: true,
source: this.filename, source: this.filename,
file file

@ -30,8 +30,12 @@ export default function dom(
} }
const css = component.stylesheet.render(options.filename, !options.customElement); 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 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; : css.code;
const add_css = component.get_unique_name('add_css'); const add_css = component.get_unique_name('add_css');
@ -467,12 +471,15 @@ export default function dom(
} }
if (options.customElement) { 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 = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
const declaration = b` const declaration = b`
class ${name} extends @SvelteElement { class ${name} extends @SvelteElement {
constructor(options) { constructor(options) {
super(); super();
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`} ${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=TODO_FIXME */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty}); @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});

@ -137,6 +137,7 @@ export default function ssr(
main main
].filter(Boolean); ].filter(Boolean);
// TODO use combined css.map? see compile/Component.ts
const js = b` const js = b`
${css.code ? b` ${css.code ? b`
const #css = { const #css = {

@ -1,5 +1,7 @@
import { Node, Program } from "estree"; import { Node, Program } from "estree";
import { SourceMap } from 'magic-string';
// eslint-disable-next-line import/named
import { DecodedSourceMap } from 'magic-string';
interface BaseNode { interface BaseNode {
start: number; start: number;
@ -166,5 +168,5 @@ export interface Var {
export interface CssResult { export interface CssResult {
code: string; code: string;
map: SourceMap; map: DecodedSourceMap;
} }

@ -1,8 +1,7 @@
import remapping from '@ampproject/remapping'; import { SourceMapInput, RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { SourceMapInput, SourceMapLoader, RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { decode as decode_mappings } from 'sourcemap-codec'; import { decode as decode_mappings } from 'sourcemap-codec';
import { getLocator } from 'locate-character'; 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 { export interface Processed {
code: string; code: string;
@ -113,10 +112,17 @@ function get_replacement(
export default async function preprocess( export default async function preprocess(
source: string, source: string,
preprocessor: PreprocessorGroup | PreprocessorGroup[], preprocessor: PreprocessorGroup | PreprocessorGroup[],
options?: { filename?: string } options?: {
filename?: string,
sourcemapWarnLoss?: number, // default 0.5
sourcemapEncodedWarn?: boolean // default true
}
) { ) {
// @ts-ignore todo: doublecheck // @ts-ignore todo: doublecheck
const filename = (options && options.filename) || preprocessor.filename; // legacy 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 dependencies = [];
const preprocessors = preprocessor 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) // 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 // so we use sourcemap_list.unshift() to add new maps
// https://github.com/ampproject/remapping#multiple-transformations-of-a-file // https://github.com/ampproject/remapping#multiple-transformations-of-a-file
const sourcemap_list: (DecodedSourceMap | RawSourceMap)[] = []; const sourcemap_list: Array<DecodedSourceMap | RawSourceMap> = [];
// 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) { 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) { for (const fn of script) {
const get_location = getLocator(source); const get_location = getLocator(source);
const res = await replace_async( const res = await replace_async(
@ -216,37 +226,41 @@ export default async function preprocess(
sourcemap_list.unshift(res.map); sourcemap_list.unshift(res.map);
} }
let map: RawSourceMap; const map_stats: combine_sourcemaps_map_stats = {
let map_idx = 0; sourcemapWarnLoss,
try { sourcemapEncodedWarn
map = // property `result` is set by combine_sourcemaps
sourcemap_list.length == 0 };
? null
: sourcemap_list.slice(0, -1).find(m => m.sources.length !== 1) === undefined const map: DecodedSourceMap = combine_sourcemaps(
? remapping( // use array interface filename,
sourcemap_list, sourcemap_list,
() => null, map_stats,
true // skip optional field `sourcesContent` true // explicitly decode mappings
) // TODO remove this, when `remapping` allows to return decoded mappings, so we skip the unnecessary encode + decode steps
: remapping( // use loader interface ) as DecodedSourceMap;
sourcemap_list[map_idx++],
function loader(sourcefile) { // TODO better than console.log?
if (sourcefile === filename)
return sourcemap_list[map_idx++] || null; if (map_stats.result && map_stats.result.segments_lost) {
// bundle file = branch node const { segment_loss_per_map, segments_per_map } = map_stats.result;
else return null; // source file = leaf node console.log('warning. svelte.preprocess seems to receive low-resolution sourcemaps. '+
} as SourceMapLoader 'relative segment loss per combine_sourcemaps step: '+
); segment_loss_per_map.map(f => f.toFixed(2)).join(' -> ')+
} catch (error) { '. absolute number of segments per sourcemap: '+
throw { ...error, message: error.message + segments_per_map.join(' -> ')+
'\n\ncould not combine sourcemaps:\n' + '. make your preprocessors return high-resolution sourcemaps '+
JSON.stringify(sourcemap_list.map(m => { 'or increase the tolerated loss with svelte.preprocess(_, _, { sourcemapWarnLoss: 0.8 })'
return { ...m, mappings: JSON.stringify(m.mappings).slice(0, 100)+' ....'}; );
}), null, 2)
};
} }
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 { return {
// TODO return separated output, in future version where svelte.compile supports it: // TODO return separated output, in future version where svelte.compile supports it:

@ -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 = { type SourceLocation = {
line: number; line: number;
@ -180,3 +182,152 @@ export class StringWithSourcemap {
return new StringWithSourcemap(source, map); 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<DecodedSourceMap | RawSourceMap>,
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());
}
}
});
}

@ -6,6 +6,7 @@ import { loadConfig, svelte } from "../helpers.js";
// https://github.com/mozilla/source-map/issues/400 // https://github.com/mozilla/source-map/issues/400
import { SourceMapConsumer } from "source-map"; import { SourceMapConsumer } from "source-map";
import { getLocator } from "locate-character"; import { getLocator } from "locate-character";
import { encode as encode_mappings, decode as decode_mappings } from 'sourcemap-codec';
describe("sourcemaps", () => { describe("sourcemaps", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => { fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@ -44,6 +45,13 @@ describe("sourcemaps", () => {
return test({ assert, input, preprocessed }); 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( const { js, css } = svelte.compile(
preprocessed.code, { preprocessed.code, {
filename: "input.svelte", filename: "input.svelte",
@ -60,7 +68,11 @@ describe("sourcemaps", () => {
fs.writeFileSync(`${outputBase}.svelte`, preprocessed.code); fs.writeFileSync(`${outputBase}.svelte`, preprocessed.code);
if (preprocessed.map) { 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( fs.writeFileSync(
`${outputBase}.js`, `${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: // use locate_1 with mapConsumer:
// lines are one-based, columns are zero-based // lines are one-based, columns are zero-based
if (preprocessed.map) { preprocessed.mapConsumer = preprocessed.map && await new SourceMapConsumer(preprocessed.map);
preprocessed.mapConsumer = await new SourceMapConsumer(preprocessed.map); preprocessed.locate = getLocator(preprocessed.code);
preprocessed.locate = getLocator(preprocessed.code); preprocessed.locate_1 = getLocator(preprocessed.code, { offsetLine: 1 });
preprocessed.locate_1 = getLocator(preprocessed.code, { offsetLine: 1 });
}
if (js.map) { js.mapConsumer = js.map && await new SourceMapConsumer(js.map);
js.mapConsumer = await new SourceMapConsumer(js.map); js.locate = getLocator(js.code);
js.locate = getLocator(js.code); js.locate_1 = getLocator(js.code, { offsetLine: 1 });
js.locate_1 = getLocator(js.code, { offsetLine: 1 });
}
if (css.map) { css.mapConsumer = css.map && await new SourceMapConsumer(css.map);
css.mapConsumer = await new SourceMapConsumer(css.map); css.locate = getLocator(css.code || '');
css.locate = getLocator(css.code); css.locate_1 = getLocator(css.code || '', { offsetLine: 1 });
css.locate_1 = getLocator(css.code, { offsetLine: 1 });
}
test({ assert, input, preprocessed, js, css }); test({ assert, input, preprocessed, js, css });
}); });

@ -19,6 +19,9 @@ function result(src, filename) {
} }
export default { export default {
js_map_sources: [], // test component has no scripts
preprocess: { preprocess: {
markup: ({ content, filename }) => { markup: ({ content, filename }) => {
const src = new MagicString(content); const src = new MagicString(content);

@ -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 });
} }
]
};

@ -0,0 +1,10 @@
replace_me
replace_me
replace_me
replace_me
replace_me
replace_me
replace_me
replace_me
replace_me
replace_me

@ -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?
}

@ -8,7 +8,7 @@ export default {
src.overwrite(idx, idx+"baritone".length, "bar"); src.overwrite(idx, idx+"baritone".length, "bar");
return { return {
code: src.toString(), code: src.toString(),
map: src.generateMap({ map: src.generateDecodedMap({
source: filename, source: filename,
includeContent: false includeContent: false
}) })

@ -11,7 +11,7 @@ export default {
src.overwrite(css_idx, css_idx + "--bazitone".length, "--baz"); src.overwrite(css_idx, css_idx + "--bazitone".length, "--baz");
return { return {
code: src.toString(), code: src.toString(),
map: src.generateMap({ map: src.generateDecodedMap({
source: filename, source: filename,
hires: true, hires: true,
includeContent: false includeContent: false
@ -24,7 +24,7 @@ export default {
src.prependLeft(idx, " "); src.prependLeft(idx, " ");
return { return {
code: src.toString(), code: src.toString(),
map: src.generateMap({ map: src.generateDecodedMap({
source: filename, source: filename,
hires: true, hires: true,
includeContent: false includeContent: false
@ -37,7 +37,7 @@ export default {
src.prependLeft(idx, " "); src.prependLeft(idx, " ");
return { return {
code: src.toString(), code: src.toString(),
map: src.generateMap({ map: src.generateDecodedMap({
source: filename, source: filename,
hires: true, hires: true,
includeContent: false includeContent: false

@ -10,7 +10,7 @@ function replace(search, replace, content, src, options = { storeName: true }) {
function result(src, filename) { function result(src, filename) {
return { return {
code: src.toString(), code: src.toString(),
map: src.generateMap({ map: src.generateDecodedMap({
source: filename, source: filename,
hires: true, hires: true,
includeContent: false includeContent: false

@ -4,8 +4,8 @@ function add(bundle, filename, source) {
bundle.addSource({ bundle.addSource({
filename, filename,
content: new MagicString(source), content: new MagicString(source),
separator: '\n', separator: '\n'
//separator: '', // ERROR. probably a bug in magic-string //separator: '' // ERROR. probably a bug in magic-string
}); });
} }
@ -15,7 +15,7 @@ function result(bundle, filename) {
map: bundle.generateMap({ map: bundle.generateMap({
file: filename, file: filename,
includeContent: false, includeContent: false,
hires: false hires: true // required for remapping
}) })
}; };
} }
@ -26,7 +26,7 @@ export default {
'foo.js', 'foo.js',
'bar.js', 'bar.js',
'foo2.js', 'foo2.js',
'bar2.js', 'bar2.js'
], ],
preprocess: [ preprocess: [
{ {
@ -34,8 +34,8 @@ export default {
const bundle = new Bundle(); const bundle = new Bundle();
add(bundle, filename, content); add(bundle, filename, content);
add(bundle, 'foo.js', 'var answer = 42;'); add(bundle, 'foo.js', 'var answer = 42; // foo.js\n');
add(bundle, 'bar.js', 'console.log(answer);'); add(bundle, 'bar.js', 'console.log(answer); // bar.js\n');
return result(bundle, filename); return result(bundle, filename);
} }
@ -45,8 +45,8 @@ export default {
const bundle = new Bundle(); const bundle = new Bundle();
add(bundle, filename, content); add(bundle, filename, content);
add(bundle, 'foo2.js', 'var answer2 = 84;'); add(bundle, 'foo2.js', 'var answer2 = 84; // foo2.js\n');
add(bundle, 'bar2.js', 'console.log(answer2);'); add(bundle, 'bar2.js', 'console.log(answer2); // bar2.js\n');
return result(bundle, filename); return result(bundle, filename);
} }

@ -5,12 +5,12 @@ export function test({ assert, preprocessed, js }) {
// sourcemap stores location only for 'answer = 42;' // sourcemap stores location only for 'answer = 42;'
// not for 'var answer = 42;' // not for 'var answer = 42;'
[ [
[js, 'foo.js', 'answer = 42;'], [js, 'foo.js', 'answer = 42;', 4],
[js, 'bar.js', 'console.log(answer);'], [js, 'bar.js', 'console.log(answer);', 0],
[js, 'foo2.js', 'answer2 = 84;'], [js, 'foo2.js', 'answer2 = 84;', 4],
[js, 'bar2.js', 'console.log(answer2);'], [js, 'bar2.js', 'console.log(answer2);', 0]
] ]
.forEach(([where, sourcefile, content]) => { .forEach(([where, sourcefile, content, column]) => {
assert.deepEqual( assert.deepEqual(
where.mapConsumer.originalPositionFor( where.mapConsumer.originalPositionFor(
@ -20,7 +20,7 @@ export function test({ assert, preprocessed, js }) {
source: sourcefile, source: sourcefile,
name: null, name: null,
line: 1, line: 1,
column: 0 column
}, },
`failed to locate "${content}" from "${sourcefile}"` `failed to locate "${content}" from "${sourcefile}"`
); );

@ -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);
} }
]
};

@ -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). [....]
}
Loading…
Cancel
Save