more tidying up

pull/2252/head
Richard Harris 7 years ago
parent f21c3b7136
commit 63075603f3

@ -4,7 +4,26 @@ import Block from '../render-dom/Block';
import Expression from './shared/Expression';
import mapChildren from './shared/mapChildren';
import TemplateScope from './shared/TemplateScope';
import unpackDestructuring from '../../utils/unpackDestructuring';
import { Node as INode } from '../../interfaces';
function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) {
if (!node) return;
if (node.type === 'Identifier') {
contexts.push({
key: node,
tail
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
unpack_destructuring(contexts, element, `${tail}[${i}]`);
});
} else if (node.type === 'ObjectPattern') {
node.properties.forEach((property) => {
unpack_destructuring(contexts, property.value, `${tail}.${property.key.name}`);
});
}
}
export default class EachBlock extends Node {
type: 'EachBlock';
@ -36,7 +55,7 @@ export default class EachBlock extends Node {
this.scope = scope.child();
this.contexts = [];
unpackDestructuring(this.contexts, info.context, '');
unpack_destructuring(this.contexts, info.context, '');
this.contexts.forEach(context => {
this.scope.add(context.key.name, this.expression.dependencies, this);

@ -14,7 +14,7 @@ import Text from './Text';
import Title from './Title';
import Window from './Window';
import Node from '../../nodes/shared/Node';
import { trimStart, trimEnd } from '../../../utils/trim';
import { trim_start, trim_end } from '../../../utils/trim';
import TextWrapper from './Text';
import Renderer from '../Renderer';
import Block from '../Block';
@ -89,7 +89,7 @@ export default class FragmentWrapper {
);
if (shouldTrim) {
data = trimEnd(data);
data = trim_end(data);
if (!data) continue;
}
}
@ -121,7 +121,7 @@ export default class FragmentWrapper {
const first = this.nodes[0] as TextWrapper;
if (first && first.node.type === 'Text') {
first.data = trimStart(first.data);
first.data = trim_start(first.data);
if (!first.data) {
first.var = null;
this.nodes.shift();

@ -15,7 +15,7 @@ export interface Parser {
html: Node;
css: Node;
js: Node;
metaTags: {};
meta_tags: {};
}
export interface Ast {

@ -9,7 +9,7 @@ export const parse = (source: string) => Parser.parse(source, {
preserveParens: true
});
export const parseExpressionAt = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
ecmaVersion: 9,
preserveParens: true
});

@ -8,7 +8,6 @@ import error from '../utils/error';
interface ParserOptions {
filename?: string;
bind?: boolean;
customElement?: boolean;
}
@ -25,9 +24,7 @@ export class Parser {
html: Node;
css: Node[] = [];
js: Node[] = [];
metaTags = {};
allowBindings: boolean;
meta_tags = {};
constructor(template: string, options: ParserOptions) {
if (typeof template !== 'string') {
@ -38,8 +35,6 @@ export class Parser {
this.filename = options.filename;
this.customElement = options.customElement;
this.allowBindings = options.bind !== false;
this.html = {
start: null,
end: null,
@ -92,7 +87,7 @@ export class Parser {
return this.stack[this.stack.length - 1];
}
acornError(err: any) {
acorn_error(err: any) {
this.error({
code: `parse-error`,
message: err.message.replace(/ \(\d+:\d+\)$/, '')
@ -129,14 +124,14 @@ export class Parser {
return this.template.slice(this.index, this.index + str.length) === str;
}
matchRegex(pattern: RegExp) {
match_regex(pattern: RegExp) {
const match = pattern.exec(this.template.slice(this.index));
if (!match || match.index !== 0) return null;
return match[0];
}
allowWhitespace() {
allow_whitespace() {
while (
this.index < this.template.length &&
whitespace.test(this.template[this.index])
@ -146,12 +141,12 @@ export class Parser {
}
read(pattern: RegExp) {
const result = this.matchRegex(pattern);
const result = this.match_regex(pattern);
if (result) this.index += result.length;
return result;
}
readIdentifier() {
read_identifier() {
const start = this.index;
let i = this.index;
@ -180,7 +175,7 @@ export class Parser {
return identifier;
}
readUntil(pattern: RegExp) {
read_until(pattern: RegExp) {
if (this.index >= this.template.length)
this.error({
code: `unexpected-eof`,
@ -199,7 +194,7 @@ export class Parser {
return this.template.slice(start);
}
requireWhitespace() {
require_whitespace() {
if (!whitespace.test(this.template[this.index])) {
this.error({
code: `missing-whitespace`,
@ -207,7 +202,7 @@ export class Parser {
});
}
this.allowWhitespace();
this.allow_whitespace();
}
}

@ -26,7 +26,7 @@ type Context = {
properties?: Property[];
}
function errorOnAssignmentPattern(parser: Parser) {
function error_on_assignment_pattern(parser: Parser) {
if (parser.eat('=')) {
parser.error({
code: 'invalid-assignment-pattern',
@ -35,7 +35,7 @@ function errorOnAssignmentPattern(parser: Parser) {
}
}
export default function readContext(parser: Parser) {
export default function read_context(parser: Parser) {
const context: Context = {
start: parser.index,
end: null,
@ -47,17 +47,17 @@ export default function readContext(parser: Parser) {
context.elements = [];
do {
parser.allowWhitespace();
parser.allow_whitespace();
if (parser.template[parser.index] === ',') {
context.elements.push(null);
} else {
context.elements.push(readContext(parser));
parser.allowWhitespace();
context.elements.push(read_context(parser));
parser.allow_whitespace();
}
} while (parser.eat(','));
errorOnAssignmentPattern(parser);
error_on_assignment_pattern(parser);
parser.eat(']', true);
context.end = parser.index;
}
@ -67,20 +67,20 @@ export default function readContext(parser: Parser) {
context.properties = [];
do {
parser.allowWhitespace();
parser.allow_whitespace();
const start = parser.index;
const name = parser.readIdentifier();
const name = parser.read_identifier();
const key: Identifier = {
start,
end: parser.index,
type: 'Identifier',
name
};
parser.allowWhitespace();
parser.allow_whitespace();
const value = parser.eat(':')
? (parser.allowWhitespace(), readContext(parser))
? (parser.allow_whitespace(), read_context(parser))
: key;
const property: Property = {
@ -95,16 +95,16 @@ export default function readContext(parser: Parser) {
context.properties.push(property);
parser.allowWhitespace();
parser.allow_whitespace();
} while (parser.eat(','));
errorOnAssignmentPattern(parser);
error_on_assignment_pattern(parser);
parser.eat('}', true);
context.end = parser.index;
}
else {
const name = parser.readIdentifier();
const name = parser.read_identifier();
if (name) {
context.type = 'Identifier';
context.end = parser.index;
@ -118,7 +118,7 @@ export default function readContext(parser: Parser) {
});
}
errorOnAssignmentPattern(parser);
error_on_assignment_pattern(parser);
}
return context;

@ -1,12 +1,12 @@
import { parseExpressionAt } from '../acorn';
import { parse_expression_at } from '../acorn';
import { Parser } from '../index';
const literals = new Map([['true', true], ['false', false], ['null', null]]);
export default function readExpression(parser: Parser) {
export default function read_expression(parser: Parser) {
const start = parser.index;
const name = parser.readUntil(/\s*}/);
const name = parser.read_until(/\s*}/);
if (name && /^[a-z]+$/.test(name)) {
const end = start + name.length;
@ -31,11 +31,11 @@ export default function readExpression(parser: Parser) {
parser.index = start;
try {
const node = parseExpressionAt(parser.template, parser.index);
const node = parse_expression_at(parser.template, parser.index);
parser.index = node.end;
return node;
} catch (err) {
parser.acornError(err);
parser.acorn_error(err);
}
}

@ -3,7 +3,7 @@ import repeat from '../../utils/repeat';
import { Parser } from '../index';
import { Node } from '../../interfaces';
const scriptClosingTag = '</script>';
const script_closing_tag = '</script>';
function get_context(parser: Parser, attributes: Node[], start: number) {
const context = attributes.find(attribute => attribute.name === 'context');
@ -28,28 +28,28 @@ function get_context(parser: Parser, attributes: Node[], start: number) {
return value;
}
export default function readScript(parser: Parser, start: number, attributes: Node[]) {
const scriptStart = parser.index;
const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart);
export default function read_script(parser: Parser, start: number, attributes: Node[]) {
const script_start = parser.index;
const script_end = parser.template.indexOf(script_closing_tag, script_start);
if (scriptEnd === -1) parser.error({
if (script_end === -1) parser.error({
code: `unclosed-script`,
message: `<script> must have a closing tag`
});
const source =
repeat(' ', scriptStart) + parser.template.slice(scriptStart, scriptEnd);
parser.index = scriptEnd + scriptClosingTag.length;
repeat(' ', script_start) + parser.template.slice(script_start, script_end);
parser.index = script_end + script_closing_tag.length;
let ast;
try {
ast = acorn.parse(source);
} catch (err) {
parser.acornError(err);
parser.acorn_error(err);
}
ast.start = scriptStart;
ast.start = script_start;
return {
start,
end: parser.index,

@ -3,17 +3,17 @@ import { walk } from 'estree-walker';
import { Parser } from '../index';
import { Node } from '../../interfaces';
export default function readStyle(parser: Parser, start: number, attributes: Node[]) {
const contentStart = parser.index;
const styles = parser.readUntil(/<\/style>/);
const contentEnd = parser.index;
export default function read_style(parser: Parser, start: number, attributes: Node[]) {
const content_start = parser.index;
const styles = parser.read_until(/<\/style>/);
const content_end = parser.index;
let ast;
try {
ast = parse(styles, {
positions: true,
offset: contentStart,
offset: content_start,
});
} catch (err) {
if (err.name === 'CssSyntaxError') {
@ -37,7 +37,7 @@ export default function readStyle(parser: Parser, start: number, attributes: Nod
const a = node.children[i];
const b = node.children[i + 1];
if (isRefSelector(a, b)) {
if (is_ref_selector(a, b)) {
parser.error({
code: `invalid-ref-selector`,
message: 'ref selectors are no longer supported'
@ -63,14 +63,14 @@ export default function readStyle(parser: Parser, start: number, attributes: Nod
attributes,
children: ast.children,
content: {
start: contentStart,
end: contentEnd,
start: content_start,
end: content_end,
styles,
},
};
}
function isRefSelector(a: Node, b: Node) {
function is_ref_selector(a: Node, b: Node) {
if (!b) return false;
return (

@ -1,32 +1,32 @@
import readContext from '../read/context';
import readExpression from '../read/expression';
import read_context from '../read/context';
import read_expression from '../read/expression';
import { whitespace } from '../../utils/patterns';
import { trimStart, trimEnd } from '../../utils/trim';
import { trim_start, trim_end } from '../../utils/trim';
import { Parser } from '../index';
import { Node } from '../../interfaces';
function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) {
function trim_whitespace(block: Node, trim_before: boolean, trim_after: boolean) {
if (!block.children || block.children.length === 0) return; // AwaitBlock
const firstChild = block.children[0];
const lastChild = block.children[block.children.length - 1];
if (firstChild.type === 'Text' && trimBefore) {
firstChild.data = trimStart(firstChild.data);
if (firstChild.type === 'Text' && trim_before) {
firstChild.data = trim_start(firstChild.data);
if (!firstChild.data) block.children.shift();
}
if (lastChild.type === 'Text' && trimAfter) {
lastChild.data = trimEnd(lastChild.data);
if (lastChild.type === 'Text' && trim_after) {
lastChild.data = trim_end(lastChild.data);
if (!lastChild.data) block.children.pop();
}
if (block.else) {
trimWhitespace(block.else, trimBefore, trimAfter);
trim_whitespace(block.else, trim_before, trim_after);
}
if (firstChild.elseif) {
trimWhitespace(firstChild, trimBefore, trimAfter);
trim_whitespace(firstChild, trim_before, trim_after);
}
}
@ -34,7 +34,7 @@ export default function mustache(parser: Parser) {
const start = parser.index;
parser.index += 1;
parser.allowWhitespace();
parser.allow_whitespace();
// {/if} or {/each}
if (parser.eat('/')) {
@ -63,7 +63,7 @@ export default function mustache(parser: Parser) {
}
parser.eat(expected, true);
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
while (block.elseif) {
@ -77,12 +77,12 @@ export default function mustache(parser: Parser) {
}
// strip leading/trailing whitespace as necessary
const charBefore = parser.template[block.start - 1];
const charAfter = parser.template[parser.index];
const trimBefore = !charBefore || whitespace.test(charBefore);
const trimAfter = !charAfter || whitespace.test(charAfter);
const char_before = parser.template[block.start - 1];
const char_after = parser.template[parser.index];
const trim_before = !char_before || whitespace.test(char_before);
const trim_after = !char_after || whitespace.test(char_after);
trimWhitespace(block, trimBefore, trimAfter);
trim_whitespace(block, trim_before, trim_after);
block.end = parser.index;
parser.stack.pop();
@ -94,7 +94,7 @@ export default function mustache(parser: Parser) {
});
}
parser.allowWhitespace();
parser.allow_whitespace();
// :else if
if (parser.eat('if')) {
@ -105,11 +105,11 @@ export default function mustache(parser: Parser) {
message: 'Cannot have an {:else if ...} block outside an {#if ...} block'
});
parser.requireWhitespace();
parser.require_whitespace();
const expression = readExpression(parser);
const expression = read_expression(parser);
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
block.else = {
@ -141,7 +141,7 @@ export default function mustache(parser: Parser) {
});
}
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
block.else = {
@ -155,52 +155,52 @@ export default function mustache(parser: Parser) {
}
} else if (parser.eat(':then')) {
// TODO DRY out this and the next section
const pendingBlock = parser.current();
if (pendingBlock.type === 'PendingBlock') {
pendingBlock.end = start;
const pending_block = parser.current();
if (pending_block.type === 'PendingBlock') {
pending_block.end = start;
parser.stack.pop();
const awaitBlock = parser.current();
const await_block = parser.current();
if (!parser.eat('}')) {
parser.requireWhitespace();
awaitBlock.value = parser.readIdentifier();
parser.allowWhitespace();
parser.require_whitespace();
await_block.value = parser.read_identifier();
parser.allow_whitespace();
parser.eat('}', true);
}
const thenBlock: Node = {
const then_block: Node = {
start,
end: null,
type: 'ThenBlock',
children: []
};
awaitBlock.then = thenBlock;
parser.stack.push(thenBlock);
await_block.then = then_block;
parser.stack.push(then_block);
}
} else if (parser.eat(':catch')) {
const thenBlock = parser.current();
if (thenBlock.type === 'ThenBlock') {
thenBlock.end = start;
const then_block = parser.current();
if (then_block.type === 'ThenBlock') {
then_block.end = start;
parser.stack.pop();
const awaitBlock = parser.current();
const await_block = parser.current();
if (!parser.eat('}')) {
parser.requireWhitespace();
awaitBlock.error = parser.readIdentifier();
parser.allowWhitespace();
parser.require_whitespace();
await_block.error = parser.read_identifier();
parser.allow_whitespace();
parser.eat('}', true);
}
const catchBlock: Node = {
const catch_block: Node = {
start,
end: null,
type: 'CatchBlock',
children: []
};
awaitBlock.catch = catchBlock;
parser.stack.push(catchBlock);
await_block.catch = catch_block;
parser.stack.push(catch_block);
}
} else if (parser.eat('#')) {
// {#if foo}, {#each foo} or {#await foo}
@ -219,9 +219,9 @@ export default function mustache(parser: Parser) {
});
}
parser.requireWhitespace();
parser.require_whitespace();
const expression = readExpression(parser);
const expression = read_expression(parser);
const block: Node = type === 'AwaitBlock' ?
{
@ -258,50 +258,50 @@ export default function mustache(parser: Parser) {
children: [],
};
parser.allowWhitespace();
parser.allow_whitespace();
// {#each} blocks must declare a context {#each list as item}
if (type === 'EachBlock') {
parser.eat('as', true);
parser.requireWhitespace();
parser.require_whitespace();
block.context = readContext(parser);
block.context = read_context(parser);
parser.allowWhitespace();
parser.allow_whitespace();
if (parser.eat(',')) {
parser.allowWhitespace();
block.index = parser.readIdentifier();
parser.allow_whitespace();
block.index = parser.read_identifier();
if (!block.index) parser.error({
code: `expected-name`,
message: `Expected name`
});
parser.allowWhitespace();
parser.allow_whitespace();
}
if (parser.eat('(')) {
parser.allowWhitespace();
parser.allow_whitespace();
block.key = readExpression(parser);
parser.allowWhitespace();
block.key = read_expression(parser);
parser.allow_whitespace();
parser.eat(')', true);
parser.allowWhitespace();
parser.allow_whitespace();
} else if (parser.eat('@')) {
block.key = parser.readIdentifier();
block.key = parser.read_identifier();
if (!block.key) parser.error({
code: `expected-name`,
message: `Expected name`
});
parser.allowWhitespace();
parser.allow_whitespace();
}
}
let awaitBlockShorthand = type === 'AwaitBlock' && parser.eat('then');
if (awaitBlockShorthand) {
parser.requireWhitespace();
block.value = parser.readIdentifier();
parser.allowWhitespace();
let await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
if (await_block_shorthand) {
parser.require_whitespace();
block.value = parser.read_identifier();
parser.allow_whitespace();
}
parser.eat('}', true);
@ -310,17 +310,17 @@ export default function mustache(parser: Parser) {
parser.stack.push(block);
if (type === 'AwaitBlock') {
const childBlock = awaitBlockShorthand ? block.then : block.pending;
const childBlock = await_block_shorthand ? block.then : block.pending;
childBlock.start = parser.index;
parser.stack.push(childBlock);
}
} else if (parser.eat('@html')) {
// {@html content} tag
parser.requireWhitespace();
parser.require_whitespace();
const expression = readExpression(parser);
const expression = read_expression(parser);
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
parser.current().children.push({
@ -336,7 +336,7 @@ export default function mustache(parser: Parser) {
if (parser.read(/\s*}/)) {
identifiers = [];
} else {
const expression = readExpression(parser);
const expression = read_expression(parser);
identifiers = expression.type === 'SequenceExpression'
? expression.expressions
@ -351,7 +351,7 @@ export default function mustache(parser: Parser) {
}
});
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
}
@ -362,9 +362,9 @@ export default function mustache(parser: Parser) {
identifiers
});
} else {
const expression = readExpression(parser);
const expression = read_expression(parser);
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
parser.current().children.push({

@ -1,36 +1,36 @@
import readExpression from '../read/expression';
import readScript from '../read/script';
import readStyle from '../read/style';
import { decodeCharacterReferences } from '../utils/html';
import read_expression from '../read/expression';
import read_script from '../read/script';
import read_style from '../read/style';
import { decode_character_references } from '../utils/html';
import { is_void } from '../../utils/names';
import { Parser } from '../index';
import { Node } from '../../interfaces';
import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const metaTags = new Map([
const meta_tags = new Map([
['svelte:head', 'Head'],
['svelte:options', 'Options'],
['svelte:window', 'Window'],
['svelte:body', 'Body']
]);
const valid_meta_tags = Array.from(metaTags.keys()).concat('svelte:self', 'svelte:component');
const valid_meta_tags = Array.from(meta_tags.keys()).concat('svelte:self', 'svelte:component');
const specials = new Map([
[
'script',
{
read: readScript,
read: read_script,
property: 'js',
},
],
[
'style',
{
read: readStyle,
read: read_style,
property: 'css',
},
],
@ -40,7 +40,7 @@ const SELF = /^svelte:self(?=[\s\/>])/;
const COMPONENT = /^svelte:component(?=[\s\/>])/;
// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
const disallowedContents = new Map([
const disallowed_contents = new Map([
['li', new Set(['li'])],
['dt', new Set(['dt', 'dd'])],
['dd', new Set(['dt', 'dd'])],
@ -64,7 +64,7 @@ const disallowedContents = new Map([
['th', new Set(['td', 'th', 'tr'])],
]);
function parentIsHead(stack) {
function parent_is_head(stack) {
let i = stack.length;
while (i--) {
const { type } = stack[i];
@ -80,7 +80,7 @@ export default function tag(parser: Parser) {
let parent = parser.current();
if (parser.eat('!--')) {
const data = parser.readUntil(/-->/);
const data = parser.read_until(/-->/);
parser.eat('-->', true, 'comment was left open, expected -->');
parser.current().children.push({
@ -93,13 +93,13 @@ export default function tag(parser: Parser) {
return;
}
const isClosingTag = parser.eat('/');
const is_closing_tag = parser.eat('/');
const name = readTagName(parser);
const name = read_tag_name(parser);
if (metaTags.has(name)) {
const slug = metaTags.get(name).toLowerCase();
if (isClosingTag) {
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
@ -110,7 +110,7 @@ export default function tag(parser: Parser) {
}, parser.current().children[0].start);
}
} else {
if (name in parser.metaTags) {
if (name in parser.meta_tags) {
parser.error({
code: `duplicate-${slug}`,
message: `A component can only have one <${name}> tag`
@ -124,14 +124,14 @@ export default function tag(parser: Parser) {
}, start);
}
parser.metaTags[name] = true;
parser.meta_tags[name] = true;
}
}
const type = metaTags.has(name)
? metaTags.get(name)
const type = meta_tags.has(name)
? meta_tags.get(name)
: (/[A-Z]/.test(name[0]) || name === 'svelte:self' || name === 'svelte:component') ? 'InlineComponent'
: name === 'title' && parentIsHead(parser.stack) ? 'Title'
: name === 'title' && parent_is_head(parser.stack) ? 'Title'
: name === 'slot' && !parser.customElement ? 'Slot' : 'Element';
const element: Node = {
@ -143,9 +143,9 @@ export default function tag(parser: Parser) {
children: [],
};
parser.allowWhitespace();
parser.allow_whitespace();
if (isClosingTag) {
if (is_closing_tag) {
if (is_void(name)) {
parser.error({
code: `invalid-void-content`,
@ -173,42 +173,21 @@ export default function tag(parser: Parser) {
parser.stack.pop();
return;
} else if (disallowedContents.has(parent.name)) {
} 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 (disallowedContents.get(parent.name).has(name)) {
if (disallowed_contents.get(parent.name).has(name)) {
parent.end = start;
parser.stack.pop();
}
}
// TODO should this still error in in web component mode?
// if (name === 'slot') {
// let i = parser.stack.length;
// while (i--) {
// const item = parser.stack[i];
// if (item.type === 'EachBlock') {
// parser.error({
// code: `invalid-slot-placement`,
// message: `<slot> cannot be a child of an each-block`
// }, start);
// }
// }
// }
const uniqueNames = new Set();
const unique_names = new Set();
let attribute;
while ((attribute = readAttribute(parser, uniqueNames))) {
if (attribute.type === 'Binding' && !parser.allowBindings) {
parser.error({
code: `binding-disabled`,
message: `Two-way binding is disabled`
}, attribute.start);
}
while ((attribute = readAttribute(parser, unique_names))) {
element.attributes.push(attribute);
parser.allowWhitespace();
parser.allow_whitespace();
}
if (name === 'svelte:component') {
@ -244,16 +223,16 @@ export default function tag(parser: Parser) {
parser.current().children.push(element);
const selfClosing = parser.eat('/') || is_void(name);
const self_closing = parser.eat('/') || is_void(name);
parser.eat('>', true);
if (selfClosing) {
if (self_closing) {
// don't push self-closing elements onto the stack
element.end = parser.index;
} else if (name === 'textarea') {
// special case
element.children = readSequence(
element.children = read_sequence(
parser,
() =>
parser.template.slice(parser.index, parser.index + 11) === '</textarea>'
@ -263,7 +242,7 @@ export default function tag(parser: Parser) {
} else if (name === 'script') {
// special case
const start = parser.index;
const data = parser.readUntil(/<\/script>/);
const data = parser.read_until(/<\/script>/);
const end = parser.index;
element.children.push({ start, end, type: 'Text', data });
parser.eat('</script>', true);
@ -271,7 +250,7 @@ export default function tag(parser: Parser) {
} else if (name === 'style') {
// special case
const start = parser.index;
const data = parser.readUntil(/<\/style>/);
const data = parser.read_until(/<\/style>/);
const end = parser.index;
element.children.push({ start, end, type: 'Text', data });
parser.eat('</style>', true);
@ -280,7 +259,7 @@ export default function tag(parser: Parser) {
}
}
function readTagName(parser: Parser) {
function read_tag_name(parser: Parser) {
const start = parser.index;
if (parser.read(SELF)) {
@ -309,9 +288,9 @@ function readTagName(parser: Parser) {
if (parser.read(COMPONENT)) return 'svelte:component';
const name = parser.readUntil(/(\s|\/|>)/);
const name = parser.read_until(/(\s|\/|>)/);
if (metaTags.has(name)) return name;
if (meta_tags.has(name)) return name;
if (name.startsWith('svelte:')) {
const match = fuzzymatch(name.slice(7), valid_meta_tags);
@ -325,7 +304,7 @@ function readTagName(parser: Parser) {
}, start);
}
if (!validTagName.test(name)) {
if (!valid_tag_name.test(name)) {
parser.error({
code: `invalid-tag-name`,
message: `Expected valid tag name`
@ -335,16 +314,16 @@ function readTagName(parser: Parser) {
return name;
}
function readAttribute(parser: Parser, uniqueNames: Set<string>) {
function readAttribute(parser: Parser, unique_names: Set<string>) {
const start = parser.index;
if (parser.eat('{')) {
parser.allowWhitespace();
parser.allow_whitespace();
if (parser.eat('...')) {
const expression = readExpression(parser);
const expression = read_expression(parser);
parser.allowWhitespace();
parser.allow_whitespace();
parser.eat('}', true);
return {
@ -354,10 +333,10 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
expression
};
} else {
const valueStart = parser.index;
const value_start = parser.index;
const name = parser.readIdentifier();
parser.allowWhitespace();
const name = parser.read_identifier();
parser.allow_whitespace();
parser.eat('}', true);
return {
@ -366,12 +345,12 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
type: 'Attribute',
name,
value: [{
start: valueStart,
end: valueStart + name.length,
start: value_start,
end: value_start + name.length,
type: 'AttributeShorthand',
expression: {
start: valueStart,
end: valueStart + name.length,
start: value_start,
end: value_start + name.length,
type: 'Identifier',
name
}
@ -380,27 +359,27 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
}
}
let name = parser.readUntil(/(\s|=|\/|>)/);
let name = parser.read_until(/(\s|=|\/|>)/);
if (!name) return null;
if (uniqueNames.has(name)) {
if (unique_names.has(name)) {
parser.error({
code: `duplicate-attribute`,
message: 'Attributes need to be unique'
}, start);
}
uniqueNames.add(name);
unique_names.add(name);
let end = parser.index;
parser.allowWhitespace();
parser.allow_whitespace();
const colon_index = name.indexOf(':');
const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
let value: any[] | true = true;
if (parser.eat('=')) {
value = readAttributeValue(parser);
value = read_attribute_value(parser);
end = parser.index;
}
@ -470,23 +449,23 @@ function get_directive_type(name) {
if (name === 'in' || name === 'out' || name === 'transition') return 'Transition';
}
function readAttributeValue(parser: Parser) {
const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
function read_attribute_value(parser: Parser) {
const quote_mark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
const regex = (
quoteMark === `'` ? /'/ :
quoteMark === `"` ? /"/ :
quote_mark === `'` ? /'/ :
quote_mark === `"` ? /"/ :
/(\/>|[\s"'=<>`])/
);
const value = readSequence(parser, () => !!parser.matchRegex(regex));
const value = read_sequence(parser, () => !!parser.match_regex(regex));
if (quoteMark) parser.index += 1;
if (quote_mark) parser.index += 1;
return value;
}
function readSequence(parser: Parser, done: () => boolean) {
let currentChunk: Node = {
function read_sequence(parser: Parser, done: () => boolean) {
let current_chunk: Node = {
start: parser.index,
end: null,
type: 'Text',
@ -499,25 +478,25 @@ function readSequence(parser: Parser, done: () => boolean) {
const index = parser.index;
if (done()) {
currentChunk.end = parser.index;
current_chunk.end = parser.index;
if (currentChunk.data) chunks.push(currentChunk);
if (current_chunk.data) chunks.push(current_chunk);
chunks.forEach(chunk => {
if (chunk.type === 'Text')
chunk.data = decodeCharacterReferences(chunk.data);
chunk.data = decode_character_references(chunk.data);
});
return chunks;
} else if (parser.eat('{')) {
if (currentChunk.data) {
currentChunk.end = index;
chunks.push(currentChunk);
if (current_chunk.data) {
current_chunk.end = index;
chunks.push(current_chunk);
}
parser.allowWhitespace();
const expression = readExpression(parser);
parser.allowWhitespace();
parser.allow_whitespace();
const expression = read_expression(parser);
parser.allow_whitespace();
parser.eat('}', true);
chunks.push({
@ -527,14 +506,14 @@ function readSequence(parser: Parser, done: () => boolean) {
expression,
});
currentChunk = {
current_chunk = {
start: parser.index,
end: null,
type: 'Text',
data: '',
};
} else {
currentChunk.data += parser.template[parser.index++];
current_chunk.data += parser.template[parser.index++];
}
}

@ -1,4 +1,4 @@
import { decodeCharacterReferences } from '../utils/html';
import { decode_character_references } from '../utils/html';
import { Parser } from '../index';
export default function text(parser: Parser) {
@ -18,6 +18,6 @@ export default function text(parser: Parser) {
start,
end: parser.index,
type: 'Text',
data: decodeCharacterReferences(data),
data: decode_character_references(data),
});
}

@ -1,6 +1,6 @@
import htmlEntities from './entities';
import entities from './entities';
const windows1252 = [
const windows_1252 = [
8364,
129,
8218,
@ -34,18 +34,19 @@ const windows1252 = [
382,
376,
];
const entityPattern = new RegExp(
`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(htmlEntities).join('|')}));?`,
const entity_pattern = new RegExp(
`&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}));?`,
'g'
);
export function decodeCharacterReferences(html: string) {
return html.replace(entityPattern, (match, entity) => {
export function decode_character_references(html: string) {
return html.replace(entity_pattern, (match, entity) => {
let code;
// Handle named entities
if (entity[0] !== '#') {
code = htmlEntities[entity];
code = entities[entity];
} else if (entity[1] === 'x') {
code = parseInt(entity.substring(2), 16);
} else {
@ -56,7 +57,7 @@ export function decodeCharacterReferences(html: string) {
return match;
}
return String.fromCodePoint(validateCode(code));
return String.fromCodePoint(validate_code(code));
});
}
@ -67,7 +68,7 @@ const NUL = 0;
// to replace them ourselves
//
// Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters
function validateCode(code: number) {
function validate_code(code: number) {
// line feed becomes generic whitespace
if (code === 10) {
return 32;
@ -81,7 +82,7 @@ function validateCode(code: number) {
// code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need
// to correct the mistake or we'll end up with missing € signs and so on
if (code <= 159) {
return windows1252[code - 128];
return windows_1252[code - 128];
}
// basic multilingual plane

@ -21,17 +21,17 @@ interface Processed {
dependencies?: string[];
}
function parseAttributeValue(value: string) {
function parse_attribute_value(value: string) {
return /^['"]/.test(value) ?
value.slice(1, -1) :
value;
}
function parseAttributes(str: string) {
function parse_attributes(str: string) {
const attrs = {};
str.split(/\s+/).filter(Boolean).forEach(attr => {
const [name, value] = attr.split('=');
attrs[name] = value ? parseAttributeValue(value) : true;
attrs[name] = value ? parse_attribute_value(value) : true;
});
return attrs;
}
@ -99,7 +99,7 @@ export default async function preprocess(
async (match, attributes, content) => {
const processed: Processed = await fn({
content,
attributes: parseAttributes(attributes),
attributes: parse_attributes(attributes),
filename
});
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
@ -115,7 +115,7 @@ export default async function preprocess(
async (match, attributes, content) => {
const processed: Processed = await fn({
content,
attributes: parseAttributes(attributes),
attributes: parse_attributes(attributes),
filename
});
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);

@ -1,13 +1,13 @@
import { whitespace } from './patterns';
export function trimStart(str: string) {
export function trim_start(str: string) {
let i = 0;
while (whitespace.test(str[i])) i += 1;
return str.slice(i);
}
export function trimEnd(str: string) {
export function trim_end(str: string) {
let i = str.length;
while (whitespace.test(str[i - 1])) i -= 1;

@ -1,22 +0,0 @@
export default function unpackDestructuring(
contexts: Array<{ name: string, tail: string }>,
node: Node,
tail: string
) {
if (!node) return;
if (node.type === 'Identifier') {
contexts.push({
key: node,
tail
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
unpackDestructuring(contexts, element, `${tail}[${i}]`);
});
} else if (node.type === 'ObjectPattern') {
node.properties.forEach((property) => {
unpackDestructuring(contexts, property.value, `${tail}.${property.key.name}`);
});
}
}
Loading…
Cancel
Save