preprocessor sourcemaps: prettify code 2

move string_with_map.ts to string_with_sourcemap.ts
rename GeneratedStringWithMap to StringWithSourcemap
rename offset_source_location to sourcemap_add_offset
rename source_maps to sourcemap_list
rename source_locator to get_location
rename as_sourcemap to get_sourcemap
rename encode to sourcemap_encode
move { brace to line before
add some comments
pull/5428/head
Milan Hauth 5 years ago
parent 43c5d5ecfc
commit b8e9b68c0d

@ -1,7 +1,7 @@
import remapper from '@ampproject/remapping'; import remapper from '@ampproject/remapping';
import { decode } from 'sourcemap-codec'; import { decode } from 'sourcemap-codec';
import { getLocator } from 'locate-character'; import { getLocator } from 'locate-character';
import { GeneratedStringWithMap, offset_source_location } from '../utils/string_with_map'; import { StringWithSourcemap, sourcemap_add_offset } from '../utils/string_with_sourcemap';
export interface Processed { export interface Processed {
@ -43,7 +43,7 @@ function parse_attributes(str: string) {
interface Replacement { interface Replacement {
offset: number; offset: number;
length: number; length: number;
replacement: GeneratedStringWithMap; replacement: StringWithSourcemap;
} }
export default async function preprocess( export default async function preprocess(
@ -60,28 +60,41 @@ export default async function preprocess(
const markup = preprocessors.map(p => p.markup).filter(Boolean); const markup = preprocessors.map(p => p.markup).filter(Boolean);
const script = preprocessors.map(p => p.script).filter(Boolean); const script = preprocessors.map(p => p.script).filter(Boolean);
const style = preprocessors.map(p => p.style).filter(Boolean); const style = preprocessors.map(p => p.style).filter(Boolean);
const source_maps: Array<Processed['map']> = [];
let source_locator: ReturnType<typeof getLocator>; const sourcemap_list: Array<Processed['map']> = [];
function get_replacement(offset: number, original: string, processed: Processed, prefix: string, suffix: string): GeneratedStringWithMap { let get_location: ReturnType<typeof getLocator>;
const generated_prefix = GeneratedStringWithMap.from_source(filename, prefix, source_locator(offset));
const generated_suffix = GeneratedStringWithMap.from_source(filename, suffix, source_locator(offset + prefix.length + original.length)); function get_replacement(
offset: number,
original: string,
processed: Processed,
prefix: string,
suffix: string
): StringWithSourcemap {
const generated_prefix = StringWithSourcemap.from_source(
filename, prefix, get_location(offset));
const generated_suffix = StringWithSourcemap.from_source(
filename, suffix, get_location(offset + prefix.length + original.length));
let generated; let generated;
if (processed.map) { if (processed.map) {
const full_map = typeof processed.map === "string" ? JSON.parse(processed.map) : processed.map; const full_map = typeof processed.map === "string" ? JSON.parse(processed.map) : processed.map;
const decoded_map = { ...full_map, mappings: decode(full_map.mappings) }; const decoded_map = { ...full_map, mappings: decode(full_map.mappings) };
const processed_offset = source_locator(offset + prefix.length); const processed_offset = get_location(offset + prefix.length);
generated = GeneratedStringWithMap.from_generated(processed.code, offset_source_location(processed_offset, decoded_map)); generated = StringWithSourcemap.from_generated(processed.code, sourcemap_add_offset(processed_offset, decoded_map));
} else { } else {
generated = GeneratedStringWithMap.from_generated(processed.code); generated = StringWithSourcemap.from_generated(processed.code);
} }
const map = generated_prefix.concat(generated).concat(generated_suffix); const map = generated_prefix.concat(generated).concat(generated_suffix);
return map; return map;
} }
async function replace_async(str: string, re: RegExp, func: (...any) => Promise<GeneratedStringWithMap>): Promise<GeneratedStringWithMap> { async function replace_async(
str: string,
re: RegExp,
func: (...any) => Promise<StringWithSourcemap>
): Promise<StringWithSourcemap> {
const replacement_promises: Array<Promise<Replacement>> = []; const replacement_promises: Array<Promise<Replacement>> = [];
str.replace(re, (...args) => { str.replace(re, (...args) => {
replacement_promises.push( replacement_promises.push(
@ -98,86 +111,117 @@ export default async function preprocess(
}); });
const replacements = await Promise.all(replacement_promises); const replacements = await Promise.all(replacement_promises);
let out: GeneratedStringWithMap; let out: StringWithSourcemap;
let last_end = 0; let last_end = 0;
for (const { offset, length, replacement } of replacements) for (const { offset, length, replacement } of replacements) {
{ // content = source before replacement
const content = GeneratedStringWithMap.from_source(filename, str.slice(last_end, offset), source_locator(last_end)); const content = StringWithSourcemap.from_source(
filename, str.slice(last_end, offset), get_location(last_end));
out = out ? out.concat(content) : content; out = out ? out.concat(content) : content;
out = out.concat(replacement); out = out.concat(replacement);
last_end = offset + length; last_end = offset + length;
} }
const final_content = GeneratedStringWithMap.from_source(filename, str.slice(last_end), source_locator(last_end)); // final_content = source after last replacement
const final_content = StringWithSourcemap.from_source(
filename, str.slice(last_end), get_location(last_end));
out = out.concat(final_content); out = out.concat(final_content);
return out; return out;
} }
for (const fn of markup) { for (const fn of markup) {
// run markup preprocessor
const processed = await fn({ const processed = await fn({
content: source, content: source,
filename filename
}); });
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
if (processed && processed.dependencies) {
dependencies.push(...processed.dependencies);
}
source = processed ? processed.code : source; source = processed ? processed.code : source;
if (processed && processed.map) source_maps.unshift(processed.map); if (processed && processed.map) {
sourcemap_list.unshift(processed.map);
}
} }
for (const fn of script) { for (const fn of script) {
source_locator = getLocator(source); get_location = getLocator(source);
const res = await replace_async( const res = await replace_async(
source, source,
/<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi, /<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi,
async (match, attributes = '', content, offset) => { async (match, attributes = '', content, offset) => {
const no_change = () => GeneratedStringWithMap.from_source(filename, match, source_locator(offset)); const no_change = () => StringWithSourcemap.from_source(
filename, match, get_location(offset));
if (!attributes && !content) { if (!attributes && !content) {
return no_change(); return no_change();
} }
attributes = attributes || ''; attributes = attributes || '';
// run script preprocessor
const processed = await fn({ const processed = await fn({
content, content,
attributes: parse_attributes(attributes), attributes: parse_attributes(attributes),
filename filename
}); });
if (!processed) return no_change(); if (!processed) {
if (processed.dependencies) dependencies.push(...processed.dependencies); return no_change();
return get_replacement(offset, content, processed, `<script${attributes}>`, `</script>`); }
if (processed.dependencies) {
dependencies.push(...processed.dependencies);
}
return get_replacement(
offset, content, processed,
`<script${attributes}>`, `</script>`
);
} }
); );
source = res.generated; source = res.generated;
source_maps.unshift(res.as_sourcemap()); sourcemap_list.unshift(res.get_sourcemap());
} }
for (const fn of style) { for (const fn of style) {
source_locator = getLocator(source); get_location = getLocator(source);
const res = await replace_async( const res = await replace_async(
source, source,
/<!--[^]*?-->|<style(\s[^]*?)?>([^]*?)<\/style>/gi, /<!--[^]*?-->|<style(\s[^]*?)?>([^]*?)<\/style>/gi,
async (match, attributes = '', content, offset) => { async (match, attributes = '', content, offset) => {
const no_change = () => GeneratedStringWithMap.from_source(filename, match, source_locator(offset)); const no_change = () => StringWithSourcemap.from_source(
filename, match, get_location(offset));
if (!attributes && !content) { if (!attributes && !content) {
return no_change(); return no_change();
} }
// run style preprocessor
const processed: Processed = await fn({ const processed: Processed = await fn({
content, content,
attributes: parse_attributes(attributes), attributes: parse_attributes(attributes),
filename filename
}); });
if (!processed) return no_change(); if (!processed) {
if (processed.dependencies) dependencies.push(...processed.dependencies); return no_change();
return get_replacement(offset, content, processed, `<style${attributes}>`, `</style>`); }
if (processed.dependencies) {
dependencies.push(...processed.dependencies);
}
return get_replacement(
offset, content, processed,
`<style${attributes}>`, `</style>`
);
} }
); );
source = res.generated; source = res.generated;
source_maps.unshift(res.as_sourcemap()); sourcemap_list.unshift(res.get_sourcemap());
} }
const map: ReturnType<typeof remapper> = source_maps.length == 0 ? null : remapper(source_maps as any, () => null); const map: ReturnType<typeof remapper> =
sourcemap_list.length == 0
? null
: remapper(sourcemap_list as any, () => null);
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:
// style: { code: styleCode, map: styleMap }, // style: { code: styleCode, map: styleMap },

@ -1,4 +1,4 @@
import { encode } from "sourcemap-codec"; import { encode as sourcemap_encode } from "sourcemap-codec";
type MappingSegment = type MappingSegment =
| [number] | [number]
@ -24,7 +24,7 @@ function get_end_location(s: string): SourceLocation {
}; };
} }
export function offset_source_location( export function sourcemap_add_offset(
offset: SourceLocation, offset: SourceLocation,
map: SourceMappings map: SourceMappings
): SourceMappings { ): SourceMappings {
@ -72,7 +72,7 @@ function merge_tables<T>(
return { table, new_idx }; return { table, new_idx };
} }
export class GeneratedStringWithMap { export class StringWithSourcemap {
readonly generated: string; readonly generated: string;
readonly map: SourceMappings; readonly map: SourceMappings;
@ -81,16 +81,16 @@ export class GeneratedStringWithMap {
this.map = map; this.map = map;
} }
as_sourcemap() { get_sourcemap() {
return { return {
version: 3, version: 3,
sources: this.map.sources, sources: this.map.sources,
names: [], names: [],
mappings: encode(this.map.mappings as any), mappings: sourcemap_encode(this.map.mappings as any),
}; };
} }
concat(other: GeneratedStringWithMap): GeneratedStringWithMap { concat(other: StringWithSourcemap): StringWithSourcemap {
// if one is empty, return the other // if one is empty, return the other
if (this.generated.length == 0) return other; if (this.generated.length == 0) return other;
if (other.generated.length == 0) return this; if (other.generated.length == 0) return this;
@ -103,6 +103,8 @@ export class GeneratedStringWithMap {
this.map.sources, this.map.sources,
other.map.sources other.map.sources
); );
// combine names
const { const {
table: new_names, table: new_names,
new_idx: other_name_idx new_idx: other_name_idx
@ -135,10 +137,14 @@ export class GeneratedStringWithMap {
); );
// combine the mappings // combine the mappings
// this.map is readonly, so we copy
let new_mappings = this.map.mappings.slice(); let new_mappings = this.map.mappings.slice();
// shift the first line of the second mapping // combine:
// by the number of columns in the last line of the first mapping // 1. last line of first map
// 2. first line of second map
// columns of 2 must be shifted
const end = get_end_location(this.generated); const end = get_end_location(this.generated);
const col_offset = end.column + 1; const col_offset = end.column + 1;
const first_line = const first_line =
@ -149,16 +155,17 @@ export class GeneratedStringWithMap {
new_seg[0] = seg[0] + col_offset; new_seg[0] = seg[0] + col_offset;
return new_seg; return new_seg;
}); });
new_mappings[new_mappings.length - 1] = new_mappings[
new_mappings.length - 1
].concat(first_line);
// the rest don't need modification and can just be appended // append segments to last line of first map
new_mappings[new_mappings.length - 1] =
new_mappings[new_mappings.length - 1].concat(first_line);
// the other lines don't need modification and can just be appended
new_mappings = new_mappings.concat( new_mappings = new_mappings.concat(
other_mappings.slice(1) as MappingSegment[][] other_mappings.slice(1) as MappingSegment[][]
); );
return new GeneratedStringWithMap( return new StringWithSourcemap(
this.generated + other.generated, { this.generated + other.generated, {
sources: new_sources, sources: new_sources,
names: new_names, names: new_names,
@ -169,8 +176,8 @@ export class GeneratedStringWithMap {
static from_generated( static from_generated(
generated: string, generated: string,
map?: SourceMappings map?: SourceMappings
): GeneratedStringWithMap { ): StringWithSourcemap {
if (map) return new GeneratedStringWithMap(generated, map); if (map) return new StringWithSourcemap(generated, map);
const replacement_map: SourceMappings = { const replacement_map: SourceMappings = {
names: [], names: [],
@ -179,7 +186,7 @@ export class GeneratedStringWithMap {
}; };
if (generated.length == 0) if (generated.length == 0)
return new GeneratedStringWithMap(generated, replacement_map); return new StringWithSourcemap(generated, replacement_map);
// we generate a mapping // we generate a mapping
// where the source was overwritten by the generated // where the source was overwritten by the generated
@ -188,14 +195,14 @@ export class GeneratedStringWithMap {
replacement_map.mappings.push([]); // unmapped line replacement_map.mappings.push([]); // unmapped line
} }
return new GeneratedStringWithMap(generated, replacement_map); return new StringWithSourcemap(generated, replacement_map);
} }
static from_source( static from_source(
source_file: string, source_file: string,
source: string, source: string,
offset_in_source?: SourceLocation offset_in_source?: SourceLocation
): GeneratedStringWithMap { ): 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: [], names: [],
@ -203,7 +210,7 @@ export class GeneratedStringWithMap {
mappings: [], mappings: [],
}; };
if (source.length == 0) return new GeneratedStringWithMap(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,
@ -213,12 +220,13 @@ export class GeneratedStringWithMap {
const identity_map = lines.map((line, line_idx) => { const identity_map = lines.map((line, line_idx) => {
const segs = line const segs = line
.split(/([^\d\w\s]|\s+)/g) .split(/([^\d\w\s]|\s+)/g)
.filter((x) => x !== "") .filter((s) => s !== "")
.map((s) => { .map((s) => {
const seg: MappingSegment = [ const seg: MappingSegment = [
pos, pos,
0, 0,
offset.line + line_idx, line_idx + offset.line,
// shift first line
pos + (line_idx == 0 ? offset.column : 0), pos + (line_idx == 0 ? offset.column : 0),
]; ];
pos = pos + s.length; pos = pos + s.length;
@ -230,6 +238,6 @@ export class GeneratedStringWithMap {
map.mappings = identity_map; map.mappings = identity_map;
return new GeneratedStringWithMap(source, map); return new StringWithSourcemap(source, map);
} }
} }
Loading…
Cancel
Save