pull/5428/head
Milan Hauth 5 years ago
parent 6668f12148
commit 459dd88b71

@ -95,22 +95,21 @@ function get_replacement(
prefix: string, prefix: string,
suffix: string suffix: string
): StringWithSourcemap { ): StringWithSourcemap {
const generated_prefix = StringWithSourcemap.from_source( const prefix_with_map = StringWithSourcemap.from_source(
filename, prefix, get_location(offset)); filename, prefix, get_location(offset));
const generated_suffix = StringWithSourcemap.from_source( const suffix_with_map = StringWithSourcemap.from_source(
filename, suffix, get_location(offset + prefix.length + original.length)); filename, suffix, get_location(offset + prefix.length + original.length));
let generated; let processed_map_shifted;
if (processed.map) { if (processed.map) {
const full_map = typeof processed.map === "string" ? JSON.parse(processed.map) : processed.map; const decoded_map = typeof processed.map === "string" ? JSON.parse(processed.map) : processed.map;
const decoded_map = { ...full_map, mappings: sourcemap_decode(full_map.mappings) }; decoded_map.mappings = sourcemap_decode(decoded_map.mappings);
const processed_offset = get_location(offset + prefix.length); const processed_offset = get_location(offset + prefix.length);
generated = StringWithSourcemap.from_generated(processed.code, sourcemap_add_offset(processed_offset, decoded_map)); processed_map_shifted = sourcemap_add_offset(decoded_map, processed_offset);
} else {
generated = StringWithSourcemap.from_generated(processed.code);
} }
const map = generated_prefix.concat(generated).concat(generated_suffix); const processed_with_map = StringWithSourcemap.from_processed(processed.code, processed_map_shifted);
return map;
return prefix_with_map.concat(processed_with_map).concat(suffix_with_map);
} }
export default async function preprocess( export default async function preprocess(
@ -177,7 +176,7 @@ export default async function preprocess(
: no_change(); : no_change();
} }
); );
source = res.generated; source = res.string;
sourcemap_list.unshift(res.get_sourcemap()); sourcemap_list.unshift(res.get_sourcemap());
} }
@ -210,7 +209,7 @@ export default async function preprocess(
: no_change(); : no_change();
} }
); );
source = res.generated; source = res.string;
sourcemap_list.unshift(res.get_sourcemap()); sourcemap_list.unshift(res.get_sourcemap());
} }

@ -16,145 +16,97 @@ type SourceLocation = {
column: number; column: number;
}; };
function get_end_location(s: string): SourceLocation { function last_line_length(s: string) {
const parts = s.split("\n"); return s.length - s.lastIndexOf('\n') - 1;
return {
line: parts.length - 1,
column: parts[parts.length - 1].length - 1
};
} }
export function sourcemap_add_offset( export function sourcemap_add_offset(
offset: SourceLocation, map: SourceMappings, offset: SourceLocation
map: SourceMappings
): SourceMappings { ): SourceMappings {
const new_mappings = map.mappings.map((line) => return {
line.map((seg) => { sources: map.sources.slice(),
if (seg.length < 3) return seg; mappings: map.mappings.map((line, line_idx) =>
line.map(seg => {
const new_seg = seg.slice() as MappingSegment; const new_seg = seg.slice() as MappingSegment;
if (seg.length >= 4) {
new_seg[2] = new_seg[2] + offset.line; new_seg[2] = new_seg[2] + offset.line;
if (line_idx == 0)
new_seg[3] = new_seg[3] + offset.column;
}
return new_seg; return new_seg;
}) })
); )
// column changed in first line
if (new_mappings.length > 0) {
new_mappings[0] = new_mappings[0].map((seg) => {
if (seg.length < 4) return seg;
const newSeg = seg.slice() as MappingSegment;
newSeg[3] = newSeg[3] + offset.column;
return newSeg;
});
}
return {
sources: map.sources,
mappings: new_mappings
} as SourceMappings; } as SourceMappings;
} }
function merge_tables<T>( function merge_tables<T>(this_table: T[], other_table): [T[], number[]] {
original: T[], const new_table = this_table.slice();
extended: T[] const idx_map = [];
): { table: T[]; new_idx: number[] } { other_table = other_table || [];
const table = original.slice(); for (const [other_idx, other_val] of other_table.entries()) {
const new_idx = []; const this_idx = this_table.indexOf(other_val);
if (extended) { if (this_idx >= 0) {
for (let j = 0; j < extended.length; j++) { idx_map[other_idx] = this_idx;
const current = extended[j];
const existing = table.indexOf(current);
if (existing == -1) {
table.push(current);
new_idx[j] = table.length - 1;
} else { } else {
new_idx[j] = existing; const new_idx = new_table.length;
} new_table[new_idx] = other_val;
idx_map[other_idx] = new_idx;
} }
} }
return { table, new_idx }; return [new_table, idx_map];
} }
export class StringWithSourcemap { export class StringWithSourcemap {
readonly generated: string; readonly string: string;
readonly map: SourceMappings; readonly map: SourceMappings;
constructor(generated: string, map: SourceMappings) { constructor(string: string, map: SourceMappings) {
this.generated = generated; this.string = string;
this.map = map; this.map = map;
} }
get_sourcemap() { get_sourcemap() {
return { return {
version: 3, version: 3,
sources: this.map.sources, sources: this.map.sources.slice(),
names: [], names: [],
mappings: sourcemap_encode(this.map.mappings as any) mappings: sourcemap_encode(this.map.mappings as any)
}; };
} }
concat(other: StringWithSourcemap): StringWithSourcemap { concat(other: StringWithSourcemap): StringWithSourcemap {
// if one is empty, return the other // noop: if one is empty, return the other
if (this.generated.length == 0) return other; if (this.string == '') return other;
if (other.generated.length == 0) return this; if (other.string == '') return this;
// combine sources
const {
table: new_sources,
new_idx: other_source_idx
} = merge_tables(
this.map.sources,
other.map.sources
);
// combine names // combine sources and names
const { const [sources, new_source_idx] = merge_tables(this.map.sources, other.map.sources);
table: new_names, const [names, new_name_idx] = merge_tables(this.map.names, other.map.names);
new_idx: other_name_idx
} = merge_tables(
this.map.names,
other.map.names
);
// update source refs and name refs in segments // update source refs and name refs in segments
const other_mappings = other.map.mappings.map((line) => const other_mappings = other.map.mappings.map((line) =>
line.map((seg) => { line.map(seg => {
// to reduce allocations,
// we only return a new segment if a value has changed
if (
// new source idx
(seg.length > 1 && other_source_idx[seg[1]] != seg[1]) ||
// new name idx
(seg.length == 5 && other_name_idx[seg[4]] != seg[4])
) {
const new_seg = seg.slice() as MappingSegment; const new_seg = seg.slice() as MappingSegment;
new_seg[1] = other_source_idx[seg[1]]; if (seg[1]) new_seg[1] = new_source_idx[seg[1]];
if (seg.length == 5) { if (seg[4]) new_seg[4] = new_name_idx[seg[4]];
new_seg[4] = other_name_idx[seg[4]];
}
return new_seg; return new_seg;
} else {
return seg;
}
}) })
); );
// combine the mappings // combine the mappings
// this.map is read-only, so we copy // combine
let new_mappings = this.map.mappings.slice();
// combine:
// 1. last line of first map // 1. last line of first map
// 2. first line of second map // 2. first line of second map
// columns of 2 must be shifted // columns of 2 must be shifted
const end = get_end_location(this.generated);
const col_offset = end.column + 1;
const first_line = const col_offset = last_line_length(this.string);
const first_line: MappingSegment[] =
other_mappings.length == 0 other_mappings.length == 0
? [] ? []
: col_offset == 0 : col_offset == 0
? other_mappings[0] ? other_mappings[0].slice() as MappingSegment[]
: other_mappings[0].map((seg) => { : other_mappings[0].map((seg) => {
// shift columns // shift columns
const new_seg = seg.slice() as MappingSegment; const new_seg = seg.slice() as MappingSegment;
@ -162,88 +114,54 @@ export class StringWithSourcemap {
return new_seg; return new_seg;
}); });
// append segments to last line of first map const mappings: MappingSegment[][] =
new_mappings[new_mappings.length - 1] = this.map.mappings.slice(0, -1)
new_mappings[new_mappings.length - 1].concat(first_line); .concat([
this.map.mappings.slice(-1)[0] // last line
// the other lines don't need modification and can just be appended .concat(first_line)
new_mappings = new_mappings.concat( ])
other_mappings.slice(1) as MappingSegment[][] .concat(other_mappings.slice(1) as MappingSegment[][]);
);
return new StringWithSourcemap( return new StringWithSourcemap(
this.generated + other.generated, { this.string + other.string,
sources: new_sources, { sources, names, mappings }
names: new_names, );
mappings: new_mappings
});
}
static from_generated(
generated: string,
map?: SourceMappings
): StringWithSourcemap {
if (map) return new StringWithSourcemap(generated, map);
const replacement_map: SourceMappings = {
names: [],
sources: [],
mappings: []
};
if (generated.length == 0)
return new StringWithSourcemap(generated, replacement_map);
// we generate a mapping
// where the source was overwritten by the generated
const end = get_end_location(generated);
for (let i = 0; i <= end.line; i++) {
replacement_map.mappings.push([]); // unmapped line
} }
return new StringWithSourcemap(generated, replacement_map); static from_processed(string: string, map?: SourceMappings): StringWithSourcemap {
if (map) return new StringWithSourcemap(string, map);
map = { names: [], sources: [], mappings: [] };
if (string == '') return new StringWithSourcemap(string, map);
// add empty MappingSegment[] for every line
const lineCount = string.split('\n').length;
map.mappings = Array.from({length: lineCount}).map(_ => []);
return new StringWithSourcemap(string, map);
} }
static from_source( static from_source(
source_file: string, source_file: string, source: string, offset_in_source?: SourceLocation
source: string,
offset_in_source?: SourceLocation
): StringWithSourcemap { ): StringWithSourcemap {
const offset = offset_in_source || { line: 0, column: 0 }; const offset = offset_in_source || { line: 0, column: 0 };
const map: SourceMappings = { const map: SourceMappings = { names: [], sources: [source_file], mappings: [] };
names: [],
sources: [source_file],
mappings: []
};
if (source.length == 0) return new StringWithSourcemap(source, map); if (source.length == 0) return new StringWithSourcemap(source, map);
// we create a high resolution identity map here, // we create a high resolution identity map here,
// we know that it will eventually be merged with svelte's map, // we know that it will eventually be merged with svelte's map,
// at which stage the resolution will decrease. // at which stage the resolution will decrease.
const lines = source.split("\n"); map.mappings = source.split("\n").map((line, line_idx) => {
let pos = 0; let pos = 0;
const identity_map = lines.map((line, line_idx) => { const segs = line.split(/([^\d\w\s]|\s+)/g)
const segs = line .filter(s => s !== "").map(s => {
.split(/([^\d\w\s]|\s+)/g)
.filter((s) => s !== "")
.map((s) => {
const seg: MappingSegment = [ const seg: MappingSegment = [
pos, pos, 0,
0,
line_idx + offset.line, line_idx + offset.line,
// shift first line pos + (line_idx == 0 ? offset.column : 0) // shift first line
pos + (line_idx == 0 ? offset.column : 0)
]; ];
pos = pos + s.length; pos = pos + s.length;
return seg; return seg;
}); });
pos = 0;
return segs; return segs;
}); });
map.mappings = identity_map;
return new StringWithSourcemap(source, map); return new StringWithSourcemap(source, map);
} }
} }

Loading…
Cancel
Save