|
|
|
@ -77,6 +77,190 @@ function parent_is_head(stack) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function tag(parser: Parser) {
|
|
|
|
|
const start = parser.index++;
|
|
|
|
|
|
|
|
|
|
let parent = parser.current();
|
|
|
|
|
|
|
|
|
|
if (parser.eat('!--')) {
|
|
|
|
|
const data = parser.read_until(/-->/);
|
|
|
|
|
parser.eat('-->', true, 'comment was left open, expected -->');
|
|
|
|
|
|
|
|
|
|
parser.current().children.push({
|
|
|
|
|
start,
|
|
|
|
|
end: parser.index,
|
|
|
|
|
type: 'Comment',
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const is_closing_tag = parser.eat('/');
|
|
|
|
|
|
|
|
|
|
const name = read_tag_name(parser);
|
|
|
|
|
|
|
|
|
|
if (meta_tags.has(name)) {
|
|
|
|
|
const slug = meta_tags.get(name).toLowerCase();
|
|
|
|
|
if (is_closing_tag) {
|
|
|
|
|
if (
|
|
|
|
|
(name === 'svelte:window' || name === 'svelte:body') &&
|
|
|
|
|
parser.current().children.length
|
|
|
|
|
) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-${name.slice(7)}-content`,
|
|
|
|
|
message: `<${name}> cannot have children`
|
|
|
|
|
}, parser.current().children[0].start);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (name in parser.meta_tags) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `duplicate-${slug}`,
|
|
|
|
|
message: `A component can only have one <${name}> tag`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parser.stack.length > 1) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-${slug}-placement`,
|
|
|
|
|
message: `<${name}> tags cannot be inside elements or blocks`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.meta_tags[name] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const type = meta_tags.has(name)
|
|
|
|
|
? meta_tags.get(name)
|
|
|
|
|
: (/[A-Z]/.test(name[0]) || name === 'svelte:self' || name === 'svelte:component') ? 'InlineComponent'
|
|
|
|
|
: name === 'title' && parent_is_head(parser.stack) ? 'Title'
|
|
|
|
|
: name === 'slot' && !parser.customElement ? 'Slot' : 'Element';
|
|
|
|
|
|
|
|
|
|
const element: Node = {
|
|
|
|
|
start,
|
|
|
|
|
end: null, // filled in later
|
|
|
|
|
type,
|
|
|
|
|
name,
|
|
|
|
|
attributes: [],
|
|
|
|
|
children: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
|
|
|
|
|
if (is_closing_tag) {
|
|
|
|
|
if (is_void(name)) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-void-content`,
|
|
|
|
|
message: `<${name}> is a void element and cannot have children, or a closing tag`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
|
|
|
|
|
// close any elements that don't have their own closing tags, e.g. <div><p></div>
|
|
|
|
|
while (parent.name !== name) {
|
|
|
|
|
if (parent.type !== 'Element')
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-closing-tag`,
|
|
|
|
|
message: `</${name}> attempted to close an element that was not open`
|
|
|
|
|
}, start);
|
|
|
|
|
|
|
|
|
|
parent.end = start;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
|
|
|
|
|
parent = parser.current();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.end = parser.index;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
} else if (disallowed_contents.has(parent.name)) {
|
|
|
|
|
// can this be a child of the parent element, or does it implicitly
|
|
|
|
|
// close it, like `<li>one<li>two`?
|
|
|
|
|
if (disallowed_contents.get(parent.name).has(name)) {
|
|
|
|
|
parent.end = start;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const unique_names = new Set();
|
|
|
|
|
|
|
|
|
|
let attribute;
|
|
|
|
|
while ((attribute = read_attribute(parser, unique_names))) {
|
|
|
|
|
element.attributes.push(attribute);
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name === 'svelte:component') {
|
|
|
|
|
const index = element.attributes.findIndex(attr => attr.type === 'Attribute' && attr.name === 'this');
|
|
|
|
|
if (!~index) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `missing-component-definition`,
|
|
|
|
|
message: `<svelte:component> must have a 'this' attribute`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const definition = element.attributes.splice(index, 1)[0];
|
|
|
|
|
if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-component-definition`,
|
|
|
|
|
message: `invalid component definition`
|
|
|
|
|
}, definition.start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
element.expression = definition.value[0].expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// special cases – top-level <script> and <style>
|
|
|
|
|
if (specials.has(name) && parser.stack.length === 1) {
|
|
|
|
|
const special = specials.get(name);
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
const content = special.read(parser, start, element.attributes);
|
|
|
|
|
if (content) parser[special.property].push(content);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.current().children.push(element);
|
|
|
|
|
|
|
|
|
|
const self_closing = parser.eat('/') || is_void(name);
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
|
|
|
|
|
if (self_closing) {
|
|
|
|
|
// don't push self-closing elements onto the stack
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'textarea') {
|
|
|
|
|
// special case
|
|
|
|
|
element.children = read_sequence(
|
|
|
|
|
parser,
|
|
|
|
|
() =>
|
|
|
|
|
parser.template.slice(parser.index, parser.index + 11) === '</textarea>'
|
|
|
|
|
);
|
|
|
|
|
parser.read(/<\/textarea>/);
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'script') {
|
|
|
|
|
// special case
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
const data = parser.read_until(/<\/script>/);
|
|
|
|
|
const end = parser.index;
|
|
|
|
|
element.children.push({ start, end, type: 'Text', data });
|
|
|
|
|
parser.eat('</script>', true);
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'style') {
|
|
|
|
|
// special case
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
const data = parser.read_until(/<\/style>/);
|
|
|
|
|
const end = parser.index;
|
|
|
|
|
element.children.push({ start, end, type: 'Text', data });
|
|
|
|
|
parser.eat('</style>', true);
|
|
|
|
|
} else {
|
|
|
|
|
parser.stack.push(element);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function read_tag_name(parser: Parser) {
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
|
|
|
|
@ -132,90 +316,6 @@ function read_tag_name(parser: Parser) {
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function get_directive_type(name: string): DirectiveType {
|
|
|
|
|
if (name === 'use') return 'Action';
|
|
|
|
|
if (name === 'animate') return 'Animation';
|
|
|
|
|
if (name === 'bind') return 'Binding';
|
|
|
|
|
if (name === 'class') return 'Class';
|
|
|
|
|
if (name === 'on') return 'EventHandler';
|
|
|
|
|
if (name === 'let') return 'Let';
|
|
|
|
|
if (name === 'ref') return 'Ref';
|
|
|
|
|
if (name === 'in' || name === 'out' || name === 'transition') return 'Transition';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function read_sequence(parser: Parser, done: () => boolean): Node[] {
|
|
|
|
|
let current_chunk: Text = {
|
|
|
|
|
start: parser.index,
|
|
|
|
|
end: null,
|
|
|
|
|
type: 'Text',
|
|
|
|
|
raw: '',
|
|
|
|
|
data: null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const chunks: Node[] = [];
|
|
|
|
|
|
|
|
|
|
function flush() {
|
|
|
|
|
if (current_chunk.raw) {
|
|
|
|
|
current_chunk.data = decode_character_references(current_chunk.raw);
|
|
|
|
|
current_chunk.end = parser.index;
|
|
|
|
|
chunks.push(current_chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (parser.index < parser.template.length) {
|
|
|
|
|
const index = parser.index;
|
|
|
|
|
|
|
|
|
|
if (done()) {
|
|
|
|
|
flush();
|
|
|
|
|
return chunks;
|
|
|
|
|
} else if (parser.eat('{')) {
|
|
|
|
|
flush();
|
|
|
|
|
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
const expression = read_expression(parser);
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
parser.eat('}', true);
|
|
|
|
|
|
|
|
|
|
chunks.push({
|
|
|
|
|
start: index,
|
|
|
|
|
end: parser.index,
|
|
|
|
|
type: 'MustacheTag',
|
|
|
|
|
expression,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
current_chunk = {
|
|
|
|
|
start: parser.index,
|
|
|
|
|
end: null,
|
|
|
|
|
type: 'Text',
|
|
|
|
|
raw: '',
|
|
|
|
|
data: null
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
current_chunk.raw += parser.template[parser.index++];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `unexpected-eof`,
|
|
|
|
|
message: `Unexpected end of input`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function read_attribute_value(parser: Parser) {
|
|
|
|
|
const quote_mark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
|
|
|
|
|
|
|
|
|
|
const regex = (
|
|
|
|
|
quote_mark === `'` ? /'/ :
|
|
|
|
|
quote_mark === `"` ? /"/ :
|
|
|
|
|
/(\/>|[\s"'=<>`])/
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const value = read_sequence(parser, () => !!parser.match_regex(regex));
|
|
|
|
|
|
|
|
|
|
if (quote_mark) parser.index += 1;
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function read_attribute(parser: Parser, unique_names: Set<string>) {
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
|
|
|
|
@ -349,186 +449,86 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function tag(parser: Parser) {
|
|
|
|
|
const start = parser.index++;
|
|
|
|
|
|
|
|
|
|
let parent = parser.current();
|
|
|
|
|
|
|
|
|
|
if (parser.eat('!--')) {
|
|
|
|
|
const data = parser.read_until(/-->/);
|
|
|
|
|
parser.eat('-->', true, 'comment was left open, expected -->');
|
|
|
|
|
|
|
|
|
|
parser.current().children.push({
|
|
|
|
|
start,
|
|
|
|
|
end: parser.index,
|
|
|
|
|
type: 'Comment',
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
function get_directive_type(name: string): DirectiveType {
|
|
|
|
|
if (name === 'use') return 'Action';
|
|
|
|
|
if (name === 'animate') return 'Animation';
|
|
|
|
|
if (name === 'bind') return 'Binding';
|
|
|
|
|
if (name === 'class') return 'Class';
|
|
|
|
|
if (name === 'on') return 'EventHandler';
|
|
|
|
|
if (name === 'let') return 'Let';
|
|
|
|
|
if (name === 'ref') return 'Ref';
|
|
|
|
|
if (name === 'in' || name === 'out' || name === 'transition') return 'Transition';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const is_closing_tag = parser.eat('/');
|
|
|
|
|
|
|
|
|
|
const name = read_tag_name(parser);
|
|
|
|
|
function read_attribute_value(parser: Parser) {
|
|
|
|
|
const quote_mark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
|
|
|
|
|
|
|
|
|
|
if (meta_tags.has(name)) {
|
|
|
|
|
const slug = meta_tags.get(name).toLowerCase();
|
|
|
|
|
if (is_closing_tag) {
|
|
|
|
|
if (
|
|
|
|
|
(name === 'svelte:window' || name === 'svelte:body') &&
|
|
|
|
|
parser.current().children.length
|
|
|
|
|
) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-${name.slice(7)}-content`,
|
|
|
|
|
message: `<${name}> cannot have children`
|
|
|
|
|
}, parser.current().children[0].start);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (name in parser.meta_tags) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `duplicate-${slug}`,
|
|
|
|
|
message: `A component can only have one <${name}> tag`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
const regex = (
|
|
|
|
|
quote_mark === `'` ? /'/ :
|
|
|
|
|
quote_mark === `"` ? /"/ :
|
|
|
|
|
/(\/>|[\s"'=<>`])/
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (parser.stack.length > 1) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-${slug}-placement`,
|
|
|
|
|
message: `<${name}> tags cannot be inside elements or blocks`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
const value = read_sequence(parser, () => !!parser.match_regex(regex));
|
|
|
|
|
|
|
|
|
|
parser.meta_tags[name] = true;
|
|
|
|
|
}
|
|
|
|
|
if (quote_mark) parser.index += 1;
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const type = meta_tags.has(name)
|
|
|
|
|
? meta_tags.get(name)
|
|
|
|
|
: (/[A-Z]/.test(name[0]) || name === 'svelte:self' || name === 'svelte:component') ? 'InlineComponent'
|
|
|
|
|
: name === 'title' && parent_is_head(parser.stack) ? 'Title'
|
|
|
|
|
: name === 'slot' && !parser.customElement ? 'Slot' : 'Element';
|
|
|
|
|
|
|
|
|
|
const element: Node = {
|
|
|
|
|
start,
|
|
|
|
|
end: null, // filled in later
|
|
|
|
|
type,
|
|
|
|
|
name,
|
|
|
|
|
attributes: [],
|
|
|
|
|
children: [],
|
|
|
|
|
function read_sequence(parser: Parser, done: () => boolean): Node[] {
|
|
|
|
|
let current_chunk: Text = {
|
|
|
|
|
start: parser.index,
|
|
|
|
|
end: null,
|
|
|
|
|
type: 'Text',
|
|
|
|
|
raw: '',
|
|
|
|
|
data: null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
|
|
|
|
|
if (is_closing_tag) {
|
|
|
|
|
if (is_void(name)) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-void-content`,
|
|
|
|
|
message: `<${name}> is a void element and cannot have children, or a closing tag`
|
|
|
|
|
}, start);
|
|
|
|
|
function flush() {
|
|
|
|
|
if (current_chunk.raw) {
|
|
|
|
|
current_chunk.data = decode_character_references(current_chunk.raw);
|
|
|
|
|
current_chunk.end = parser.index;
|
|
|
|
|
chunks.push(current_chunk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
|
|
|
|
|
// close any elements that don't have their own closing tags, e.g. <div><p></div>
|
|
|
|
|
while (parent.name !== name) {
|
|
|
|
|
if (parent.type !== 'Element')
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-closing-tag`,
|
|
|
|
|
message: `</${name}> attempted to close an element that was not open`
|
|
|
|
|
}, start);
|
|
|
|
|
|
|
|
|
|
parent.end = start;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
|
|
|
|
|
parent = parser.current();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.end = parser.index;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
const chunks: Node[] = [];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
} else if (disallowed_contents.has(parent.name)) {
|
|
|
|
|
// can this be a child of the parent element, or does it implicitly
|
|
|
|
|
// close it, like `<li>one<li>two`?
|
|
|
|
|
if (disallowed_contents.get(parent.name).has(name)) {
|
|
|
|
|
parent.end = start;
|
|
|
|
|
parser.stack.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (parser.index < parser.template.length) {
|
|
|
|
|
const index = parser.index;
|
|
|
|
|
|
|
|
|
|
const unique_names = new Set();
|
|
|
|
|
if (done()) {
|
|
|
|
|
flush();
|
|
|
|
|
return chunks;
|
|
|
|
|
} else if (parser.eat('{')) {
|
|
|
|
|
flush();
|
|
|
|
|
|
|
|
|
|
let attribute;
|
|
|
|
|
while ((attribute = read_attribute(parser, unique_names))) {
|
|
|
|
|
element.attributes.push(attribute);
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name === 'svelte:component') {
|
|
|
|
|
const index = element.attributes.findIndex(attr => attr.type === 'Attribute' && attr.name === 'this');
|
|
|
|
|
if (!~index) {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `missing-component-definition`,
|
|
|
|
|
message: `<svelte:component> must have a 'this' attribute`
|
|
|
|
|
}, start);
|
|
|
|
|
}
|
|
|
|
|
const expression = read_expression(parser);
|
|
|
|
|
parser.allow_whitespace();
|
|
|
|
|
parser.eat('}', true);
|
|
|
|
|
|
|
|
|
|
const definition = element.attributes.splice(index, 1)[0];
|
|
|
|
|
if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `invalid-component-definition`,
|
|
|
|
|
message: `invalid component definition`
|
|
|
|
|
}, definition.start);
|
|
|
|
|
}
|
|
|
|
|
chunks.push({
|
|
|
|
|
start: index,
|
|
|
|
|
end: parser.index,
|
|
|
|
|
type: 'MustacheTag',
|
|
|
|
|
expression,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
element.expression = definition.value[0].expression;
|
|
|
|
|
current_chunk = {
|
|
|
|
|
start: parser.index,
|
|
|
|
|
end: null,
|
|
|
|
|
type: 'Text',
|
|
|
|
|
raw: '',
|
|
|
|
|
data: null
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
current_chunk.raw += parser.template[parser.index++];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// special cases - top-level <script> and <style>
|
|
|
|
|
if (specials.has(name) && parser.stack.length === 1) {
|
|
|
|
|
const special = specials.get(name);
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
const content = special.read(parser, start, element.attributes);
|
|
|
|
|
if (content) parser[special.property].push(content);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parser.current().children.push(element);
|
|
|
|
|
|
|
|
|
|
const self_closing = parser.eat('/') || is_void(name);
|
|
|
|
|
|
|
|
|
|
parser.eat('>', true);
|
|
|
|
|
|
|
|
|
|
if (self_closing) {
|
|
|
|
|
// don't push self-closing elements onto the stack
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'textarea') {
|
|
|
|
|
// special case
|
|
|
|
|
element.children = read_sequence(
|
|
|
|
|
parser,
|
|
|
|
|
() =>
|
|
|
|
|
parser.template.slice(parser.index, parser.index + 11) === '</textarea>'
|
|
|
|
|
);
|
|
|
|
|
parser.read(/<\/textarea>/);
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'script') {
|
|
|
|
|
// special case
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
const data = parser.read_until(/<\/script>/);
|
|
|
|
|
const end = parser.index;
|
|
|
|
|
element.children.push({ start, end, type: 'Text', data });
|
|
|
|
|
parser.eat('</script>', true);
|
|
|
|
|
element.end = parser.index;
|
|
|
|
|
} else if (name === 'style') {
|
|
|
|
|
// special case
|
|
|
|
|
const start = parser.index;
|
|
|
|
|
const data = parser.read_until(/<\/style>/);
|
|
|
|
|
const end = parser.index;
|
|
|
|
|
element.children.push({ start, end, type: 'Text', data });
|
|
|
|
|
parser.eat('</style>', true);
|
|
|
|
|
} else {
|
|
|
|
|
parser.stack.push(element);
|
|
|
|
|
}
|
|
|
|
|
parser.error({
|
|
|
|
|
code: `unexpected-eof`,
|
|
|
|
|
message: `Unexpected end of input`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|