mirror of https://github.com/sveltejs/svelte
parent
b554e343e8
commit
24b8d45f67
@ -0,0 +1,321 @@
|
||||
import { whitespace } from '../utils/patterns';
|
||||
|
||||
export interface Position {
|
||||
offset: number;
|
||||
length: number;
|
||||
source: string;
|
||||
attributes: Record<string, string|boolean> | null;
|
||||
raw_attributes: string;
|
||||
}
|
||||
|
||||
const matching_bracket = {
|
||||
']': '[',
|
||||
'}': '{',
|
||||
')': '('
|
||||
};
|
||||
|
||||
// simplified svelte parser
|
||||
export default function parse(template: string) {
|
||||
const scripts: Position[] = [];
|
||||
const styles: Position[] = [];
|
||||
const expressions: Position[] = [];
|
||||
|
||||
let index = 0;
|
||||
while (index < template.length) {
|
||||
if (match('<')) {
|
||||
parse_tag();
|
||||
} else if (match('{')) {
|
||||
parse_mustache();
|
||||
} else {
|
||||
parse_text();
|
||||
}
|
||||
}
|
||||
|
||||
function add_script_expression(get_expression: () => string) {
|
||||
const offset = index;
|
||||
const source = get_expression();
|
||||
expressions.push({ offset, length: source.length, source, attributes: null, raw_attributes: null });
|
||||
}
|
||||
|
||||
function parse_tag() {
|
||||
const tag_start = index;
|
||||
index++;
|
||||
if (eat('!--')) {
|
||||
read_until_regex(/-->/);
|
||||
eat('-->');
|
||||
} else if (eat('/')) {
|
||||
// closing tag
|
||||
read_until('>');
|
||||
index++;
|
||||
} else {
|
||||
const tag_name = read_until_regex(/(\s|\/|>)/);
|
||||
if (tag_name === 'script') {
|
||||
const raw_attributes = read_until_regex(/\/?>/);
|
||||
const attributes = parse_tag_attributes(raw_attributes);
|
||||
let source: string;
|
||||
if (eat('/>')) {
|
||||
source = '';
|
||||
} else {
|
||||
eat('>');
|
||||
source = read_until_regex(/<\/script\s*>/);
|
||||
eat_regex(/<\/script\s*>/);
|
||||
}
|
||||
const length = index - tag_start;
|
||||
scripts.push({ offset: tag_start, length, source, attributes, raw_attributes });
|
||||
} else if (tag_name === 'style') {
|
||||
const raw_attributes = read_until_regex(/\/?>/);
|
||||
const attributes = parse_tag_attributes(raw_attributes);
|
||||
let source: string;
|
||||
if (eat('/>')) {
|
||||
source = '';
|
||||
} else {
|
||||
eat('>');
|
||||
source = read_until_regex(/<\/style\s*>/);
|
||||
eat_regex(/<\/style\s*>/);
|
||||
}
|
||||
const length = index - tag_start;
|
||||
styles.push({ offset: tag_start, length, source, attributes, raw_attributes });
|
||||
} else {
|
||||
while (index < template.length) {
|
||||
allow_whitespace();
|
||||
if (eat('{')) {
|
||||
// handle spread
|
||||
eat_regex(/\s+\.\.\./);
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
// attribute name
|
||||
if (!read_until_regex(/[\s=/>"']/)) {
|
||||
break;
|
||||
}
|
||||
allow_whitespace();
|
||||
if (eat('=')) {
|
||||
// attribute value
|
||||
allow_whitespace();
|
||||
const quote_mark = eat("'") ? "'" : eat('"') ? '"' : null;
|
||||
if (quote_mark && eat(quote_mark)) {
|
||||
continue;
|
||||
}
|
||||
const ending_regex =
|
||||
quote_mark === "'"
|
||||
? /'/
|
||||
: quote_mark === '"' ? /"/ : /(\/>|[\s"'=<>`])/;
|
||||
while (index < template.length) {
|
||||
if (match_regex(ending_regex)) {
|
||||
break;
|
||||
} else if (eat('{')) {
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
if (quote_mark) index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parse_tag_attributes(str: string) {
|
||||
const attributes = {};
|
||||
let i = 0;
|
||||
while (i < str.length) {
|
||||
const char = str[i++];
|
||||
if (/\s/.test(char)) continue;
|
||||
let name = char;
|
||||
while (i < str.length && !/[\s=/>"']/.test(str[i])) {
|
||||
name += str[i++];
|
||||
}
|
||||
if (str[i] === '=') {
|
||||
i++;
|
||||
const quote_mark = str[i];
|
||||
const regex = (
|
||||
quote_mark === "'" ? /'/ :
|
||||
quote_mark === '"' ? /"/ :
|
||||
/(\/>|[\s"'=<>`])/
|
||||
);
|
||||
if (quote_mark === "'" || quote_mark === '"') {
|
||||
i++;
|
||||
}
|
||||
const match = str.slice(i).match(regex);
|
||||
let value;
|
||||
if (match) {
|
||||
value = str.slice(i, i + match.index);
|
||||
i += match.index + 1;
|
||||
} else {
|
||||
value = str.slice(i + 1);
|
||||
i = str.length;
|
||||
}
|
||||
attributes[name] = value;
|
||||
} else {
|
||||
attributes[name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function parse_mustache() {
|
||||
index++;
|
||||
// {/if}, {/each}, {/await} or {/key}
|
||||
if (eat('/')) {
|
||||
read_until('}');
|
||||
index++;
|
||||
} else if (eat(':else')) {
|
||||
allow_whitespace();
|
||||
if (eat('if')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
}
|
||||
allow_whitespace();
|
||||
eat('}');
|
||||
} else if (match(':then') || match(':catch')) {
|
||||
eat(':then') || eat('catch');
|
||||
if (!eat('}')) {
|
||||
allow_whitespace();
|
||||
read_expression_until('}');
|
||||
eat('}');
|
||||
}
|
||||
} else if (eat('#')) {
|
||||
if (eat('if') || eat('key')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
} else if (eat('each')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until(/\sas/));
|
||||
eat_regex(/\sas/);
|
||||
allow_whitespace();
|
||||
read_expression_until('}');
|
||||
index++;
|
||||
} else if (eat('await')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until(/\sthen|\scatch|\}/));
|
||||
if (eat_regex(/\sthen/) || eat_regex(/\scatch/)) {
|
||||
read_expression_until('}');
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
read_until('}');
|
||||
index++;
|
||||
}
|
||||
} else if (eat('@html')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
} else if (eat('@debug')) {
|
||||
allow_whitespace();
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
} else {
|
||||
add_script_expression(() => read_expression_until('}'));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
function parse_text() {
|
||||
while (index < template.length && !match('<') && !match('{')) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
function match(str: string) {
|
||||
return template.slice(index, index + str.length) === str;
|
||||
}
|
||||
function match_regex(pattern: RegExp) {
|
||||
const match = pattern.exec(template.slice(index));
|
||||
if (!match || match.index !== 0) return null;
|
||||
return match[0];
|
||||
}
|
||||
function eat(str: string) {
|
||||
if (match(str)) {
|
||||
index += str.length;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function eat_regex(pattern: RegExp) {
|
||||
const result = match_regex(pattern);
|
||||
if (result) index += result.length;
|
||||
return result;
|
||||
}
|
||||
function allow_whitespace() {
|
||||
while (index < template.length && whitespace.test(template[index])) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
function read_until(str: string) {
|
||||
const start = index;
|
||||
const next_index = template.slice(start).indexOf(str);
|
||||
if (next_index > -1) {
|
||||
index += next_index;
|
||||
return template.slice(start, index);
|
||||
}
|
||||
index = template.length;
|
||||
return template.slice(start);
|
||||
}
|
||||
function read_until_regex(pattern: RegExp) {
|
||||
const start = index;
|
||||
const match = pattern.exec(template.slice(index));
|
||||
if (match) {
|
||||
index += match.index;
|
||||
return template.slice(start, index);
|
||||
}
|
||||
index = template.length;
|
||||
return template.slice(start);
|
||||
}
|
||||
function read_expression_until(str: string | RegExp) {
|
||||
const bracket_stack = [];
|
||||
let quote = null;
|
||||
const start = index;
|
||||
|
||||
const is_ending =
|
||||
typeof str === 'string' ? () => match(str) : () => match_regex(str);
|
||||
|
||||
while (index < template.length) {
|
||||
if (bracket_stack.length === 0 && quote === null && is_ending()) {
|
||||
break;
|
||||
}
|
||||
const char = template[index];
|
||||
switch (char) {
|
||||
case "'":
|
||||
case '"':
|
||||
case '`': {
|
||||
if (quote === null) {
|
||||
quote = char;
|
||||
} else if (quote === char) {
|
||||
quote = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '[':
|
||||
case '{':
|
||||
case '(':
|
||||
if (quote === null) {
|
||||
bracket_stack.push(char);
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
case '}':
|
||||
case ')':
|
||||
if (quote === null) {
|
||||
const find = matching_bracket[char];
|
||||
// assume unclosed bracket
|
||||
while (bracket_stack.length) {
|
||||
if (bracket_stack.pop() === find) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return template.slice(start, index);
|
||||
}
|
||||
|
||||
return { scripts, styles, expressions };
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
preprocess: {
|
||||
script: () => {
|
||||
return {
|
||||
code: 'tag'
|
||||
};
|
||||
},
|
||||
expression: () => {
|
||||
return {
|
||||
code: 'replaced'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
<script>
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<div on:click={() => { return a?.b?.c; }}>
|
||||
this is {a#c}
|
||||
</div>
|
||||
|
||||
<div
|
||||
use:action={{ foo: 1 }}
|
||||
use:action="{ foo: 1 }"
|
||||
{ ...asdf[123]["qwe(((("]() }
|
||||
class:foo="{123}"
|
||||
transition:fade
|
||||
in:fly={{ y: 10 }}
|
||||
/>
|
||||
|
||||
{#if a}
|
||||
{#each array['a`[x']["]]]]]]"]['[[[['] as item}
|
||||
{:else}
|
||||
{#await getPromise().[foo()]['"a"'] then { a }}
|
||||
{:catch error}
|
||||
{/await}
|
||||
{/each}
|
||||
{:else if b}
|
||||
{#each array[123](f, y, z) as item}
|
||||
{ [([(a['asdf](((}}}[][!@#asdf}}}}'])])] + foo }
|
||||
{/each}
|
||||
{:else}
|
||||
{/if}
|
||||
|
||||
{#each foo
|
||||
as {
|
||||
as
|
||||
}}
|
||||
{as}
|
||||
{/each}
|
||||
|
||||
<Component
|
||||
a={foo > 32 + { a: 1}}
|
||||
b="3[3+4)4"
|
||||
class="a {b} fds {c[123]} df {{ a: 3 }[`a
|
||||
`]} qwe"
|
||||
>
|
||||
<div on:click on:bind> {a} {@debug b} {@html '@debug qwe'} {a < 32 }</div>
|
||||
</Component>
|
||||
@ -0,0 +1,43 @@
|
||||
<script>tag</script>
|
||||
|
||||
<div on:click={replaced}>
|
||||
this is {replaced}
|
||||
</div>
|
||||
|
||||
<div
|
||||
use:action={replaced}
|
||||
use:action="{replaced}"
|
||||
{ ...replaced}
|
||||
class:foo="{replaced}"
|
||||
transition:fade
|
||||
in:fly={replaced}
|
||||
/>
|
||||
|
||||
{#if replaced}
|
||||
{#each replaced as item}
|
||||
{:else}
|
||||
{#await replaced then { a }}
|
||||
{:catch error}
|
||||
{/await}
|
||||
{/each}
|
||||
{:else if replaced}
|
||||
{#each replaced as item}
|
||||
{replaced}
|
||||
{/each}
|
||||
{:else}
|
||||
{/if}
|
||||
|
||||
{#each replaced
|
||||
as {
|
||||
as
|
||||
}}
|
||||
{replaced}
|
||||
{/each}
|
||||
|
||||
<Component
|
||||
a={replaced}
|
||||
b="3[3+4)4"
|
||||
class="a {replaced} fds {replaced} df {replaced} qwe"
|
||||
>
|
||||
<div on:click on:bind> {replaced} {@debug replaced} {@html replaced} {replaced}</div>
|
||||
</Component>
|
||||
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
preprocess: {
|
||||
script: ({ is_expression, content }) => {
|
||||
return {
|
||||
code: is_expression ? content : 'let z = 42;'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<script context="module">
|
||||
let b = 2;
|
||||
</script>
|
||||
|
||||
{@html '<script>let c = 3;</script>'}
|
||||
|
||||
<script context="worker">
|
||||
let d = 4;
|
||||
</script>
|
||||
|
||||
{@html `
|
||||
<script>
|
||||
let e = 5;
|
||||
</script>
|
||||
`}
|
||||
|
||||
<script>
|
||||
let f = 6;
|
||||
</script>
|
||||
@ -0,0 +1,15 @@
|
||||
<script>let z = 42;</script>
|
||||
|
||||
<script context="module">let z = 42;</script>
|
||||
|
||||
{@html '<script>let c = 3;</script>'}
|
||||
|
||||
<script context="worker">let z = 42;</script>
|
||||
|
||||
{@html `
|
||||
<script>
|
||||
let e = 5;
|
||||
</script>
|
||||
`}
|
||||
|
||||
<script>let z = 42;</script>
|
||||
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
preprocess: {
|
||||
style: () => {
|
||||
return {
|
||||
code: 'h1 { color: white; }'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,45 @@
|
||||
<script context="module">
|
||||
let a = '<style>div { color: blue; }</style>';
|
||||
let b = `
|
||||
<style>
|
||||
div { color: green; }
|
||||
</style>
|
||||
`;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let c = '<style>div { color: blue; }</style>';
|
||||
let d = `
|
||||
<style>
|
||||
div { color: green; }
|
||||
</style>
|
||||
`;
|
||||
</script>
|
||||
|
||||
{a} {b} {c} {d}
|
||||
|
||||
{@html '<style>div { color: yellow; }</style>'}
|
||||
|
||||
<style>
|
||||
div {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
|
||||
{@html `
|
||||
<style>
|
||||
div { color: pink; }
|
||||
</style>
|
||||
`}
|
||||
|
||||
<style>
|
||||
div {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,33 @@
|
||||
<script context="module">
|
||||
let a = '<style>div { color: blue; }</style>';
|
||||
let b = `
|
||||
<style>
|
||||
div { color: green; }
|
||||
</style>
|
||||
`;
|
||||
</script>
|
||||
|
||||
<style>h1 { color: white; }</style>
|
||||
|
||||
<script>
|
||||
let c = '<style>div { color: blue; }</style>';
|
||||
let d = `
|
||||
<style>
|
||||
div { color: green; }
|
||||
</style>
|
||||
`;
|
||||
</script>
|
||||
|
||||
{a} {b} {c} {d}
|
||||
|
||||
{@html '<style>div { color: yellow; }</style>'}
|
||||
|
||||
<style>h1 { color: white; }</style>
|
||||
|
||||
{@html `
|
||||
<style>
|
||||
div { color: pink; }
|
||||
</style>
|
||||
`}
|
||||
|
||||
<style>h1 { color: white; }</style>
|
||||
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
preprocess: {
|
||||
script: ({ content, attributes }) => {
|
||||
attributes = { ...attributes, c: false, insert: 'foobar' };
|
||||
delete attributes['lang'];
|
||||
if (attributes.context) {
|
||||
attributes.context = 'module';
|
||||
}
|
||||
|
||||
return {
|
||||
code: content,
|
||||
attributes
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
<script lang="ts" a="b" />
|
||||
|
||||
<script context="worker" foo='a'>
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<script a="a=3" b='qwe' c d={a} e f class="a {asdf} qwe">
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<script a="'a' b `c`" b='"a cd `""'>
|
||||
</script>
|
||||
|
||||
{a}
|
||||
@ -0,0 +1,14 @@
|
||||
<script a="b" insert="foobar"></script>
|
||||
|
||||
<script context="module" foo="a" insert="foobar">
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<script a="a=3" b="qwe" d="{a}" e f class="a {asdf} qwe" insert="foobar">
|
||||
let a = 1;
|
||||
</script>
|
||||
|
||||
<script a="'a' b `c`" b='"a cd `""' insert="foobar">
|
||||
</script>
|
||||
|
||||
{a}
|
||||
Loading…
Reference in new issue