move fn replace_async, etc

move fn replace_async back to old place to reduce line diffs
fn preprocess: allow empty argument `preprocessor` (noop)
make fns replace_async, get_replacement pure fns
  pass filename, get_location as argument
restore old format to reduce line diffs
remove empty fields of sourcemap: file, sourcesContent
pull/5428/head
Milan Hauth 5 years ago
parent 307276ac1c
commit dee3aabf9c

6
package-lock.json generated

@ -2661,9 +2661,9 @@
} }
}, },
"magic-string": { "magic-string": {
"version": "0.25.7", "version": "0.25.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz",
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==",
"dev": true, "dev": true,
"requires": { "requires": {
"sourcemap-codec": "^1.4.4" "sourcemap-codec": "^1.4.4"

@ -46,6 +46,46 @@ interface Replacement {
replacement: StringWithSourcemap; replacement: StringWithSourcemap;
} }
async function replace_async(
filename: string,
source: string,
get_location: ReturnType<typeof getLocator>,
re: RegExp,
func: (...any) => Promise<StringWithSourcemap>
): Promise<StringWithSourcemap> {
const replacements: Array<Promise<Replacement>> = [];
source.replace(re, (...args) => {
replacements.push(
func(...args).then(
res =>
({
offset: args[args.length - 2],
length: args[0].length,
replacement: res
}) as Replacement
)
);
return '';
});
let out: StringWithSourcemap;
let last_end = 0;
for (const { offset, length, replacement } of await Promise.all(
replacements
)) {
// content = source before replacement
const content = StringWithSourcemap.from_source(
filename, source.slice(last_end, offset), get_location(last_end));
out = out ? out.concat(content) : content;
out = out.concat(replacement);
last_end = offset + length;
}
// final_content = source after last replacement
const final_content = StringWithSourcemap.from_source(
filename, source.slice(last_end), get_location(last_end));
out = out.concat(final_content);
return out;
}
export default async function preprocess( export default async function preprocess(
source: string, source: string,
preprocessor: PreprocessorGroup | PreprocessorGroup[], preprocessor: PreprocessorGroup | PreprocessorGroup[],
@ -55,23 +95,29 @@ export default async function preprocess(
const filename = (options && options.filename) || preprocessor.filename; // legacy const filename = (options && options.filename) || preprocessor.filename; // legacy
const dependencies = []; const dependencies = [];
const preprocessors = Array.isArray(preprocessor) ? preprocessor : [preprocessor]; const preprocessors = preprocessor
? Array.isArray(preprocessor) ? preprocessor : [preprocessor]
: []; // noop
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);
// 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: Array<Processed['map']> = []; const sourcemap_list: Array<Processed['map']> = [];
let get_location: ReturnType<typeof getLocator>;
function get_replacement( function get_replacement(
filename: string,
offset: number, offset: number,
get_location: ReturnType<typeof getLocator>,
original: string, original: string,
processed: Processed, processed: Processed,
prefix: string, prefix: string,
suffix: string suffix: string
): StringWithSourcemap { ): StringWithSourcemap {
const generated_prefix = StringWithSourcemap.from_source( const generated_prefix = StringWithSourcemap.from_source(
filename, prefix, get_location(offset)); filename, prefix, get_location(offset));
const generated_suffix = StringWithSourcemap.from_source( const generated_suffix = StringWithSourcemap.from_source(
@ -90,44 +136,6 @@ export default async function preprocess(
return map; return map;
} }
async function replace_async(
str: string,
re: RegExp,
func: (...any) => Promise<StringWithSourcemap>
): Promise<StringWithSourcemap> {
const replacement_promises: Array<Promise<Replacement>> = [];
str.replace(re, (...args) => {
replacement_promises.push(
func(...args).then(
(replacement) =>
({
offset: args[args.length - 2],
length: args[0].length,
replacement
}) as Replacement
)
);
return '';
});
const replacements = await Promise.all(replacement_promises);
let out: StringWithSourcemap;
let last_end = 0;
for (const { offset, length, replacement } of replacements) {
// content = source before replacement
const content = StringWithSourcemap.from_source(
filename, str.slice(last_end, offset), get_location(last_end));
out = out ? out.concat(content) : content;
out = out.concat(replacement);
last_end = offset + length;
}
// 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);
return out;
}
for (const fn of markup) { for (const fn of markup) {
// run markup preprocessor // run markup preprocessor
@ -136,21 +144,19 @@ export default async function preprocess(
filename filename
}); });
if (processed && processed.dependencies) { if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
dependencies.push(...processed.dependencies);
}
source = processed ? processed.code : source; source = processed ? processed.code : source;
if (processed && processed.map) { if (processed && processed.map) sourcemap_list.unshift(processed.map);
sourcemap_list.unshift(processed.map);
}
} }
for (const fn of script) { for (const fn of script) {
get_location = getLocator(source); const get_location = getLocator(source);
const res = await replace_async( const res = await replace_async(
filename,
source, source,
get_location,
/<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi, /<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi,
async (match, attributes = '', content, offset) => { async (match, attributes = '', content = '', offset) => {
const no_change = () => StringWithSourcemap.from_source( const no_change = () => StringWithSourcemap.from_source(
filename, match, get_location(offset)); filename, match, get_location(offset));
if (!attributes && !content) { if (!attributes && !content) {
@ -166,16 +172,10 @@ export default async function preprocess(
filename filename
}); });
if (!processed) { if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
return no_change(); return processed
} ? get_replacement(filename, offset, get_location, content, processed, `<script${attributes}>`, `</script>`)
if (processed.dependencies) { : no_change();
dependencies.push(...processed.dependencies);
}
return get_replacement(
offset, content, processed,
`<script${attributes}>`, `</script>`
);
} }
); );
source = res.generated; source = res.generated;
@ -183,11 +183,13 @@ export default async function preprocess(
} }
for (const fn of style) { for (const fn of style) {
get_location = getLocator(source); const get_location = getLocator(source);
const res = await replace_async( const res = await replace_async(
filename,
source, source,
get_location,
/<!--[^]*?-->|<style(\s[^]*?)?(?:>([^]*?)<\/style>|\/>)/gi, /<!--[^]*?-->|<style(\s[^]*?)?(?:>([^]*?)<\/style>|\/>)/gi,
async (match, attributes = '', content, offset) => { async (match, attributes = '', content = '', offset) => {
const no_change = () => StringWithSourcemap.from_source( const no_change = () => StringWithSourcemap.from_source(
filename, match, get_location(offset)); filename, match, get_location(offset));
if (!attributes && !content) { if (!attributes && !content) {
@ -203,27 +205,35 @@ export default async function preprocess(
filename filename
}); });
if (!processed) { if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
return no_change(); return processed
} ? get_replacement(filename, offset, get_location, content, processed, `<style${attributes}>`, `</style>`)
if (processed.dependencies) { : no_change();
dependencies.push(...processed.dependencies);
}
return get_replacement(
offset, content, processed,
`<style${attributes}>`, `</style>`
);
} }
); );
source = res.generated; source = res.generated;
sourcemap_list.unshift(res.get_sourcemap()); sourcemap_list.unshift(res.get_sourcemap());
} }
// HACK
// remove `undefined` sources in first sourcemap
// otherwise remapper throws error:
// Error: Transformation map 0 must have exactly one source file.
// Did you specify these with the most recent transformation maps first?
// test: preprocess comments
(firstMap => {
if (firstMap && firstMap.sources)
firstMap.sources = firstMap.sources.filter(Boolean);
})(sourcemap_list[0] as any);
// https://github.com/ampproject/remapping#usage
// https://github.com/mozilla/source-map#new-sourcemapconsumerrawsourcemap
const map: ReturnType<typeof remapper> = const map: ReturnType<typeof remapper> =
sourcemap_list.length == 0 sourcemap_list.length == 0
? null ? null
: remapper(sourcemap_list as any, () => null); : remapper(sourcemap_list as any, () => null, true); // true: skip optional field `sourcesContent`
if (map) delete map.file; // skip optional field `file`
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:
@ -234,6 +244,7 @@ export default async function preprocess(
code: source, code: source,
dependencies: [...new Set(dependencies)], dependencies: [...new Set(dependencies)],
map, map,
toString() { toString() {
return source; return source;
} }

@ -138,7 +138,7 @@ export class StringWithSourcemap {
// combine the mappings // combine the mappings
// this.map is readonly, so we copy // this.map is read-only, so we copy
let new_mappings = this.map.mappings.slice(); let new_mappings = this.map.mappings.slice();
// combine: // combine:
@ -147,10 +147,14 @@ export class StringWithSourcemap {
// columns of 2 must be shifted // 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 =
other_mappings.length == 0 other_mappings.length == 0
? [] ? []
: col_offset == 0
? other_mappings[0]
: other_mappings[0].map((seg) => { : other_mappings[0].map((seg) => {
// shift columns
const new_seg = seg.slice() as MappingSegment; const new_seg = seg.slice() as MappingSegment;
new_seg[0] = seg[0] + col_offset; new_seg[0] = seg[0] + col_offset;
return new_seg; return new_seg;

Loading…
Cancel
Save