Merge pull request #1810 from sveltejs/gh-1809

in svelte.preprocess, replace all instances of style and script tags
pull/1878/head
Rich Harris 6 years ago committed by GitHub
commit 85b731c1bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,5 @@
import { SourceMap } from 'magic-string';
import replaceAsync from '../utils/replaceAsync';
export interface PreprocessOptions {
markup?: (options: {
@ -16,6 +17,11 @@ export type Preprocessor = (options: {
filename?: string
}) => { code: string, map?: SourceMap | string };
interface Processed {
code: string;
map?: SourceMap | string;
}
function parseAttributeValue(value: string) {
return /^['"]/.test(value) ?
value.slice(1, -1) :
@ -31,60 +37,32 @@ function parseAttributes(str: string) {
return attrs;
}
async function replaceTagContents(
source,
type: 'script' | 'style',
preprocessor: Preprocessor,
options: PreprocessOptions
) {
const exp = new RegExp(`<${type}([\\S\\s]*?)>([\\S\\s]*?)<\\/${type}>`, 'ig');
const match = exp.exec(source);
if (match) {
const attributes: Record<string, string | boolean> = parseAttributes(match[1]);
const content: string = match[2];
const processed: { code: string, map?: SourceMap | string } = await preprocessor({
content,
attributes,
filename : options.filename
});
if (processed && processed.code) {
return (
source.slice(0, match.index) +
`<${type}>${processed.code}</${type}>` +
source.slice(match.index + match[0].length)
);
}
}
return source;
}
export default async function preprocess(
source: string,
options: PreprocessOptions
) {
const { markup, style, script } = options;
if (!!markup) {
const processed: {
code: string,
map?: SourceMap | string
} = await markup({
if (options.markup) {
const processed: Processed = await options.markup({
content: source,
filename: options.filename
filename: options.filename,
});
source = processed.code;
source = processed ? processed.code : source;
}
if (!!style) {
source = await replaceTagContents(source, 'style', style, options);
}
if (!!script) {
source = await replaceTagContents(source, 'script', script, options);
if (options.style || options.script) {
source = await replaceAsync(
source,
/<(script|style)([^]*?)>([^]*?)<\/\1>/gi,
async (match, type, attributes, content) => {
const processed: Processed =
type in options &&
(await options[type]({
content,
attributes: parseAttributes(attributes),
filename: options.filename,
}));
return processed ? `<${type}>${processed.code}</${type}>` : match;
}
);
}
return {
@ -97,4 +75,4 @@ export default async function preprocess(
return source;
}
};
}
}

@ -0,0 +1,38 @@
// asynchronous String#replace
export default async function replaceAsync(
str: string,
re: RegExp,
func: (...any) => Promise<string>
) {
const replacements: Promise<Replacement>[] = [];
str.replace(re, (...args) => {
replacements.push(
func(...args).then(
res =>
<Replacement>{
offset: args[args.length - 2],
length: args[0].length,
replacement: res,
}
)
);
return '';
});
let out = '';
let lastEnd = 0;
for (const { offset, length, replacement } of await Promise.all(
replacements
)) {
out += str.slice(lastEnd, offset) + replacement;
lastEnd = offset + length;
}
out += str.slice(lastEnd);
return out;
}
interface Replacement {
offset: number;
length: number;
replacement: string;
}

@ -110,6 +110,42 @@ describe('preprocess', () => {
});
});
it('preprocesses multiple matching tags', () => {
const source = `
<script>
REPLACEME
</script>
<style>
SHOULD NOT BE REPLACED
</style>
<script>
REPLACEMETOO
</script>
`;
const expected = `
<script>
replaceme
</script>
<style>
SHOULD NOT BE REPLACED
</style>
<script>
replacemetoo
</script>
`;
return svelte.preprocess(source, {
script: ({ content }) => {
return {
code: content.toLowerCase()
};
}
}).then(processed => {
assert.equal(processed.toString(), expected);
});
});
it('parses attributes', () => {
const source = `
<style type='text/scss' data-foo="bar" bool></style>

Loading…
Cancel
Save