more tidying up

pull/2252/head
Richard Harris 7 years ago
parent 6ffb1ea00b
commit cc92f8fdcd

@ -15,7 +15,7 @@ export default class ReplProxy {
}
iframeCommand(command, args) {
return new Promise( (resolve, reject) => {
return new Promise((resolve, reject) => {
this.cmdId += 1;
this.pendingCmds.set(this.cmdId, { resolve, reject });
@ -23,7 +23,7 @@ export default class ReplProxy {
action: command,
cmdId: this.cmdId,
args
}, '*')
}, '*');
});
}

@ -3,8 +3,8 @@ import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character';
import Stats from '../Stats';
import { globals, reserved } from '../utils/names';
import { namespaces, validNamespaces } from '../utils/namespaces';
import wrapModule from './wrapModule';
import { namespaces, valid_namespaces } from '../utils/namespaces';
import create_module from './create_module';
import { create_scopes, extract_names, Scope } from './utils/scope';
import Stylesheet from './css/Stylesheet';
import { test } from '../config';
@ -70,13 +70,13 @@ export default class Component {
source: string;
code: MagicString;
name: string;
compileOptions: CompileOptions;
compile_options: CompileOptions;
fragment: Fragment;
module_scope: Scope;
instance_scope: Scope;
instance_scope_map: WeakMap<Node, Scope>;
componentOptions: ComponentOptions;
component_options: ComponentOptions;
namespace: string;
tag: string;
accessors: boolean;
@ -98,7 +98,7 @@ export default class Component {
injected_reactive_declaration_vars: Set<string> = new Set();
helpers: Set<string> = new Set();
indirectDependencies: Map<string, Set<string>> = new Map();
indirect_dependencies: Map<string, Set<string>> = new Map();
file: string;
locate: (c: number) => { line: number, column: number };
@ -112,13 +112,13 @@ export default class Component {
stylesheet: Stylesheet;
aliases: Map<string, string> = new Map();
usedNames: Set<string> = new Set();
used_names: Set<string> = new Set();
constructor(
ast: Ast,
source: string,
name: string,
compileOptions: CompileOptions,
compile_options: CompileOptions,
stats: Stats,
warnings: Warning[]
) {
@ -128,24 +128,24 @@ export default class Component {
this.warnings = warnings;
this.ast = ast;
this.source = source;
this.compileOptions = compileOptions;
this.compile_options = compile_options;
this.file = compileOptions.filename && (
typeof process !== 'undefined' ? compileOptions.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : compileOptions.filename
this.file = compile_options.filename && (
typeof process !== 'undefined' ? compile_options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : compile_options.filename
);
this.locate = getLocator(this.source);
this.code = new MagicString(source);
// styles
this.stylesheet = new Stylesheet(source, ast, compileOptions.filename, compileOptions.dev);
this.stylesheet = new Stylesheet(source, ast, compile_options.filename, compile_options.dev);
this.stylesheet.validate(this);
this.componentOptions = process_component_options(this, this.ast.html.children);
this.namespace = namespaces[this.componentOptions.namespace] || this.componentOptions.namespace;
this.component_options = process_component_options(this, this.ast.html.children);
this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace;
if (compileOptions.customElement) {
this.tag = this.componentOptions.tag || compileOptions.tag;
if (compile_options.customElement) {
this.tag = this.component_options.tag || compile_options.tag;
if (!this.tag) {
throw new Error(`Cannot compile to a custom element without specifying a tag name via options.tag or <svelte:options>`);
}
@ -157,11 +157,11 @@ export default class Component {
this.walk_instance_js_pre_template();
this.fragment = new Fragment(this, ast.html);
this.name = this.getUniqueName(name);
this.name = this.get_unique_name(name);
this.walk_instance_js_post_template();
if (!compileOptions.customElement) this.stylesheet.reify();
if (!compile_options.customElement) this.stylesheet.reify();
this.stylesheet.warnOnUnusedSelectors(this);
}
@ -197,11 +197,11 @@ export default class Component {
const variable = this.var_lookup.get(subscribable_name);
if (variable) variable.subscribable = true;
} else {
this.usedNames.add(name);
this.used_names.add(name);
}
}
addSourcemapLocations(node: Node) {
add_sourcemap_locations(node: Node) {
walk(node, {
enter: (node: Node) => {
this.code.addSourcemapLocation(node.start);
@ -212,7 +212,7 @@ export default class Component {
alias(name: string) {
if (!this.aliases.has(name)) {
this.aliases.set(name, this.getUniqueName(name));
this.aliases.set(name, this.get_unique_name(name));
}
return this.aliases.get(name);
@ -228,17 +228,17 @@ export default class Component {
let css = null;
if (result) {
const { compileOptions, name } = this;
const { format = 'esm' } = compileOptions;
const { compile_options, name } = this;
const { format = 'esm' } = compile_options;
const banner = `/* ${this.file ? `${this.file} ` : ``}generated by Svelte v${"__VERSION__"} */`;
result = result
.replace(/__svelte:self__/g, this.name)
.replace(compileOptions.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => {
.replace(compile_options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => {
if (sigil === '@') {
if (internal_exports.has(name)) {
if (compileOptions.dev && internal_exports.has(`${name}Dev`)) name = `${name}Dev`;
if (compile_options.dev && internal_exports.has(`${name}Dev`)) name = `${name}Dev`;
this.helpers.add(name);
}
@ -248,21 +248,20 @@ export default class Component {
return sigil.slice(1) + name;
});
const importedHelpers = Array.from(this.helpers)
const imported_helpers = Array.from(this.helpers)
.sort()
.map(name => {
const alias = this.alias(name);
return { name, alias };
});
const module = wrapModule(
const module = create_module(
result,
format,
name,
compileOptions,
banner,
compileOptions.sveltePath,
importedHelpers,
compile_options.sveltePath,
imported_helpers,
this.imports,
this.vars.filter(variable => variable.module && variable.export_name).map(variable => ({
name: variable.name,
@ -272,17 +271,17 @@ export default class Component {
);
const parts = module.split('✂]');
const finalChunk = parts.pop();
const final_chunk = parts.pop();
const compiled = new Bundle({ separator: '' });
function addString(str: string) {
function add_string(str: string) {
compiled.addSource({
content: new MagicString(str),
});
}
const { filename } = compileOptions;
const { filename } = compile_options;
// special case — the source file doesn't actually get used anywhere. we need
// to add an empty file to populate map.sources and map.sourcesContent
@ -297,7 +296,7 @@ export default class Component {
parts.forEach((str: string) => {
const chunk = str.replace(pattern, '');
if (chunk) addString(chunk);
if (chunk) add_string(chunk);
const match = pattern.exec(str);
@ -309,17 +308,17 @@ export default class Component {
});
});
addString(finalChunk);
add_string(final_chunk);
css = compileOptions.customElement ?
css = compile_options.customElement ?
{ code: null, map: null } :
this.stylesheet.render(compileOptions.cssOutputFilename, true);
this.stylesheet.render(compile_options.cssOutputFilename, true);
js = {
code: compiled.toString(),
map: compiled.generateMap({
includeContent: true,
file: compileOptions.outputFilename,
file: compile_options.outputFilename,
})
};
}
@ -343,25 +342,25 @@ export default class Component {
};
}
getUniqueName(name: string) {
get_unique_name(name: string) {
if (test) name = `${name}$`;
let alias = name;
for (
let i = 1;
reserved.has(alias) ||
this.var_lookup.has(alias) ||
this.usedNames.has(alias);
this.used_names.has(alias);
alias = `${name}_${i++}`
);
this.usedNames.add(alias);
this.used_names.add(alias);
return alias;
}
getUniqueNameMaker() {
const localUsedNames = new Set();
get_unique_name_maker() {
const local_used_names = new Set();
function add(name: string) {
localUsedNames.add(name);
local_used_names.add(name);
}
reserved.forEach(add);
@ -373,11 +372,11 @@ export default class Component {
let alias = name;
for (
let i = 1;
this.usedNames.has(alias) ||
localUsedNames.has(alias);
this.used_names.has(alias) ||
local_used_names.has(alias);
alias = `${name}_${i++}`
);
localUsedNames.add(alias);
local_used_names.add(alias);
return alias;
};
}
@ -398,7 +397,7 @@ export default class Component {
source: this.source,
start: pos.start,
end: pos.end,
filename: this.compileOptions.filename
filename: this.compile_options.filename
});
}
@ -428,7 +427,7 @@ export default class Component {
start,
end,
pos: pos.start,
filename: this.compileOptions.filename,
filename: this.compile_options.filename,
toString: () => `${warning.message} (${start.line + 1}:${start.column})\n${frame}`,
});
}
@ -536,7 +535,7 @@ export default class Component {
const script = this.ast.module;
if (!script) return;
this.addSourcemapLocations(script.content);
this.add_sourcemap_locations(script.content);
let { scope, globals } = create_scopes(script.content);
this.module_scope = scope;
@ -581,7 +580,7 @@ export default class Component {
const script = this.ast.instance;
if (!script) return;
this.addSourcemapLocations(script.content);
this.add_sourcemap_locations(script.content);
// inject vars for reactive declarations
script.content.body.forEach(node => {
@ -759,7 +758,7 @@ export default class Component {
rewrite_props(get_insert: (variable: Var) => string) {
const component = this;
const { code, instance_scope, instance_scope_map: map, componentOptions } = this;
const { code, instance_scope, instance_scope_map: map, component_options } = this;
let scope = instance_scope;
const coalesced_declarations = [];
@ -1189,11 +1188,11 @@ export default class Component {
}
function process_component_options(component: Component, nodes) {
const componentOptions: ComponentOptions = {
immutable: component.compileOptions.immutable || false,
accessors: 'accessors' in component.compileOptions
? component.compileOptions.accessors
: !!component.compileOptions.customElement
const component_options: ComponentOptions = {
immutable: component.compile_options.immutable || false,
accessors: 'accessors' in component.compile_options
? component.compile_options.accessors
: !!component.compile_options.customElement
};
const node = nodes.find(node => node.name === 'svelte:options');
@ -1237,7 +1236,7 @@ function process_component_options(component: Component, nodes) {
});
}
componentOptions.tag = tag;
component_options.tag = tag;
break;
}
@ -1248,8 +1247,8 @@ function process_component_options(component: Component, nodes) {
if (typeof ns !== 'string') component.error(attribute, { code, message });
if (validNamespaces.indexOf(ns) === -1) {
const match = fuzzymatch(ns, validNamespaces);
if (valid_namespaces.indexOf(ns) === -1) {
const match = fuzzymatch(ns, valid_namespaces);
if (match) {
component.error(attribute, {
code: `invalid-namespace-property`,
@ -1263,7 +1262,7 @@ function process_component_options(component: Component, nodes) {
}
}
componentOptions.namespace = ns;
component_options.namespace = ns;
break;
}
@ -1275,7 +1274,7 @@ function process_component_options(component: Component, nodes) {
if (typeof value !== 'boolean') component.error(attribute, { code, message });
componentOptions[name] = value;
component_options[name] = value;
break;
default:
@ -1295,5 +1294,5 @@ function process_component_options(component: Component, nodes) {
});
}
return componentOptions;
return component_options;
}

@ -1,14 +1,8 @@
import deindent from './utils/deindent';
import list from '../utils/list';
import { CompileOptions, ModuleFormat, Node } from '../interfaces';
import { ModuleFormat, Node } from '../interfaces';
import { stringify_props } from './utils/stringify_props';
interface Dependency {
name: string;
statements: string[];
source: string;
}
const wrappers = { esm, cjs };
type Export = {
@ -16,11 +10,10 @@ type Export = {
as: string;
};
export default function wrapModule(
export default function create_module(
code: string,
format: ModuleFormat,
name: string,
options: CompileOptions,
banner: string,
sveltePath = 'svelte',
helpers: { name: string, alias: string }[],
@ -28,18 +21,18 @@ export default function wrapModule(
module_exports: Export[],
source: string
): string {
const internalPath = `${sveltePath}/internal`;
const internal_path = `${sveltePath}/internal`;
if (format === 'esm') {
return esm(code, name, options, banner, sveltePath, internalPath, helpers, imports, module_exports, source);
return esm(code, name, banner, sveltePath, internal_path, helpers, imports, module_exports, source);
}
if (format === 'cjs') return cjs(code, name, banner, sveltePath, internalPath, helpers, imports, module_exports);
if (format === 'cjs') return cjs(code, name, banner, sveltePath, internal_path, helpers, imports, module_exports);
throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`);
}
function editSource(source, sveltePath) {
function edit_source(source, sveltePath) {
return source === 'svelte' || source.startsWith('svelte/')
? source.replace('svelte', sveltePath)
: source;
@ -48,23 +41,22 @@ function editSource(source, sveltePath) {
function esm(
code: string,
name: string,
options: CompileOptions,
banner: string,
sveltePath: string,
internalPath: string,
internal_path: string,
helpers: { name: string, alias: string }[],
imports: Node[],
module_exports: Export[],
source: string
) {
const importHelpers = helpers.length > 0 && (
`import ${stringify_props(helpers.map(h => h.name === h.alias ? h.name : `${h.name} as ${h.alias}`).sort())} from ${JSON.stringify(internalPath)};`
const internal_imports = helpers.length > 0 && (
`import ${stringify_props(helpers.map(h => h.name === h.alias ? h.name : `${h.name} as ${h.alias}`).sort())} from ${JSON.stringify(internal_path)};`
);
const importBlock = imports.length > 0 && (
const user_imports = imports.length > 0 && (
imports
.map((declaration: Node) => {
const import_source = editSource(declaration.source.value, sveltePath);
const import_source = edit_source(declaration.source.value, sveltePath);
return (
source.slice(declaration.start, declaration.source.start) +
@ -77,8 +69,8 @@ function esm(
return deindent`
${banner}
${importHelpers}
${importBlock}
${internal_imports}
${user_imports}
${code}
@ -91,15 +83,15 @@ function cjs(
name: string,
banner: string,
sveltePath: string,
internalPath: string,
internal_path: string,
helpers: { name: string, alias: string }[],
imports: Node[],
module_exports: Export[]
) {
const helperDeclarations = helpers.map(h => `${h.alias === h.name ? h.name : `${h.name}: ${h.alias}`}`).sort();
const declarations = helpers.map(h => `${h.alias === h.name ? h.name : `${h.name}: ${h.alias}`}`).sort();
const helperBlock = helpers.length > 0 && (
`const ${stringify_props(helperDeclarations)} = require(${JSON.stringify(internalPath)});\n`
const internal_imports = helpers.length > 0 && (
`const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n`
);
const requires = imports.map(node => {
@ -121,7 +113,7 @@ function cjs(
lhs = `{ ${properties.join(', ')} }`;
}
const source = editSource(node.source.value, sveltePath);
const source = edit_source(node.source.value, sveltePath);
return `const ${lhs} = require("${source}");`
});
@ -134,7 +126,7 @@ function cjs(
${banner}
"use strict";
${helperBlock}
${internal_imports}
${requires}
${code}

@ -1,6 +1,6 @@
import MagicString from 'magic-string';
import Stylesheet from './Stylesheet';
import { gatherPossibleValues, UNKNOWN } from './gatherPossibleValues';
import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { Node } from '../interfaces';
import Component from '../compile/Component';
@ -8,14 +8,14 @@ export default class Selector {
node: Node;
stylesheet: Stylesheet;
blocks: Block[];
localBlocks: Block[];
local_blocks: Block[];
used: boolean;
constructor(node: Node, stylesheet: Stylesheet) {
this.node = node;
this.stylesheet = stylesheet;
this.blocks = groupSelectors(node);
this.blocks = group_selectors(node);
// take trailing :global(...) selectors out of consideration
let i = this.blocks.length;
@ -24,19 +24,19 @@ export default class Selector {
i -= 1;
}
this.localBlocks = this.blocks.slice(0, i);
this.local_blocks = this.blocks.slice(0, i);
this.used = this.blocks[0].global;
}
apply(node: Node, stack: Node[]) {
const toEncapsulate: Node[] = [];
const to_encapsulate: Node[] = [];
applySelector(this.stylesheet, this.localBlocks.slice(), node, stack.slice(), toEncapsulate);
apply_selector(this.stylesheet, this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
if (toEncapsulate.length > 0) {
toEncapsulate.filter((_, i) => i === 0 || i === toEncapsulate.length - 1).forEach(({ node, block }) => {
this.stylesheet.nodesWithCssClass.add(node);
block.shouldEncapsulate = true;
if (to_encapsulate.length > 0) {
to_encapsulate.filter((_, i) => i === 0 || i === to_encapsulate.length - 1).forEach(({ node, block }) => {
this.stylesheet.nodes_with_css_class.add(node);
block.should_encapsulate = true;
});
this.used = true;
@ -57,7 +57,7 @@ export default class Selector {
}
transform(code: MagicString, attr: string) {
function encapsulateBlock(block: Block) {
function encapsulate_block(block: Block) {
let i = block.selectors.length;
while (i--) {
const selector = block.selectors[i];
@ -81,7 +81,7 @@ export default class Selector {
code.remove(selector.start, first.start).remove(last.end, selector.end);
}
if (block.shouldEncapsulate) encapsulateBlock(block);
if (block.should_encapsulate) encapsulate_block(block);
});
}
@ -121,7 +121,7 @@ export default class Selector {
}
}
function applySelector(stylesheet: Stylesheet, blocks: Block[], node: Node, stack: Node[], toEncapsulate: any[]): boolean {
function apply_selector(stylesheet: Stylesheet, blocks: Block[], node: Node, stack: Node[], to_encapsulate: any[]): boolean {
const block = blocks.pop();
if (!block) return false;
@ -145,15 +145,15 @@ function applySelector(stylesheet: Stylesheet, blocks: Block[], node: Node, stac
}
if (selector.type === 'ClassSelector') {
if (!attributeMatches(node, 'class', selector.name, '~=', false) && !classMatches(node, selector.name)) return false;
if (!attribute_matches(node, 'class', selector.name, '~=', false) && !class_matches(node, selector.name)) return false;
}
else if (selector.type === 'IdSelector') {
if (!attributeMatches(node, 'id', selector.name, '=', false)) return false;
if (!attribute_matches(node, 'id', selector.name, '=', false)) return false;
}
else if (selector.type === 'AttributeSelector') {
if (!attributeMatches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) return false;
if (!attribute_matches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) return false;
}
else if (selector.type === 'TypeSelector') {
@ -163,7 +163,7 @@ function applySelector(stylesheet: Stylesheet, blocks: Block[], node: Node, stac
else {
// bail. TODO figure out what these could be
toEncapsulate.push({ node, block });
to_encapsulate.push({ node, block });
return true;
}
}
@ -171,21 +171,21 @@ function applySelector(stylesheet: Stylesheet, blocks: Block[], node: Node, stac
if (block.combinator) {
if (block.combinator.type === 'WhiteSpace') {
while (stack.length) {
if (applySelector(stylesheet, blocks.slice(), stack.pop(), stack, toEncapsulate)) {
toEncapsulate.push({ node, block });
if (apply_selector(stylesheet, blocks.slice(), stack.pop(), stack, to_encapsulate)) {
to_encapsulate.push({ node, block });
return true;
}
}
if (blocks.every(block => block.global)) {
toEncapsulate.push({ node, block });
to_encapsulate.push({ node, block });
return true;
}
return false;
} else if (block.combinator.name === '>') {
if (applySelector(stylesheet, blocks, stack.pop(), stack, toEncapsulate)) {
toEncapsulate.push({ node, block });
if (apply_selector(stylesheet, blocks, stack.pop(), stack, to_encapsulate)) {
to_encapsulate.push({ node, block });
return true;
}
@ -193,11 +193,11 @@ function applySelector(stylesheet: Stylesheet, blocks: Block[], node: Node, stac
}
// TODO other combinators
toEncapsulate.push({ node, block });
to_encapsulate.push({ node, block });
return true;
}
toEncapsulate.push({ node, block });
to_encapsulate.push({ node, block });
return true;
}
@ -210,43 +210,39 @@ const operators = {
'*=': (value: string, flags: string) => new RegExp(value, flags)
};
function attributeMatches(node: Node, name: string, expectedValue: string, operator: string, caseInsensitive: boolean) {
function attribute_matches(node: Node, name: string, expected_value: string, operator: string, caseInsensitive: boolean) {
const spread = node.attributes.find(attr => attr.type === 'Spread');
if (spread) return true;
const attr = node.attributes.find((attr: Node) => attr.name === name);
if (!attr) return false;
if (attr.isTrue) return operator === null;
if (attr.is_true) return operator === null;
if (attr.chunks.length > 1) return true;
if (!expectedValue) return true;
if (!expected_value) return true;
const pattern = operators[operator](expectedValue, caseInsensitive ? 'i' : '');
const pattern = operators[operator](expected_value, caseInsensitive ? 'i' : '');
const value = attr.chunks[0];
if (!value) return false;
if (value.type === 'Text') return pattern.test(value.data);
const possibleValues = new Set();
gatherPossibleValues(value.node, possibleValues);
if (possibleValues.has(UNKNOWN)) return true;
const possible_values = new Set();
gather_possible_values(value.node, possible_values);
if (possible_values.has(UNKNOWN)) return true;
for (const x of Array.from(possibleValues)) { // TypeScript for-of is slightly unlike JS
for (const x of Array.from(possible_values)) { // TypeScript for-of is slightly unlike JS
if (pattern.test(x)) return true;
}
return false;
}
function classMatches(node, name: string) {
return node.classes.some(function(classDir) {
return classDir.name === name;
function class_matches(node, name: string) {
return node.classes.some(function(class_directive) {
return class_directive.name === name;
});
}
function isDynamic(value: Node) {
return value.length > 1 || value[0].type !== 'Text';
}
function unquote(value: Node) {
if (value.type === 'Identifier') return value.name;
const str = value.value;
@ -262,7 +258,7 @@ class Block {
selectors: Node[]
start: number;
end: number;
shouldEncapsulate: boolean;
should_encapsulate: boolean;
constructor(combinator: Node) {
this.combinator = combinator;
@ -272,7 +268,7 @@ class Block {
this.start = null;
this.end = null;
this.shouldEncapsulate = false;
this.should_encapsulate = false;
}
add(selector: Node) {
@ -286,7 +282,7 @@ class Block {
}
}
function groupSelectors(selector: Node) {
function group_selectors(selector: Node) {
let block: Block = new Block(null);
const blocks = [block];

@ -9,7 +9,7 @@ function remove_css_prefox(name: string): string {
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
}
const isKeyframesNode = (node: Node) => remove_css_prefox(node.name) === 'keyframes';
const is_keyframes_node = (node: Node) => remove_css_prefox(node.name) === 'keyframes';
// https://github.com/darkskyapp/string-hash/blob/master/index.js
function hash(str: string): string {
@ -37,8 +37,8 @@ class Rule {
this.selectors.forEach(selector => selector.apply(node, stack)); // TODO move the logic in here?
}
isUsed(dev: boolean) {
if (this.parent && this.parent.node.type === 'Atrule' && isKeyframesNode(this.parent.node)) return true;
is_used(dev: boolean) {
if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node)) return true;
if (this.declarations.length === 0) return dev;
return this.selectors.some(s => s.used);
}
@ -79,7 +79,7 @@ class Rule {
}
transform(code: MagicString, id: string, keyframes: Map<string, string>) {
if (this.parent && this.parent.node.type === 'Atrule' && isKeyframesNode(this.parent.node)) return true;
if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node)) return true;
const attr = `.${id}`;
@ -93,7 +93,7 @@ class Rule {
});
}
warnOnUnusedSelector(handler: (selector: Selector) => void) {
warn_on_unused_selector(handler: (selector: Selector) => void) {
this.selectors.forEach(selector => {
if (!selector.used) handler(selector);
});
@ -154,7 +154,7 @@ class Atrule {
});
}
else if (isKeyframesNode(this.node)) {
else if (is_keyframes_node(this.node)) {
this.children.forEach((rule: Rule) => {
rule.selectors.forEach(selector => {
selector.used = true;
@ -163,14 +163,14 @@ class Atrule {
}
}
isUsed(dev: boolean) {
is_used(dev: boolean) {
return true; // TODO
}
minify(code: MagicString, dev: boolean) {
if (this.node.name === 'media') {
const expressionChar = code.original[this.node.expression.start];
let c = this.node.start + (expressionChar === '(' ? 6 : 7);
const expression_char = code.original[this.node.expression.start];
let c = this.node.start + (expression_char === '(' ? 6 : 7);
if (this.node.expression.start > c) code.remove(c, this.node.expression.start);
this.node.expression.children.forEach((query: Node) => {
@ -179,7 +179,7 @@ class Atrule {
});
code.remove(c, this.node.block.start);
} else if (isKeyframesNode(this.node)) {
} else if (is_keyframes_node(this.node)) {
let c = this.node.start + this.node.name.length + 1;
if (this.node.expression.start - c > 1) code.overwrite(c, this.node.expression.start, ' ');
c = this.node.expression.end;
@ -200,7 +200,7 @@ class Atrule {
let c = this.node.block.start + 1;
this.children.forEach(child => {
if (child.isUsed(dev)) {
if (child.is_used(dev)) {
code.remove(c, child.node.start);
child.minify(code, dev);
c = child.node.end;
@ -212,7 +212,7 @@ class Atrule {
}
transform(code: MagicString, id: string, keyframes: Map<string, string>) {
if (isKeyframesNode(this.node)) {
if (is_keyframes_node(this.node)) {
this.node.expression.children.forEach(({ type, name, start, end }: Node) => {
if (type === 'Identifier') {
if (name.startsWith('-global-')) {
@ -226,7 +226,7 @@ class Atrule {
this.children.forEach(child => {
child.transform(code, id, keyframes);
})
});
}
validate(component: Component) {
@ -235,31 +235,28 @@ class Atrule {
});
}
warnOnUnusedSelector(handler: (selector: Selector) => void) {
warn_on_unused_selector(handler: (selector: Selector) => void) {
if (this.node.name !== 'media') return;
this.children.forEach(child => {
child.warnOnUnusedSelector(handler);
child.warn_on_unused_selector(handler);
});
}
}
const keys = {};
export default class Stylesheet {
source: string;
ast: Ast;
filename: string;
dev: boolean;
hasStyles: boolean;
has_styles: boolean;
id: string;
children: (Rule|Atrule)[];
keyframes: Map<string, string>;
children: (Rule|Atrule)[] = [];
keyframes: Map<string, string> = new Map();
nodesWithCssClass: Set<Node>;
nodesWithRefCssClass: Map<String, Node>;
nodes_with_css_class: Set<Node> = new Set();
constructor(source: string, ast: Ast, filename: string, dev: boolean) {
this.source = source;
@ -267,19 +264,13 @@ export default class Stylesheet {
this.filename = filename;
this.dev = dev;
this.children = [];
this.keyframes = new Map();
this.nodesWithCssClass = new Set();
this.nodesWithRefCssClass = new Map();
if (ast.css && ast.css.children.length) {
this.id = `svelte-${hash(ast.css.content.styles)}`;
this.hasStyles = true;
this.has_styles = true;
const stack: (Rule | Atrule)[] = [];
let currentAtrule: Atrule = null;
let current_atrule: Atrule = null;
walk(ast.css, {
enter: (node: Node) => {
@ -293,13 +284,13 @@ export default class Stylesheet {
// possibly other future constructs)
if (last && !(last instanceof Atrule)) return;
if (currentAtrule) {
currentAtrule.children.push(atrule);
if (current_atrule) {
current_atrule.children.push(atrule);
} else {
this.children.push(atrule);
}
if (isKeyframesNode(node)) {
if (is_keyframes_node(node)) {
node.expression.children.forEach((expression: Node) => {
if (expression.type === 'Identifier' && !expression.name.startsWith('-global-')) {
this.keyframes.set(expression.name, `${this.id}-${expression.name}`);
@ -307,15 +298,15 @@ export default class Stylesheet {
});
}
currentAtrule = atrule;
current_atrule = atrule;
}
if (node.type === 'Rule') {
const rule = new Rule(node, this, currentAtrule);
const rule = new Rule(node, this, current_atrule);
stack.push(rule);
if (currentAtrule) {
currentAtrule.children.push(rule);
if (current_atrule) {
current_atrule.children.push(rule);
} else {
this.children.push(rule);
}
@ -324,16 +315,16 @@ export default class Stylesheet {
leave: (node: Node) => {
if (node.type === 'Rule' || node.type === 'Atrule') stack.pop();
if (node.type === 'Atrule') currentAtrule = stack[stack.length - 1] as Atrule;
if (node.type === 'Atrule') current_atrule = stack[stack.length - 1] as Atrule;
}
});
} else {
this.hasStyles = false;
this.has_styles = false;
}
}
apply(node: Element) {
if (!this.hasStyles) return;
if (!this.has_styles) return;
const stack: Element[] = [];
let parent: Node = node;
@ -348,13 +339,13 @@ export default class Stylesheet {
}
reify() {
this.nodesWithCssClass.forEach((node: Node) => {
node.addCssClass();
this.nodes_with_css_class.forEach((node: Node) => {
node.add_css_class();
});
}
render(cssOutputFilename: string, shouldTransformSelectors: boolean) {
if (!this.hasStyles) {
render(file: string, should_transform_selectors: boolean) {
if (!this.has_styles) {
return { code: null, map: null };
}
@ -367,7 +358,7 @@ export default class Stylesheet {
}
});
if (shouldTransformSelectors) {
if (should_transform_selectors) {
this.children.forEach((child: (Atrule|Rule)) => {
child.transform(code, this.id, this.keyframes);
});
@ -375,7 +366,7 @@ export default class Stylesheet {
let c = 0;
this.children.forEach(child => {
if (child.isUsed(this.dev)) {
if (child.is_used(this.dev)) {
code.remove(c, child.node.start);
child.minify(code, this.dev);
c = child.node.end;
@ -389,7 +380,7 @@ export default class Stylesheet {
map: code.generateMap({
includeContent: true,
source: this.filename,
file: cssOutputFilename
file
})
};
}
@ -402,7 +393,7 @@ export default class Stylesheet {
warnOnUnusedSelectors(component: Component) {
this.children.forEach(child => {
child.warnOnUnusedSelector((selector: Selector) => {
child.warn_on_unused_selector((selector: Selector) => {
component.warn(selector.node, {
code: `css-unused-selector`,
message: `Unused CSS selector`

@ -2,14 +2,14 @@ import { Node } from '../interfaces';
export const UNKNOWN = {};
export function gatherPossibleValues(node: Node, set: Set<string|{}>) {
export function gather_possible_values(node: Node, set: Set<string|{}>) {
if (node.type === 'Literal') {
set.add(node.value);
}
else if (node.type === 'ConditionalExpression') {
gatherPossibleValues(node.consequent, set);
gatherPossibleValues(node.alternate, set);
gather_possible_values(node.consequent, set);
gather_possible_values(node.alternate, set);
}
else {

@ -6,7 +6,7 @@ export default class Action extends Node {
type: 'Action';
name: string;
expression: Expression;
usesContext: boolean;
uses_context: boolean;
constructor(component: Component, parent, scope, info) {
super(component, parent, scope, info);
@ -20,6 +20,6 @@ export default class Action extends Node {
? new Expression(component, this, scope, info.expression)
: null;
this.usesContext = this.expression && this.expression.usesContext;
this.uses_context = this.expression && this.expression.uses_context;
}
}

@ -31,7 +31,7 @@ export default class Animation extends Node {
});
}
block.hasAnimation = true;
block.has_animation = true;
this.expression = info.expression
? new Expression(component, this, scope, info.expression)

@ -14,12 +14,12 @@ export default class Attribute extends Node {
component: Component;
parent: Element;
name: string;
isSpread: boolean;
isTrue: boolean;
isDynamic: boolean;
isStatic: boolean;
isSynthetic: boolean;
shouldCache: boolean;
is_spread: boolean;
is_true: boolean;
is_dynamic: boolean;
is_static: boolean;
is_synthetic: boolean;
should_cache: boolean;
expression?: Expression;
chunks: (Text | Expression)[];
dependencies: Set<string>;
@ -29,33 +29,33 @@ export default class Attribute extends Node {
if (info.type === 'Spread') {
this.name = null;
this.isSpread = true;
this.isTrue = false;
this.isSynthetic = false;
this.is_spread = true;
this.is_true = false;
this.is_synthetic = false;
this.expression = new Expression(component, this, scope, info.expression);
this.dependencies = this.expression.dependencies;
this.chunks = null;
this.isDynamic = true; // TODO not necessarily
this.isStatic = false;
this.shouldCache = false; // TODO does this mean anything here?
this.is_dynamic = true; // TODO not necessarily
this.is_static = false;
this.should_cache = false; // TODO does this mean anything here?
}
else {
this.name = info.name;
this.isTrue = info.value === true;
this.isStatic = true;
this.isSynthetic = info.synthetic;
this.is_true = info.value === true;
this.is_static = true;
this.is_synthetic = info.synthetic;
this.dependencies = new Set();
this.chunks = this.isTrue
this.chunks = this.is_true
? []
: info.value.map(node => {
if (node.type === 'Text') return node;
this.isStatic = false;
this.is_static = false;
const expression = new Expression(component, this, scope, node.expression);
@ -63,9 +63,9 @@ export default class Attribute extends Node {
return expression;
});
this.isDynamic = this.dependencies.size > 0;
this.is_dynamic = this.dependencies.size > 0;
this.shouldCache = this.isDynamic
this.should_cache = this.is_dynamic
? this.chunks.length === 1
? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name)
: true
@ -74,7 +74,7 @@ export default class Attribute extends Node {
}
get_dependencies() {
if (this.isSpread) return this.expression.dynamic_dependencies();
if (this.is_spread) return this.expression.dynamic_dependencies();
const dependencies = new Set();
this.chunks.forEach(chunk => {
@ -86,8 +86,8 @@ export default class Attribute extends Node {
return Array.from(dependencies);
}
getValue(block) {
if (this.isTrue) return true;
get_value(block) {
if (this.is_true) return true;
if (this.chunks.length === 0) return `""`;
if (this.chunks.length === 1) {
@ -102,16 +102,16 @@ export default class Attribute extends Node {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
return chunk.getPrecedence() <= 13 ? `(${chunk.render()})` : chunk.render();
return chunk.get_precedence() <= 13 ? `(${chunk.render()})` : chunk.render();
}
})
.join(' + ');
}
getStaticValue() {
if (this.isSpread || this.isDynamic) return null;
get_static_value() {
if (this.is_spread || this.is_dynamic) return null;
return this.isTrue
return this.is_true
? true
: this.chunks[0]
? this.chunks[0].data

@ -7,7 +7,7 @@ import TemplateScope from './shared/TemplateScope';
export default class Binding extends Node {
name: string;
expression: Expression;
isContextual: boolean;
is_contextual: boolean;
obj: string;
prop: string;
@ -28,11 +28,11 @@ export default class Binding extends Node {
let prop;
const { name } = get_object(this.expression.node);
this.isContextual = scope.names.has(name);
this.is_contextual = scope.names.has(name);
// make sure we track this as a mutable ref
if (this.isContextual) {
scope.dependenciesForName.get(name).forEach(name => {
if (this.is_contextual) {
scope.dependencies_for_name.get(name).forEach(name => {
const variable = component.var_lookup.get(name);
variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
});

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
export default class CatchBlock extends Node {
@ -13,8 +13,8 @@ export default class CatchBlock extends Node {
this.scope = scope.child();
this.scope.add(parent.error, parent.expression.dependencies, this);
this.children = mapChildren(component, parent, this.scope, info.children);
this.children = map_children(component, parent, this.scope, info.children);
this.warnIfEmptyBlock();
this.warn_if_empty_block();
}
}

@ -2,7 +2,7 @@ import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import { Node as INode } from '../../interfaces';
@ -38,7 +38,7 @@ export default class EachBlock extends Node {
key: Expression;
scope: TemplateScope;
contexts: Array<{ name: string, tail: string }>;
hasAnimation: boolean;
has_animation: boolean;
has_binding = false;
children: Node[];
@ -71,11 +71,11 @@ export default class EachBlock extends Node {
this.scope.add(this.index, dependencies, this);
}
this.hasAnimation = false;
this.has_animation = false;
this.children = mapChildren(component, this, this.scope, info.children);
this.children = map_children(component, this, this.scope, info.children);
if (this.hasAnimation) {
if (this.has_animation) {
if (this.children.length !== 1) {
const child = this.children.find(child => !!child.animation);
component.error(child.animation, {
@ -85,7 +85,7 @@ export default class EachBlock extends Node {
}
}
this.warnIfEmptyBlock(); // TODO would be better if EachBlock, IfBlock etc extended an abstract Block class
this.warn_if_empty_block(); // TODO would be better if EachBlock, IfBlock etc extended an abstract Block class
this.else = info.else
? new ElseBlock(component, this, this.scope, info.else)

@ -9,7 +9,7 @@ import Action from './Action';
import Class from './Class';
import Text from './Text';
import { namespaces } from '../../utils/namespaces';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import { dimensions } from '../../utils/patterns';
import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
@ -18,13 +18,13 @@ import TemplateScope from './shared/TemplateScope';
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;
const ariaAttributes = 'activedescendant atomic autocomplete busy checked colindex controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowindex selected setsize sort valuemax valuemin valuenow valuetext'.split(' ');
const ariaAttributeSet = new Set(ariaAttributes);
const aria_attributes = 'activedescendant atomic autocomplete busy checked colindex controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowindex selected setsize sort valuemax valuemin valuenow valuetext'.split(' ');
const aria_attribute_set = new Set(aria_attributes);
const ariaRoles = 'alert alertdialog application article banner button cell checkbox columnheader combobox command complementary composite contentinfo definition dialog directory document feed figure form grid gridcell group heading img input landmark link list listbox listitem log main marquee math menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search searchbox section sectionhead select separator slider spinbutton status structure switch tab table tablist tabpanel term textbox timer toolbar tooltip tree treegrid treeitem widget window'.split(' ');
const ariaRoleSet = new Set(ariaRoles);
const aria_roles = 'alert alertdialog application article banner button cell checkbox columnheader combobox command complementary composite contentinfo definition dialog directory document feed figure form grid gridcell group heading img input landmark link list listbox listitem log main marquee math menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search searchbox section sectionhead select separator slider spinbutton status structure switch tab table tablist tabpanel term textbox timer toolbar tooltip tree treegrid treeitem widget window'.split(' ');
const aria_role_set = new Set(aria_roles);
const a11yRequiredAttributes = {
const a11y_required_attributes = {
a: ['href'],
area: ['alt', 'aria-label', 'aria-labelledby'],
@ -37,12 +37,12 @@ const a11yRequiredAttributes = {
object: ['title', 'aria-label', 'aria-labelledby']
};
const a11yDistractingElements = new Set([
const a11y_distracting_elements = new Set([
'blink',
'marquee'
]);
const a11yRequiredContent = new Set([
const a11y_required_content = new Set([
// anchor-has-content
'a',
@ -55,9 +55,9 @@ const a11yRequiredContent = new Set([
'h6'
])
const invisibleElements = new Set(['meta', 'html', 'script', 'style']);
const invisible_elements = new Set(['meta', 'html', 'script', 'style']);
const validModifiers = new Set([
const valid_modifiers = new Set([
'preventDefault',
'stopPropagation',
'capture',
@ -65,7 +65,7 @@ const validModifiers = new Set([
'passive'
]);
const passiveEvents = new Set([
const passive_events = new Set([
'wheel',
'touchstart',
'touchmove',
@ -93,10 +93,10 @@ export default class Element extends Node {
super(component, parent, scope, info);
this.name = info.name;
const parentElement = parent.findNearest(/^Element/);
const parent_element = parent.find_nearest(/^Element/);
this.namespace = this.name === 'svg' ?
namespaces.svg :
parentElement ? parentElement.namespace : this.component.namespace;
parent_element ? parent_element.namespace : this.component.namespace;
if (!this.namespace && svg.test(this.name)) {
this.component.warn(this, {
@ -107,9 +107,9 @@ export default class Element extends Node {
if (this.name === 'textarea') {
if (info.children.length > 0) {
const valueAttribute = info.attributes.find(node => node.name === 'value');
if (valueAttribute) {
component.error(valueAttribute, {
const value_attribute = info.attributes.find(node => node.name === 'value');
if (value_attribute) {
component.error(value_attribute, {
code: `textarea-duplicate-value`,
message: `A <textarea> can have either a value attribute or (equivalently) child content, but not both`
});
@ -131,9 +131,9 @@ export default class Element extends Node {
// Special case — treat these the same way:
// <option>{foo}</option>
// <option value={foo}>{foo}</option>
const valueAttribute = info.attributes.find((attribute: Node) => attribute.name === 'value');
const value_attribute = info.attributes.find((attribute: Node) => attribute.name === 'value');
if (!valueAttribute) {
if (!value_attribute) {
info.attributes.push({
type: 'Attribute',
name: 'value',
@ -202,7 +202,7 @@ export default class Element extends Node {
this.scope = scope;
}
this.children = mapChildren(component, this, this.scope, info.children);
this.children = map_children(component, this, this.scope, info.children);
this.validate();
@ -210,7 +210,7 @@ export default class Element extends Node {
}
validate() {
if (a11yDistractingElements.has(this.name)) {
if (a11y_distracting_elements.has(this.name)) {
// no-distracting-elements
this.component.warn(this, {
code: `a11y-distracting-elements`,
@ -244,25 +244,25 @@ export default class Element extends Node {
}
}
this.validateAttributes();
this.validateBindings();
this.validateContent();
this.validateEventHandlers();
this.validate_attributes();
this.validate_bindings();
this.validate_content();
this.validate_event_handlers();
}
validateAttributes() {
validate_attributes() {
const { component } = this;
const attributeMap = new Map();
const attribute_map = new Map();
this.attributes.forEach(attribute => {
if (attribute.isSpread) return;
if (attribute.is_spread) return;
const name = attribute.name.toLowerCase();
// aria-props
if (name.startsWith('aria-')) {
if (invisibleElements.has(this.name)) {
if (invisible_elements.has(this.name)) {
// aria-unsupported-elements
component.warn(attribute, {
code: `a11y-aria-attributes`,
@ -271,8 +271,8 @@ export default class Element extends Node {
}
const type = name.slice(5);
if (!ariaAttributeSet.has(type)) {
const match = fuzzymatch(type, ariaAttributes);
if (!aria_attribute_set.has(type)) {
const match = fuzzymatch(type, aria_attributes);
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
if (match) message += ` (did you mean '${match}'?)`;
@ -292,7 +292,7 @@ export default class Element extends Node {
// aria-role
if (name === 'role') {
if (invisibleElements.has(this.name)) {
if (invisible_elements.has(this.name)) {
// aria-unsupported-elements
component.warn(attribute, {
code: `a11y-misplaced-role`,
@ -300,9 +300,9 @@ export default class Element extends Node {
});
}
const value = attribute.getStaticValue();
if (value && !ariaRoleSet.has(value)) {
const match = fuzzymatch(value, ariaRoles);
const value = attribute.get_static_value();
if (value && !aria_role_set.has(value)) {
const match = fuzzymatch(value, aria_roles);
let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`;
@ -339,7 +339,7 @@ export default class Element extends Node {
// tabindex-no-positive
if (name === 'tabindex') {
const value = attribute.getStaticValue();
const value = attribute.get_static_value();
if (!isNaN(value) && +value > 0) {
component.warn(attribute, {
code: `a11y-positive-tabindex`,
@ -349,7 +349,7 @@ export default class Element extends Node {
}
if (name === 'slot') {
if (!attribute.isStatic) {
if (!attribute.is_static) {
component.error(attribute, {
code: `invalid-slot-attribute`,
message: `slot attribute cannot have a dynamic value`
@ -380,15 +380,15 @@ export default class Element extends Node {
}
}
attributeMap.set(attribute.name, attribute);
attribute_map.set(attribute.name, attribute);
});
// handle special cases
if (this.name === 'a') {
const attribute = attributeMap.get('href') || attributeMap.get('xlink:href');
const attribute = attribute_map.get('href') || attribute_map.get('xlink:href');
if (attribute) {
const value = attribute.getStaticValue();
const value = attribute.get_static_value();
if (value === '' || value === '#') {
component.warn(attribute, {
@ -405,19 +405,19 @@ export default class Element extends Node {
}
else {
const requiredAttributes = a11yRequiredAttributes[this.name];
if (requiredAttributes) {
const hasAttribute = requiredAttributes.some(name => attributeMap.has(name));
const required_attributes = a11y_required_attributes[this.name];
if (required_attributes) {
const has_attribute = required_attributes.some(name => attribute_map.has(name));
if (!hasAttribute) {
shouldHaveAttribute(this, requiredAttributes);
if (!has_attribute) {
should_have_attribute(this, required_attributes);
}
}
if (this.name === 'input') {
const type = attributeMap.get('type');
if (type && type.getStaticValue() === 'image') {
shouldHaveAttribute(
const type = attribute_map.get('type');
if (type && type.get_static_value() === 'image') {
should_have_attribute(
this,
['alt', 'aria-label', 'aria-labelledby'],
'input type="image"'
@ -427,24 +427,24 @@ export default class Element extends Node {
}
}
validateBindings() {
validate_bindings() {
const { component } = this;
const checkTypeAttribute = () => {
const check_type_attribute = () => {
const attribute = this.attributes.find(
(attribute: Attribute) => attribute.name === 'type'
);
if (!attribute) return null;
if (!attribute.isStatic) {
if (!attribute.is_static) {
component.error(attribute, {
code: `invalid-type`,
message: `'type' attribute cannot be dynamic if input uses two-way binding`
});
}
const value = attribute.getStaticValue();
const value = attribute.get_static_value();
if (value === true) {
component.error(attribute, {
@ -476,14 +476,14 @@ export default class Element extends Node {
(attribute: Attribute) => attribute.name === 'multiple'
);
if (attribute && !attribute.isStatic) {
if (attribute && !attribute.is_static) {
component.error(attribute, {
code: `dynamic-multiple-attribute`,
message: `'multiple' attribute cannot be dynamic if select uses two-way binding`
});
}
} else {
checkTypeAttribute();
check_type_attribute();
}
} else if (name === 'checked' || name === 'indeterminate') {
if (this.name !== 'input') {
@ -493,7 +493,7 @@ export default class Element extends Node {
});
}
if (checkTypeAttribute() !== 'checkbox') {
if (check_type_attribute() !== 'checkbox') {
component.error(binding, {
code: `invalid-binding`,
message: `'${name}' binding can only be used with <input type="checkbox">`
@ -507,7 +507,7 @@ export default class Element extends Node {
});
}
const type = checkTypeAttribute();
const type = check_type_attribute();
if (type !== 'checkbox' && type !== 'radio') {
component.error(binding, {
@ -523,7 +523,7 @@ export default class Element extends Node {
});
}
const type = checkTypeAttribute();
const type = check_type_attribute();
if (type !== 'file') {
component.error(binding, {
@ -572,8 +572,8 @@ export default class Element extends Node {
});
}
validateContent() {
if (!a11yRequiredContent.has(this.name)) return;
validate_content() {
if (!a11y_required_content.has(this.name)) return;
if (this.children.length === 0) {
this.component.warn(this, {
@ -583,7 +583,7 @@ export default class Element extends Node {
}
}
validateEventHandlers() {
validate_event_handlers() {
const { component } = this;
this.handlers.forEach(handler => {
@ -595,16 +595,16 @@ export default class Element extends Node {
}
handler.modifiers.forEach(modifier => {
if (!validModifiers.has(modifier)) {
if (!valid_modifiers.has(modifier)) {
component.error(handler, {
code: 'invalid-event-modifier',
message: `Valid event modifiers are ${list(Array.from(validModifiers))}`
message: `Valid event modifiers are ${list(Array.from(valid_modifiers))}`
});
}
if (modifier === 'passive') {
if (passiveEvents.has(handler.name)) {
if (handler.canMakePassive) {
if (passive_events.has(handler.name)) {
if (handler.can_make_passive) {
component.warn(handler, {
code: 'redundant-event-modifier',
message: `Touch event handlers that don't use the 'event' object are passive by default`
@ -618,7 +618,7 @@ export default class Element extends Node {
}
}
if (component.compileOptions.legacy && (modifier === 'once' || modifier === 'passive')) {
if (component.compile_options.legacy && (modifier === 'once' || modifier === 'passive')) {
// TODO this could be supported, but it would need a few changes to
// how event listeners work
component.error(handler, {
@ -628,21 +628,21 @@ export default class Element extends Node {
}
});
if (passiveEvents.has(handler.name) && handler.canMakePassive && !handler.modifiers.has('preventDefault')) {
if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault')) {
// touch/wheel events should be passive by default
handler.modifiers.add('passive');
}
});
}
getStaticAttributeValue(name: string) {
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.isTrue) return true;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
@ -652,20 +652,20 @@ export default class Element extends Node {
return null;
}
isMediaNode() {
is_media_node() {
return this.name === 'audio' || this.name === 'video';
}
addCssClass(className = this.component.stylesheet.id) {
const classAttribute = this.attributes.find(a => a.name === 'class');
if (classAttribute && !classAttribute.isTrue) {
if (classAttribute.chunks.length === 1 && classAttribute.chunks[0].type === 'Text') {
(classAttribute.chunks[0] as Text).data += ` ${className}`;
add_css_class(class_name = this.component.stylesheet.id) {
const class_attribute = this.attributes.find(a => a.name === 'class');
if (class_attribute && !class_attribute.is_true) {
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
(class_attribute.chunks[0] as Text).data += ` ${class_name}`;
} else {
(<Node[]>classAttribute.chunks).push(
(<Node[]>class_attribute.chunks).push(
new Text(this.component, this, this.scope, {
type: 'Text',
data: ` ${className}`
data: ` ${class_name}`
})
);
}
@ -674,14 +674,14 @@ export default class Element extends Node {
new Attribute(this.component, this, this.scope, {
type: 'Attribute',
name: 'class',
value: [{ type: 'Text', data: className }]
value: [{ type: 'Text', data: class_name }]
})
);
}
}
}
function shouldHaveAttribute(
function should_have_attribute(
node,
attributes: string[],
name = node.name

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
export default class ElseBlock extends Node {
type: 'ElseBlock';
@ -9,8 +9,8 @@ export default class ElseBlock extends Node {
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = mapChildren(component, this, scope, info.children);
this.children = map_children(component, this, scope, info.children);
this.warnIfEmptyBlock();
this.warn_if_empty_block();
}
}

@ -9,8 +9,8 @@ export default class EventHandler extends Node {
modifiers: Set<string>;
expression: Expression;
handler_name: string;
usesContext = false;
canMakePassive = false;
uses_context = false;
can_make_passive = false;
constructor(component: Component, parent, template_scope, info) {
super(component, parent, template_scope, info);
@ -20,12 +20,12 @@ export default class EventHandler extends Node {
if (info.expression) {
this.expression = new Expression(component, this, template_scope, info.expression);
this.usesContext = this.expression.usesContext;
this.uses_context = this.expression.uses_context;
if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) {
// TODO make this detection more accurate — if `event.preventDefault` isn't called, and
// `event` is passed to another function, we can make it passive
this.canMakePassive = true;
this.can_make_passive = true;
} else if (info.expression.type === 'Identifier') {
let node = component.node_for_declaration.get(info.expression.name);
@ -36,11 +36,11 @@ export default class EventHandler extends Node {
}
if (node && /Function/.test(node.type) && node.params.length === 0) {
this.canMakePassive = true;
this.can_make_passive = true;
}
}
} else {
const name = component.getUniqueName(`${this.name}_handler`);
const name = component.get_unique_name(`${this.name}_handler`);
component.add_var({
name,

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Component from '../Component';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import Block from '../render-dom/Block';
import TemplateScope from './shared/TemplateScope';
@ -14,6 +14,6 @@ export default class Fragment extends Node {
super(component, null, scope, info);
this.scope = scope;
this.children = mapChildren(component, this, scope, info.children);
this.children = map_children(component, this, scope, info.children);
}
}

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
export default class Head extends Node {
type: 'Head';
@ -16,7 +16,7 @@ export default class Head extends Node {
});
}
this.children = mapChildren(component, parent, scope, info.children.filter(child => {
this.children = map_children(component, parent, scope, info.children.filter(child => {
return (child.type !== 'Text' || /\S/.test(child.data));
}));
}

@ -2,7 +2,7 @@ import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
export default class IfBlock extends Node {
type: 'IfBlock';
@ -16,12 +16,12 @@ export default class IfBlock extends Node {
super(component, parent, scope, info);
this.expression = new Expression(component, this, scope, info.expression);
this.children = mapChildren(component, this, scope, info.children);
this.children = map_children(component, this, scope, info.children);
this.else = info.else
? new ElseBlock(component, this, scope, info.else)
: null;
this.warnIfEmptyBlock();
this.warn_if_empty_block();
}
}

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Attribute from './Attribute';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import Binding from './Binding';
import EventHandler from './EventHandler';
import Expression from './shared/Expression';
@ -96,6 +96,6 @@ export default class InlineComponent extends Node {
this.scope = scope;
}
this.children = mapChildren(component, this, this.scope, info.children);
this.children = map_children(component, this, this.scope, info.children);
}
}

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
export default class PendingBlock extends Node {
block: Block;
@ -8,8 +8,8 @@ export default class PendingBlock extends Node {
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = mapChildren(component, parent, scope, info.children);
this.children = map_children(component, parent, scope, info.children);
this.warnIfEmptyBlock();
this.warn_if_empty_block();
}
}

@ -19,13 +19,6 @@ export default class Slot extends Element {
});
}
// if (attr.name !== 'name') {
// component.error(attr, {
// code: `invalid-slot-attribute`,
// message: `"name" is the only attribute permitted on <slot> elements`
// });
// }
if (attr.name === 'name') {
if (attr.value.length !== 1 || attr.value[0].type !== 'Text') {
component.error(attr, {
@ -61,14 +54,14 @@ export default class Slot extends Element {
// }
}
getStaticAttributeValue(name: string) {
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
attr => attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.isTrue) return true;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {

@ -3,7 +3,6 @@ import Node from './shared/Node';
export default class Text extends Node {
type: 'Text';
data: string;
shouldSkip: boolean;
constructor(component, parent, scope, info) {
super(component, parent, scope, info);

@ -1,6 +1,6 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
export default class ThenBlock extends Node {
@ -13,8 +13,8 @@ export default class ThenBlock extends Node {
this.scope = scope.child();
this.scope.add(parent.value, parent.expression.dependencies, this);
this.children = mapChildren(component, parent, this.scope, info.children);
this.children = map_children(component, parent, this.scope, info.children);
this.warnIfEmptyBlock();
this.warn_if_empty_block();
}
}

@ -1,14 +1,14 @@
import Node from './shared/Node';
import mapChildren from './shared/mapChildren';
import map_children from './shared/map_children';
export default class Title extends Node {
type: 'Title';
children: any[]; // TODO
shouldCache: boolean;
should_cache: boolean;
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = mapChildren(component, parent, scope, info.children);
this.children = map_children(component, parent, scope, info.children);
if (info.attributes.length > 0) {
component.error(info.attributes[0], {
@ -26,7 +26,7 @@ export default class Title extends Node {
}
});
this.shouldCache = info.children.length === 1
this.should_cache = info.children.length === 1
? (
info.children[0].type !== 'Identifier' ||
scope.names.has(info.children[0].name)

@ -21,11 +21,11 @@ export default class Transition extends Node {
this.is_local = info.modifiers.includes('local');
if ((info.intro && parent.intro) || (info.outro && parent.outro)) {
const parentTransition = (parent.intro || parent.outro);
const parent_transition = (parent.intro || parent.outro);
const message = this.directive === parentTransition.directive
const message = this.directive === parent_transition.directive
? `An element can only have one '${this.directive}' directive`
: `An element cannot have both ${describe(parentTransition)} directive and ${describe(this)} directive`;
: `An element cannot have both ${describe(parent_transition)} directive and ${describe(this)} directive`;
component.error(info, {
code: `duplicate-transition`,

@ -6,7 +6,7 @@ import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
import Action from './Action';
const validBindings = [
const valid_bindings = [
'innerWidth',
'innerHeight',
'outerWidth',
@ -41,11 +41,11 @@ export default class Window extends Node {
});
}
if (!~validBindings.indexOf(node.name)) {
if (!~valid_bindings.indexOf(node.name)) {
const match = (
node.name === 'width' ? 'innerWidth' :
node.name === 'height' ? 'innerHeight' :
fuzzymatch(node.name, validBindings)
fuzzymatch(node.name, valid_bindings)
);
const message = `'${node.name}' is not a valid binding on <svelte:window>`;
@ -58,7 +58,7 @@ export default class Window extends Node {
} else {
component.error(node, {
code: `invalid-binding`,
message: `${message} — valid bindings are ${list(validBindings)}`
message: `${message} — valid bindings are ${list(valid_bindings)}`
});
}
}

@ -77,8 +77,7 @@ export default class Expression {
is_synthetic: boolean;
declarations: string[] = [];
usesContext = false;
usesEvent = false;
uses_context = false;
rendered: string;
@ -93,7 +92,7 @@ export default class Expression {
this.node = info;
this.template_scope = template_scope;
this.owner = owner;
this.is_synthetic = owner.isSynthetic;
this.is_synthetic = owner.is_synthetic;
const { dependencies, contextual_dependencies } = this;
@ -137,12 +136,12 @@ export default class Expression {
dependencies.add(name);
}
} else if (template_scope.names.has(name)) {
expression.usesContext = true;
expression.uses_context = true;
contextual_dependencies.add(name);
if (!function_expression) {
template_scope.dependenciesForName.get(name).forEach(name => dependencies.add(name));
template_scope.dependencies_for_name.get(name).forEach(name => dependencies.add(name));
}
} else {
if (!function_expression) {
@ -175,7 +174,7 @@ export default class Expression {
if (names) {
names.forEach(name => {
if (template_scope.names.has(name)) {
template_scope.dependenciesForName.get(name).forEach(name => {
template_scope.dependencies_for_name.get(name).forEach(name => {
const variable = component.var_lookup.get(name);
if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
});
@ -214,7 +213,7 @@ export default class Expression {
});
}
getPrecedence() {
get_precedence() {
return this.node.type in precedence ? precedence[this.node.type](this.node) : 0;
}
@ -263,14 +262,14 @@ export default class Expression {
if (template_scope.names.has(name)) {
contextual_dependencies.add(name);
template_scope.dependenciesForName.get(name).forEach(dependency => {
template_scope.dependencies_for_name.get(name).forEach(dependency => {
dependencies.add(dependency);
});
} else {
dependencies.add(name);
component.add_reference(name);
}
} else if (!is_synthetic && isContextual(component, template_scope, name)) {
} else if (!is_synthetic && is_contextual(component, template_scope, name)) {
code.prependRight(node.start, key === 'key' && parent.shorthand
? `${name}: ctx.`
: 'ctx.');
@ -347,7 +346,7 @@ export default class Expression {
// the return value doesn't matter
}
const name = component.getUniqueName(
const name = component.get_unique_name(
sanitize(get_function_name(node, owner))
);
@ -465,7 +464,7 @@ export default class Expression {
});
if (declarations.length > 0) {
block.maintainContext = true;
block.maintain_context = true;
declarations.forEach(declaration => {
block.builders.init.add_block(declaration);
});
@ -487,11 +486,11 @@ function get_function_name(node, parent) {
return 'func';
}
function isContextual(component: Component, scope: TemplateScope, name: string) {
function is_contextual(component: Component, scope: TemplateScope, name: string) {
if (name === '$$props') return true;
// if it's a name below root scope, it's contextual
if (!scope.isTopLevel(name)) return true;
if (!scope.is_top_level(name)) return true;
const variable = component.var_lookup.get(name);

@ -10,7 +10,7 @@ export default class Node {
prev?: Node;
next?: Node;
canUseInnerHTML: boolean;
can_use_innerhtml: boolean;
var: string;
constructor(component: Component, parent, scope, info: any) {
@ -30,25 +30,25 @@ export default class Node {
});
}
cannotUseInnerHTML() {
if (this.canUseInnerHTML !== false) {
this.canUseInnerHTML = false;
if (this.parent) this.parent.cannotUseInnerHTML();
cannot_use_innerhtml() {
if (this.can_use_innerhtml !== false) {
this.can_use_innerhtml = false;
if (this.parent) this.parent.cannot_use_innerhtml();
}
}
hasAncestor(type: string) {
has_ancestor(type: string) {
return this.parent ?
this.parent.type === type || this.parent.hasAncestor(type) :
this.parent.type === type || this.parent.has_ancestor(type) :
false;
}
findNearest(selector: RegExp) {
find_nearest(selector: RegExp) {
if (selector.test(this.type)) return this;
if (this.parent) return this.parent.findNearest(selector);
if (this.parent) return this.parent.find_nearest(selector);
}
warnIfEmptyBlock() {
warn_if_empty_block() {
if (!/Block$/.test(this.type) || !this.children) return;
if (this.children.length > 1) return;

@ -3,13 +3,13 @@ import Expression from './Expression';
export default class Tag extends Node {
expression: Expression;
shouldCache: boolean;
should_cache: boolean;
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.expression = new Expression(component, this, scope, info.expression);
this.shouldCache = (
this.should_cache = (
info.expression.type !== 'Identifier' ||
(this.expression.dependencies.size && scope.names.has(info.expression.name))
);

@ -1,4 +1,3 @@
import Node from './Node';
import EachBlock from '../EachBlock';
import ThenBlock from '../ThenBlock';
import CatchBlock from '../CatchBlock';
@ -8,19 +7,19 @@ type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Elem
export default class TemplateScope {
names: Set<string>;
dependenciesForName: Map<string, Set<string>>;
dependencies_for_name: Map<string, Set<string>>;
owners: Map<string, NodeWithScope> = new Map();
parent?: TemplateScope;
constructor(parent?: TemplateScope) {
this.parent = parent;
this.names = new Set(parent ? parent.names : []);
this.dependenciesForName = new Map(parent ? parent.dependenciesForName : []);
this.dependencies_for_name = new Map(parent ? parent.dependencies_for_name : []);
}
add(name, dependencies: Set<string>, owner) {
this.names.add(name);
this.dependenciesForName.set(name, dependencies);
this.dependencies_for_name.set(name, dependencies);
this.owners.set(name, owner);
return this;
}
@ -30,16 +29,16 @@ export default class TemplateScope {
return child;
}
isTopLevel(name: string) {
return !this.parent || !this.names.has(name) && this.parent.isTopLevel(name);
is_top_level(name: string) {
return !this.parent || !this.names.has(name) && this.parent.is_top_level(name);
}
getOwner(name: string): NodeWithScope {
return this.owners.get(name) || (this.parent && this.parent.getOwner(name));
get_owner(name: string): NodeWithScope {
return this.owners.get(name) || (this.parent && this.parent.get_owner(name));
}
is_let(name: string) {
const owner = this.getOwner(name);
const owner = this.get_owner(name);
return owner && (owner.type === 'Element' || owner.type === 'InlineComponent');
}
}

@ -16,7 +16,7 @@ import Title from '../Title';
import Window from '../Window';
import Node from './Node';
function getConstructor(type): typeof Node {
function get_constructor(type): typeof Node {
switch (type) {
case 'AwaitBlock': return AwaitBlock;
case 'Body': return Body;
@ -38,10 +38,10 @@ function getConstructor(type): typeof Node {
}
}
export default function mapChildren(component, parent, scope, children: any[]) {
export default function map_children(component, parent, scope, children: any[]) {
let last = null;
return children.map(child => {
const constructor = getConstructor(child.type);
const constructor = get_constructor(child.type);
const node = new constructor(component, parent, scope, child);
if (last) last.next = node;

@ -2,9 +2,6 @@ import CodeBuilder from '../utils/CodeBuilder';
import deindent from '../utils/deindent';
import Renderer from './Renderer';
import Wrapper from './wrappers/shared/Wrapper';
import EachBlockWrapper from './wrappers/EachBlock';
import InlineComponentWrapper from './wrappers/InlineComponent';
import ElementWrapper from './wrappers/Element';
export interface BlockOptions {
parent?: Block;
@ -48,19 +45,19 @@ export default class Block {
event_listeners: string[] = [];
maintainContext: boolean;
hasAnimation: boolean;
hasIntros: boolean;
hasOutros: boolean;
hasIntroMethod: boolean; // could have the method without the transition, due to siblings
hasOutroMethod: boolean;
maintain_context: boolean;
has_animation: boolean;
has_intros: boolean;
has_outros: boolean;
has_intro_method: boolean; // could have the method without the transition, due to siblings
has_outro_method: boolean;
outros: number;
aliases: Map<string, string>;
variables: Map<string, string>;
getUniqueName: (name: string) => string;
get_unique_name: (name: string) => string;
hasUpdateMethod = false;
has_update_method = false;
autofocus: string;
constructor(options: BlockOptions) {
@ -94,19 +91,19 @@ export default class Block {
destroy: new CodeBuilder(),
};
this.hasAnimation = false;
this.hasIntroMethod = false; // a block could have an intro method but not intro transitions, e.g. if a sibling block has intros
this.hasOutroMethod = false;
this.has_animation = false;
this.has_intro_method = false; // a block could have an intro method but not intro transitions, e.g. if a sibling block has intros
this.has_outro_method = false;
this.outros = 0;
this.getUniqueName = this.renderer.component.getUniqueNameMaker();
this.get_unique_name = this.renderer.component.get_unique_name_maker();
this.variables = new Map();
this.aliases = new Map().set('ctx', this.getUniqueName('ctx'));
if (this.key) this.aliases.set('key', this.getUniqueName('key'));
this.aliases = new Map().set('ctx', this.get_unique_name('ctx'));
if (this.key) this.aliases.set('key', this.get_unique_name('key'));
}
assignVariableNames() {
assign_variable_names() {
const seen = new Set();
const dupes = new Set();
@ -116,7 +113,7 @@ export default class Block {
const wrapper = this.wrappers[i];
if (!wrapper.var) continue;
if (wrapper.parent && wrapper.parent.canUseInnerHTML) continue;
if (wrapper.parent && wrapper.parent.can_use_innerhtml) continue;
if (seen.has(wrapper.var)) {
dupes.add(wrapper.var);
@ -136,60 +133,60 @@ export default class Block {
if (dupes.has(wrapper.var)) {
const i = counts.get(wrapper.var) || 0;
counts.set(wrapper.var, i + 1);
wrapper.var = this.getUniqueName(wrapper.var + i);
wrapper.var = this.get_unique_name(wrapper.var + i);
} else {
wrapper.var = this.getUniqueName(wrapper.var);
wrapper.var = this.get_unique_name(wrapper.var);
}
}
}
addDependencies(dependencies: Set<string>) {
add_dependencies(dependencies: Set<string>) {
dependencies.forEach(dependency => {
this.dependencies.add(dependency);
});
this.hasUpdateMethod = true;
this.has_update_method = true;
}
addElement(
add_element(
name: string,
renderStatement: string,
claimStatement: string,
parentNode: string,
noDetach?: boolean
render_statement: string,
claim_statement: string,
parent_node: string,
no_detach?: boolean
) {
this.addVariable(name);
this.builders.create.add_line(`${name} = ${renderStatement};`);
this.add_variable(name);
this.builders.create.add_line(`${name} = ${render_statement};`);
if (this.renderer.options.hydratable) {
this.builders.claim.add_line(`${name} = ${claimStatement || renderStatement};`);
this.builders.claim.add_line(`${name} = ${claim_statement || render_statement};`);
}
if (parentNode) {
this.builders.mount.add_line(`@append(${parentNode}, ${name});`);
if (parentNode === 'document.head') this.builders.destroy.add_line(`@detach(${name});`);
if (parent_node) {
this.builders.mount.add_line(`@append(${parent_node}, ${name});`);
if (parent_node === 'document.head') this.builders.destroy.add_line(`@detach(${name});`);
} else {
this.builders.mount.add_line(`@insert(#target, ${name}, anchor);`);
if (!noDetach) this.builders.destroy.add_conditional('detaching', `@detach(${name});`);
if (!no_detach) this.builders.destroy.add_conditional('detaching', `@detach(${name});`);
}
}
addIntro(local?: boolean) {
this.hasIntros = this.hasIntroMethod = this.renderer.hasIntroTransitions = true;
if (!local && this.parent) this.parent.addIntro();
add_intro(local?: boolean) {
this.has_intros = this.has_intro_method = true;
if (!local && this.parent) this.parent.add_intro();
}
addOutro(local?: boolean) {
this.hasOutros = this.hasOutroMethod = this.renderer.hasOutroTransitions = true;
add_outro(local?: boolean) {
this.has_outros = this.has_outro_method = true;
this.outros += 1;
if (!local && this.parent) this.parent.addOutro();
if (!local && this.parent) this.parent.add_outro();
}
addAnimation() {
this.hasAnimation = true;
add_animation() {
this.has_animation = true;
}
addVariable(name: string, init?: string) {
add_variable(name: string, init?: string) {
if (name[0] === '#') {
name = this.alias(name.slice(1));
}
@ -205,7 +202,7 @@ export default class Block {
alias(name: string) {
if (!this.aliases.has(name)) {
this.aliases.set(name, this.getUniqueName(name));
this.aliases.set(name, this.get_unique_name(name));
}
return this.aliases.get(name);
@ -215,11 +212,11 @@ export default class Block {
return new Block(Object.assign({}, this, { key: null }, options, { parent: this }));
}
getContents(localKey?: string) {
get_contents(local_key?: string) {
const { dev } = this.renderer.options;
if (this.hasOutros) {
this.addVariable('#current');
if (this.has_outros) {
this.add_variable('#current');
if (!this.builders.intro.is_empty()) {
this.builders.intro.add_line(`#current = true;`);
@ -235,14 +232,14 @@ export default class Block {
this.builders.mount.add_line(`${this.autofocus}.focus();`);
}
this.renderListeners();
this.render_listeners();
const properties = new CodeBuilder();
const methodName = (short: string, long: string) => dev ? `${short}: function ${this.getUniqueName(long)}` : short;
const method_name = (short: string, long: string) => dev ? `${short}: function ${this.get_unique_name(long)}` : short;
if (localKey) {
properties.add_block(`key: ${localKey},`);
if (local_key) {
properties.add_block(`key: ${local_key},`);
}
if (this.first) {
@ -260,7 +257,7 @@ export default class Block {
);
properties.add_block(deindent`
${methodName('c', 'create')}() {
${method_name('c', 'create')}() {
${this.builders.create}
${hydrate}
},
@ -272,7 +269,7 @@ export default class Block {
properties.add_line(`l: @noop,`);
} else {
properties.add_block(deindent`
${methodName('l', 'claim')}(nodes) {
${method_name('l', 'claim')}(nodes) {
${this.builders.claim}
${this.renderer.options.hydratable && !this.builders.hydrate.is_empty() && `this.h();`}
},
@ -282,7 +279,7 @@ export default class Block {
if (this.renderer.options.hydratable && !this.builders.hydrate.is_empty()) {
properties.add_block(deindent`
${methodName('h', 'hydrate')}() {
${method_name('h', 'hydrate')}() {
${this.builders.hydrate}
},
`);
@ -292,48 +289,48 @@ export default class Block {
properties.add_line(`m: @noop,`);
} else {
properties.add_block(deindent`
${methodName('m', 'mount')}(#target, anchor) {
${method_name('m', 'mount')}(#target, anchor) {
${this.builders.mount}
},
`);
}
if (this.hasUpdateMethod || this.maintainContext) {
if (this.builders.update.is_empty() && !this.maintainContext) {
if (this.has_update_method || this.maintain_context) {
if (this.builders.update.is_empty() && !this.maintain_context) {
properties.add_line(`p: @noop,`);
} else {
properties.add_block(deindent`
${methodName('p', 'update')}(changed, ${this.maintainContext ? 'new_ctx' : 'ctx'}) {
${this.maintainContext && `ctx = new_ctx;`}
${method_name('p', 'update')}(changed, ${this.maintain_context ? 'new_ctx' : 'ctx'}) {
${this.maintain_context && `ctx = new_ctx;`}
${this.builders.update}
},
`);
}
}
if (this.hasAnimation) {
if (this.has_animation) {
properties.add_block(deindent`
${methodName('r', 'measure')}() {
${method_name('r', 'measure')}() {
${this.builders.measure}
},
${methodName('f', 'fix')}() {
${method_name('f', 'fix')}() {
${this.builders.fix}
},
${methodName('a', 'animate')}() {
${method_name('a', 'animate')}() {
${this.builders.animate}
},
`);
}
if (this.hasIntroMethod || this.hasOutroMethod) {
if (this.has_intro_method || this.has_outro_method) {
if (this.builders.intro.is_empty()) {
properties.add_line(`i: @noop,`);
} else {
properties.add_block(deindent`
${methodName('i', 'intro')}(#local) {
${this.hasOutros && `if (#current) return;`}
${method_name('i', 'intro')}(#local) {
${this.has_outros && `if (#current) return;`}
${this.builders.intro}
},
`);
@ -343,7 +340,7 @@ export default class Block {
properties.add_line(`o: @noop,`);
} else {
properties.add_block(deindent`
${methodName('o', 'outro')}(#local) {
${method_name('o', 'outro')}(#local) {
${this.builders.outro}
},
`);
@ -354,7 +351,7 @@ export default class Block {
properties.add_line(`d: @noop`);
} else {
properties.add_block(deindent`
${methodName('d', 'destroy')}(detaching) {
${method_name('d', 'destroy')}(detaching) {
${this.builders.destroy}
}
`);
@ -379,9 +376,9 @@ export default class Block {
});
}
renderListeners(chunk: string = '') {
render_listeners(chunk: string = '') {
if (this.event_listeners.length > 0) {
this.addVariable(`#dispose${chunk}`);
this.add_variable(`#dispose${chunk}`);
if (this.event_listeners.length === 1) {
this.builders.hydrate.add_line(
@ -406,12 +403,12 @@ export default class Block {
}
toString() {
const localKey = this.key && this.getUniqueName('key');
const local_key = this.key && this.get_unique_name('key');
return deindent`
${this.comment && `// ${this.comment}`}
function ${this.name}(${this.key ? `${localKey}, ` : ''}ctx) {
${this.getContents(localKey)}
function ${this.name}(${this.key ? `${local_key}, ` : ''}ctx) {
${this.get_contents(local_key)}
}
`;
}

@ -11,16 +11,13 @@ export default class Renderer {
blocks: (Block | string)[];
readonly: Set<string>;
slots: Set<string>;
metaBindings: CodeBuilder;
bindingGroups: string[];
meta_bindings: CodeBuilder;
binding_groups: string[];
block: Block;
fragment: FragmentWrapper;
fileVar: string;
hasIntroTransitions: boolean;
hasOutroTransitions: boolean;
file_var: string;
constructor(component: Component, options: CompileOptions) {
this.component = component;
@ -30,12 +27,12 @@ export default class Renderer {
this.readonly = new Set();
this.slots = new Set();
this.fileVar = options.dev && this.component.getUniqueName('file');
this.file_var = options.dev && this.component.get_unique_name('file');
// initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
this.metaBindings = new CodeBuilder();
this.meta_bindings = new CodeBuilder();
this.bindingGroups = [];
this.binding_groups = [];
// main block
this.block = new Block({
@ -48,7 +45,7 @@ export default class Renderer {
dependencies: new Set(),
});
this.block.hasUpdateMethod = true;
this.block.has_update_method = true;
this.blocks = [];
this.fragment = new FragmentWrapper(
@ -62,11 +59,11 @@ export default class Renderer {
this.blocks.forEach(block => {
if (typeof block !== 'string') {
block.assignVariableNames();
block.assign_variable_names();
}
});
this.block.assignVariableNames();
this.block.assign_variable_names();
this.fragment.render(this.block, null, 'nodes');
}

@ -21,23 +21,23 @@ export default function dom(
const renderer = new Renderer(component, options);
const { block } = renderer;
block.hasOutroMethod = true;
block.has_outro_method = true;
// prevent fragment being created twice (#1063)
if (options.customElement) block.builders.create.add_line(`this.c = @noop;`);
const builder = new CodeBuilder();
if (component.compileOptions.dev) {
builder.add_line(`const ${renderer.fileVar} = ${JSON.stringify(component.file)};`);
if (component.compile_options.dev) {
builder.add_line(`const ${renderer.file_var} = ${JSON.stringify(component.file)};`);
}
const css = component.stylesheet.render(options.filename, !options.customElement);
const styles = component.stylesheet.hasStyles && stringify(options.dev ?
const styles = component.stylesheet.has_styles && stringify(options.dev ?
`${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */` :
css.code, { onlyEscapeAtSymbol: true });
if (styles && component.compileOptions.css !== false && !options.customElement) {
if (styles && component.compile_options.css !== false && !options.customElement) {
builder.add_block(deindent`
function @add_css() {
var style = @element("style");
@ -66,7 +66,7 @@ export default function dom(
// explicit opt-in, or something?
const should_add_css = (
!options.customElement &&
component.stylesheet.hasStyles &&
component.stylesheet.has_styles &&
options.css !== false
);
@ -90,7 +90,7 @@ export default function dom(
const body = [];
const not_equal = component.componentOptions.immutable ? `@not_equal` : `@safe_not_equal`;
const not_equal = component.component_options.immutable ? `@not_equal` : `@safe_not_equal`;
let dev_props_check;
props.forEach(x => {
@ -102,7 +102,7 @@ export default function dom(
return ${x.name};
}
`);
} else if (component.componentOptions.accessors) {
} else if (component.component_options.accessors) {
body.push(deindent`
get ${x.export_name}() {
return this.$$.ctx.${x.name};
@ -111,7 +111,7 @@ export default function dom(
}
if (variable.writable && !renderer.readonly.has(x.export_name)) {
if (component.componentOptions.accessors) {
if (component.component_options.accessors) {
body.push(deindent`
set ${x.export_name}(${x.name}) {
this.$set({ ${x.name} });
@ -119,7 +119,7 @@ export default function dom(
}
`);
}
} else if (component.compileOptions.dev) {
} else if (component.compile_options.dev) {
body.push(deindent`
set ${x.export_name}(value) {
throw new Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'");
@ -128,7 +128,7 @@ export default function dom(
}
});
if (component.compileOptions.dev) {
if (component.compile_options.dev) {
// TODO check no uunexpected props were passed, as well as
// checking that expected ones were passed
const expected = props.filter(prop => !prop.initialised);
@ -258,7 +258,7 @@ export default function dom(
const subscribe = component.helper('subscribe');
let insert = `${subscribe}($$self, ${name}, $${callback})`;
if (component.compileOptions.dev) {
if (component.compile_options.dev) {
const validate_store = component.helper('validate_store');
insert = `${validate_store}(${name}, '${name}'); ${insert}`;
}
@ -274,7 +274,7 @@ export default function dom(
builder.add_block(deindent`
function create_fragment(ctx) {
${block.getContents()}
${block.get_contents()}
}
${component.module_javascript}
@ -303,7 +303,7 @@ export default function dom(
filtered_declarations.push(...arr.map(name => `$$slot_${sanitize(name)}`), '$$scope');
}
if (renderer.bindingGroups.length > 0) {
if (renderer.binding_groups.length > 0) {
filtered_declarations.push(`$$binding_groups`);
}
@ -331,7 +331,7 @@ export default function dom(
return !variable || variable.hoistable;
})
.map(({ name }) => deindent`
${component.compileOptions.dev && `@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
${component.compile_options.dev && `@validate_store(${name.slice(1)}, '${name.slice(1)}');`}
@subscribe($$self, ${name.slice(1)}, $$value => { ${name} = $$value; $$invalidate('${name}', ${name}); });
`);
@ -391,7 +391,7 @@ export default function dom(
${renderer.slots.size && `let { ${[...renderer.slots].map(name => `$$slot_${sanitize(name)}`).join(', ')}, $$scope } = $$props;`}
${renderer.bindingGroups.length > 0 && `const $$binding_groups = [${renderer.bindingGroups.map(_ => `[]`).join(', ')}];`}
${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`}
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}

@ -13,7 +13,7 @@ class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
block: Block;
fragment: FragmentWrapper;
isDynamic: boolean;
is_dynamic: boolean;
var = null;
@ -23,14 +23,14 @@ class AwaitBlockBranch extends Wrapper {
block: Block,
parent: Wrapper,
node: AwaitBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.getUniqueName(`create_${status}_block`)
name: this.renderer.component.get_unique_name(`create_${status}_block`)
});
this.fragment = new FragmentWrapper(
@ -38,11 +38,11 @@ class AwaitBlockBranch extends Wrapper {
this.block,
this.node.children,
parent,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
this.isDynamic = this.block.dependencies.size > 0;
this.is_dynamic = this.block.dependencies.size > 0;
}
}
@ -60,18 +60,18 @@ export default class AwaitBlockWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: AwaitBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
block.addDependencies(this.node.expression.dependencies);
block.add_dependencies(this.node.expression.dependencies);
let isDynamic = false;
let hasIntros = false;
let hasOutros = false;
let is_dynamic = false;
let has_intros = false;
let has_outros = false;
['pending', 'then', 'catch'].forEach(status => {
const child = this.node[status];
@ -82,59 +82,59 @@ export default class AwaitBlockWrapper extends Wrapper {
block,
this,
child,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
renderer.blocks.push(branch.block);
if (branch.isDynamic) {
isDynamic = true;
if (branch.is_dynamic) {
is_dynamic = true;
// TODO should blocks update their own parents?
block.addDependencies(branch.block.dependencies);
block.add_dependencies(branch.block.dependencies);
}
if (branch.block.hasIntros) hasIntros = true;
if (branch.block.hasOutros) hasOutros = true;
if (branch.block.has_intros) has_intros = true;
if (branch.block.has_outros) has_outros = true;
this[status] = branch;
});
this.pending.block.hasUpdateMethod = isDynamic;
this.then.block.hasUpdateMethod = isDynamic;
this.catch.block.hasUpdateMethod = isDynamic;
this.pending.block.has_update_method = is_dynamic;
this.then.block.has_update_method = is_dynamic;
this.catch.block.has_update_method = is_dynamic;
this.pending.block.hasIntroMethod = hasIntros;
this.then.block.hasIntroMethod = hasIntros;
this.catch.block.hasIntroMethod = hasIntros;
this.pending.block.has_intro_method = has_intros;
this.then.block.has_intro_method = has_intros;
this.catch.block.has_intro_method = has_intros;
this.pending.block.hasOutroMethod = hasOutros;
this.then.block.hasOutroMethod = hasOutros;
this.catch.block.hasOutroMethod = hasOutros;
this.pending.block.has_outro_method = has_outros;
this.then.block.has_outro_method = has_outros;
this.catch.block.has_outro_method = has_outros;
if (hasOutros) {
block.addOutro();
if (has_outros) {
block.add_outro();
}
}
render(
block: Block,
parentNode: string,
parentNodes: string
parent_node: string,
parent_nodes: string
) {
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor);
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const update_mount_node = this.get_update_mount_node(anchor);
const snippet = this.node.expression.render(block);
const info = block.getUniqueName(`info`);
const promise = block.getUniqueName(`promise`);
const info = block.get_unique_name(`info`);
const promise = block.get_unique_name(`promise`);
block.addVariable(promise);
block.add_variable(promise);
block.maintainContext = true;
block.maintain_context = true;
const infoProps = [
const info_props = [
'ctx',
'current: null',
this.pending.block.name && `pending: ${this.pending.block.name}`,
@ -142,12 +142,12 @@ export default class AwaitBlockWrapper extends Wrapper {
this.catch.block.name && `catch: ${this.catch.block.name}`,
this.then.block.name && `value: '${this.node.value}'`,
this.catch.block.name && `error: '${this.node.error}'`,
this.pending.block.hasOutroMethod && `blocks: Array(3)`
this.pending.block.has_outro_method && `blocks: Array(3)`
].filter(Boolean);
block.builders.init.add_block(deindent`
let ${info} = {
${infoProps.join(',\n')}
${info_props.join(',\n')}
};
`);
@ -159,24 +159,24 @@ export default class AwaitBlockWrapper extends Wrapper {
${info}.block.c();
`);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_block(deindent`
${info}.block.l(${parentNodes});
${info}.block.l(${parent_nodes});
`);
}
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const anchor_node = parent_node ? 'null' : 'anchor';
const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod;
const has_transitions = this.pending.block.has_intro_method || this.pending.block.has_outro_method;
block.builders.mount.add_block(deindent`
${info}.block.m(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.mount = () => ${updateMountNode};
${info}.block.m(${initial_mount_node}, ${info}.anchor = ${anchor_node});
${info}.mount = () => ${update_mount_node};
${info}.anchor = ${anchor};
`);
if (hasTransitions) {
if (has_transitions) {
block.builders.intro.add_line(`${info}.block.i();`);
}
@ -198,7 +198,7 @@ export default class AwaitBlockWrapper extends Wrapper {
`${info}.ctx = ctx;`
);
if (this.pending.block.hasUpdateMethod) {
if (this.pending.block.has_update_method) {
block.builders.update.add_block(deindent`
if (${conditions.join(' && ')}) {
// nothing
@ -212,7 +212,7 @@ export default class AwaitBlockWrapper extends Wrapper {
`);
}
if (this.pending.block.hasOutroMethod) {
if (this.pending.block.has_outro_method) {
block.builders.outro.add_block(deindent`
for (let #i = 0; #i < 3; #i += 1) {
const block = ${info}.blocks[#i];
@ -222,7 +222,7 @@ export default class AwaitBlockWrapper extends Wrapper {
}
block.builders.destroy.add_block(deindent`
${info}.block.d(${parentNode ? '' : 'detaching'});
${info}.block.d(${parent_node ? '' : 'detaching'});
${info} = null;
`);

@ -6,7 +6,7 @@ import Body from '../../nodes/Body';
export default class BodyWrapper extends Wrapper {
node: Body;
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
this.node.handlers.forEach(handler => {
const snippet = handler.render(block);

@ -13,13 +13,13 @@ export default class DebugTagWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: DebugTag,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
const { renderer } = this;
const { component } = renderer;

@ -11,7 +11,7 @@ class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
block: Block;
fragment: FragmentWrapper;
isDynamic: boolean;
is_dynamic: boolean;
var = null;
@ -20,14 +20,14 @@ class ElseBlockWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: ElseBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.getUniqueName(`create_else_block`)
name: this.renderer.component.get_unique_name(`create_else_block`)
});
this.fragment = new FragmentWrapper(
@ -35,11 +35,11 @@ class ElseBlockWrapper extends Wrapper {
this.block,
this.node.children,
parent,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
this.isDynamic = this.block.dependencies.size > 0;
this.is_dynamic = this.block.dependencies.size > 0;
}
}
@ -60,8 +60,8 @@ export default class EachBlockWrapper extends Wrapper {
length: string;
}
contextProps: string[];
indexName: string;
context_props: string[];
index_name: string;
var = 'each';
@ -70,27 +70,27 @@ export default class EachBlockWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: EachBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
const { dependencies } = node.expression;
block.addDependencies(dependencies);
block.add_dependencies(dependencies);
this.block = block.child({
comment: create_debugging_comment(this.node, this.renderer.component),
name: renderer.component.getUniqueName('create_each_block'),
name: renderer.component.get_unique_name('create_each_block'),
key: node.key as string,
bindings: new Map(block.bindings)
});
// TODO this seems messy
this.block.hasAnimation = this.node.hasAnimation;
this.block.has_animation = this.node.has_animation;
this.indexName = this.node.index || renderer.component.getUniqueName(`${this.node.context}_index`);
this.index_name = this.node.index || renderer.component.get_unique_name(`${this.node.context}_index`);
const fixed_length = node.expression.node.type === 'ArrayExpression'
? node.expression.node.elements.length
@ -102,13 +102,13 @@ export default class EachBlockWrapper extends Wrapper {
while (renderer.component.source[c] !== 'e') c += 1;
renderer.component.code.overwrite(c, c + 4, 'length');
const each_block_value = renderer.component.getUniqueName(`${this.var}_value`);
const iterations = block.getUniqueName(`${this.var}_blocks`);
const each_block_value = renderer.component.get_unique_name(`${this.var}_value`);
const iterations = block.get_unique_name(`${this.var}_blocks`);
this.vars = {
create_each_block: this.block.name,
each_block_value,
get_each_context: renderer.component.getUniqueName(`get_${this.var}_context`),
get_each_context: renderer.component.get_unique_name(`get_${this.var}_context`),
iterations,
length: `[✂${c}-${c+4}✂]`,
@ -124,18 +124,18 @@ export default class EachBlockWrapper extends Wrapper {
node.contexts.forEach(prop => {
this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value,
property: this.indexName,
snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}`
property: this.index_name,
snippet: `${this.vars.each_block_value}[${this.index_name}]${prop.tail}`
});
});
if (this.node.index) {
this.block.getUniqueName(this.node.index); // this prevents name collisions (#1254)
this.block.get_unique_name(this.node.index); // this prevents name collisions (#1254)
}
renderer.blocks.push(this.block);
this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, stripWhitespace, nextSibling);
this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, strip_whitespace, next_sibling);
if (this.node.else) {
this.else = new ElseBlockWrapper(
@ -143,42 +143,42 @@ export default class EachBlockWrapper extends Wrapper {
block,
this,
this.node.else,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
renderer.blocks.push(this.else.block);
if (this.else.isDynamic) {
this.block.addDependencies(this.else.block.dependencies);
if (this.else.is_dynamic) {
this.block.add_dependencies(this.else.block.dependencies);
}
}
block.addDependencies(this.block.dependencies);
block.add_dependencies(this.block.dependencies);
if (this.block.hasOutros || (this.else && this.else.block.hasOutros)) {
block.addOutro();
if (this.block.has_outros || (this.else && this.else.block.has_outros)) {
block.add_outro();
}
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
if (this.fragment.nodes.length === 0) return;
const { renderer } = this;
const { component } = renderer;
const needsAnchor = this.next
? !this.next.isDomNode() :
!parentNode || !this.parent.isDomNode();
const needs_anchor = this.next
? !this.next.is_dom_node() :
!parent_node || !this.parent.is_dom_node();
this.vars.anchor = needsAnchor
? block.getUniqueName(`${this.var}_anchor`)
this.vars.anchor = needs_anchor
? block.get_unique_name(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null';
this.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`);
this.context_props = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`);
if (this.node.has_binding) this.contextProps.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.contextProps.push(`child_ctx.${this.indexName} = i;`);
if (this.node.has_binding) this.context_props.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(`child_ctx.${this.index_name} = i;`);
const snippet = this.node.expression.render(block);
@ -187,34 +187,34 @@ export default class EachBlockWrapper extends Wrapper {
renderer.blocks.push(deindent`
function ${this.vars.get_each_context}(ctx, list, i) {
const child_ctx = Object.create(ctx);
${this.contextProps}
${this.context_props}
return child_ctx;
}
`);
if (this.node.key) {
this.renderKeyed(block, parentNode, parentNodes, snippet);
this.render_keyed(block, parent_node, parent_nodes, snippet);
} else {
this.renderUnkeyed(block, parentNode, parentNodes, snippet);
this.render_unkeyed(block, parent_node, parent_nodes, snippet);
}
if (this.block.hasIntroMethod || this.block.hasOutroMethod) {
if (this.block.has_intro_method || this.block.has_outro_method) {
block.builders.intro.add_block(deindent`
for (var #i = 0; #i < ${this.vars.data_length}; #i += 1) ${this.vars.iterations}[#i].i();
`);
}
if (needsAnchor) {
block.addElement(
if (needs_anchor) {
block.add_element(
this.vars.anchor,
`@comment()`,
parentNodes && `@comment()`,
parentNode
parent_nodes && `@comment()`,
parent_node
);
}
if (this.else) {
const each_block_else = component.getUniqueName(`${this.var}_else`);
const each_block_else = component.get_unique_name(`${this.var}_else`);
block.builders.init.add_line(`var ${each_block_else} = null;`);
@ -228,20 +228,20 @@ export default class EachBlockWrapper extends Wrapper {
block.builders.mount.add_block(deindent`
if (${each_block_else}) {
${each_block_else}.m(${parentNode || '#target'}, null);
${each_block_else}.m(${parent_node || '#target'}, null);
}
`);
const initialMountNode = parentNode || `${this.vars.anchor}.parentNode`;
const initial_mount_node = parent_node || `${this.vars.anchor}.parentNode`;
if (this.else.block.hasUpdateMethod) {
if (this.else.block.has_update_method) {
block.builders.update.add_block(deindent`
if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(changed, ctx);
} else if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(ctx);
${each_block_else}.c();
${each_block_else}.m(${initialMountNode}, ${this.vars.anchor});
${each_block_else}.m(${initial_mount_node}, ${this.vars.anchor});
} else if (${each_block_else}) {
${each_block_else}.d(1);
${each_block_else} = null;
@ -257,13 +257,13 @@ export default class EachBlockWrapper extends Wrapper {
} else if (!${each_block_else}) {
${each_block_else} = ${this.else.block.name}(ctx);
${each_block_else}.c();
${each_block_else}.m(${initialMountNode}, ${this.vars.anchor});
${each_block_else}.m(${initial_mount_node}, ${this.vars.anchor});
}
`);
}
block.builders.destroy.add_block(deindent`
if (${each_block_else}) ${each_block_else}.d(${parentNode ? '' : 'detaching'});
if (${each_block_else}) ${each_block_else}.d(${parent_node ? '' : 'detaching'});
`);
}
@ -274,10 +274,10 @@ export default class EachBlockWrapper extends Wrapper {
}
}
renderKeyed(
render_keyed(
block: Block,
parentNode: string,
parentNodes: string,
parent_node: string,
parent_nodes: string,
snippet: string
) {
const {
@ -288,20 +288,20 @@ export default class EachBlockWrapper extends Wrapper {
view_length
} = this.vars;
const get_key = block.getUniqueName('get_key');
const lookup = block.getUniqueName(`${this.var}_lookup`);
const get_key = block.get_unique_name('get_key');
const lookup = block.get_unique_name(`${this.var}_lookup`);
block.addVariable(iterations, '[]');
block.addVariable(lookup, `@blank_object()`);
block.add_variable(iterations, '[]');
block.add_variable(lookup, `@blank_object()`);
if (this.fragment.nodes[0].isDomNode()) {
if (this.fragment.nodes[0].is_dom_node()) {
this.block.first = this.fragment.nodes[0].var;
} else {
this.block.first = this.block.getUniqueName('first');
this.block.addElement(
this.block.first = this.block.get_unique_name('first');
this.block.add_element(
this.block.first,
`@comment()`,
parentNodes && `@comment()`,
parent_nodes && `@comment()`,
null
);
}
@ -316,58 +316,57 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
const initialMountNode = parentNode || '#target';
const updateMountNode = this.getUpdateMountNode(anchor);
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const update_mount_node = this.get_update_mount_node(anchor);
const anchor_node = parent_node ? 'null' : 'anchor';
block.builders.create.add_block(deindent`
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].c();
`);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_block(deindent`
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].l(${parentNodes});
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].l(${parent_nodes});
`);
}
block.builders.mount.add_block(deindent`
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].m(${initialMountNode}, ${anchorNode});
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].m(${initial_mount_node}, ${anchor_node});
`);
const dynamic = this.block.hasUpdateMethod;
const dynamic = this.block.has_update_method;
const rects = block.getUniqueName('rects');
const destroy = this.node.hasAnimation
const destroy = this.node.has_animation
? `@fix_and_outro_and_destroy_block`
: this.block.hasOutros
: this.block.has_outros
? `@outro_and_destroy_block`
: `@destroy_block`;
block.builders.update.add_block(deindent`
const ${this.vars.each_block_value} = ${snippet};
${this.block.hasOutros && `@group_outros();`}
${this.node.hasAnimation && `for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
${iterations} = @update_keyed_each(${iterations}, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, ${anchor}, ${this.vars.get_each_context});
${this.node.hasAnimation && `for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`}
${this.block.hasOutros && `@check_outros();`}
${this.block.has_outros && `@group_outros();`}
${this.node.has_animation && `for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
${iterations} = @update_keyed_each(${iterations}, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${anchor}, ${this.vars.get_each_context});
${this.node.has_animation && `for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`}
${this.block.has_outros && `@check_outros();`}
`);
if (this.block.hasOutros) {
if (this.block.has_outros) {
block.builders.outro.add_block(deindent`
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].o();
`);
}
block.builders.destroy.add_block(deindent`
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].d(${parentNode ? '' : 'detaching'});
for (#i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].d(${parent_node ? '' : 'detaching'});
`);
}
renderUnkeyed(
render_unkeyed(
block: Block,
parentNode: string,
parentNodes: string,
parent_node: string,
parent_nodes: string,
snippet: string
) {
const {
@ -388,9 +387,9 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
const initialMountNode = parentNode || '#target';
const updateMountNode = this.getUpdateMountNode(anchor);
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const update_mount_node = this.get_update_mount_node(anchor);
const anchor_node = parent_node ? 'null' : 'anchor';
block.builders.create.add_block(deindent`
for (var #i = 0; #i < ${view_length}; #i += 1) {
@ -398,30 +397,30 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_block(deindent`
for (var #i = 0; #i < ${view_length}; #i += 1) {
${iterations}[#i].l(${parentNodes});
${iterations}[#i].l(${parent_nodes});
}
`);
}
block.builders.mount.add_block(deindent`
for (var #i = 0; #i < ${view_length}; #i += 1) {
${iterations}[#i].m(${initialMountNode}, ${anchorNode});
${iterations}[#i].m(${initial_mount_node}, ${anchor_node});
}
`);
const allDependencies = new Set(this.block.dependencies);
const all_dependencies = new Set(this.block.dependencies);
const { dependencies } = this.node.expression;
dependencies.forEach((dependency: string) => {
allDependencies.add(dependency);
all_dependencies.add(dependency);
});
const outroBlock = this.block.hasOutros && block.getUniqueName('outroBlock')
if (outroBlock) {
const outro_block = this.block.has_outros && block.get_unique_name('outro_block')
if (outro_block) {
block.builders.init.add_block(deindent`
function ${outroBlock}(i, detaching, local) {
function ${outro_block}(i, detaching, local) {
if (${iterations}[i]) {
if (detaching) {
@on_outro(() => {
@ -436,14 +435,14 @@ export default class EachBlockWrapper extends Wrapper {
`);
}
const condition = Array.from(allDependencies)
const condition = Array.from(all_dependencies)
.map(dependency => `changed.${dependency}`)
.join(' || ');
const has_transitions = !!(this.block.hasIntroMethod || this.block.hasOutroMethod);
const has_transitions = !!(this.block.has_intro_method || this.block.has_outro_method);
if (condition !== '') {
const forLoopBody = this.block.hasUpdateMethod
const for_loop_body = this.block.has_update_method
? deindent`
if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx);
@ -452,29 +451,29 @@ export default class EachBlockWrapper extends Wrapper {
${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c();
${has_transitions && `${iterations}[#i].i(1);`}
${iterations}[#i].m(${updateMountNode}, ${anchor});
${iterations}[#i].m(${update_mount_node}, ${anchor});
}
`
: deindent`
${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c();
${has_transitions && `${iterations}[#i].i(1);`}
${iterations}[#i].m(${updateMountNode}, ${anchor});
${iterations}[#i].m(${update_mount_node}, ${anchor});
`;
const start = this.block.hasUpdateMethod ? '0' : `${view_length}`;
const start = this.block.has_update_method ? '0' : `${view_length}`;
let remove_old_blocks;
if (this.block.hasOutros) {
if (this.block.has_outros) {
remove_old_blocks = deindent`
@group_outros();
for (; #i < ${view_length}; #i += 1) ${outroBlock}(#i, 1, 1);
for (; #i < ${view_length}; #i += 1) ${outro_block}(#i, 1, 1);
@check_outros();
`;
} else {
remove_old_blocks = deindent`
for (${this.block.hasUpdateMethod ? `` : `#i = ${this.vars.each_block_value}.${length}`}; #i < ${view_length}; #i += 1) {
for (${this.block.has_update_method ? `` : `#i = ${this.vars.each_block_value}.${length}`}; #i < ${view_length}; #i += 1) {
${iterations}[#i].d(1);
}
${!fixed_length && `${view_length} = ${this.vars.each_block_value}.${length};`}
@ -487,7 +486,7 @@ export default class EachBlockWrapper extends Wrapper {
for (var #i = ${start}; #i < ${this.vars.each_block_value}.${length}; #i += 1) {
const child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i);
${forLoopBody}
${for_loop_body}
}
${remove_old_blocks}
@ -500,10 +499,10 @@ export default class EachBlockWrapper extends Wrapper {
`);
}
if (outroBlock) {
if (outro_block) {
block.builders.outro.add_block(deindent`
${iterations} = ${iterations}.filter(Boolean);
for (let #i = 0; #i < ${view_length}; #i += 1) ${outroBlock}(#i, 0);`
for (let #i = 0; #i < ${view_length}; #i += 1) ${outro_block}(#i, 0);`
);
}

@ -14,19 +14,19 @@ export default class AttributeWrapper {
this.parent = parent;
if (node.dependencies.size > 0) {
parent.cannotUseInnerHTML();
parent.cannot_use_innerhtml();
block.addDependencies(node.dependencies);
block.add_dependencies(node.dependencies);
// special case — <option value={foo}> — see below
if (this.parent.node.name === 'option' && node.name === 'value') {
let select: ElementWrapper = this.parent;
while (select && (select.node.type !== 'Element' || select.node.name !== 'select')) select = select.parent;
if (select && select.selectBindingDependencies) {
select.selectBindingDependencies.forEach(prop => {
if (select && select.select_binding_dependencies) {
select.select_binding_dependencies.forEach(prop => {
this.node.dependencies.forEach((dependency: string) => {
this.parent.renderer.component.indirectDependencies.get(prop).add(dependency);
this.parent.renderer.component.indirect_dependencies.get(prop).add(dependency);
});
});
}
@ -38,11 +38,11 @@ export default class AttributeWrapper {
const element = this.parent;
const name = fix_attribute_casing(this.node.name);
let metadata = element.node.namespace ? null : attributeLookup[name];
if (metadata && metadata.appliesTo && !~metadata.appliesTo.indexOf(element.node.name))
let metadata = element.node.namespace ? null : attribute_lookup[name];
if (metadata && metadata.applies_to && !~metadata.applies_to.indexOf(element.node.name))
metadata = null;
const isIndirectlyBoundValue =
const is_indirectly_bound_value =
name === 'value' &&
(element.node.name === 'option' || // TODO check it's actually bound
(element.node.name === 'input' &&
@ -51,9 +51,9 @@ export default class AttributeWrapper {
/checked|group/.test(binding.name)
)));
const propertyName = isIndirectlyBoundValue
const property_name = is_indirectly_bound_value
? '__value'
: metadata && metadata.propertyName;
: metadata && metadata.property_name;
// xlink is a special case... we could maybe extend this to generic
// namespaced attributes but I'm not sure that's applicable in
@ -64,14 +64,14 @@ export default class AttributeWrapper {
? '@xlink_attr'
: '@attr';
const isLegacyInputType = element.renderer.component.compileOptions.legacy && name === 'type' && this.parent.node.name === 'input';
const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input';
const isDataSet = /^data-/.test(name) && !element.renderer.component.compileOptions.legacy && !element.node.namespace;
const camelCaseName = isDataSet ? name.replace('data-', '').replace(/(-\w)/g, function (m) {
const is_dataset = /^data-/.test(name) && !element.renderer.component.compile_options.legacy && !element.node.namespace;
const camel_case_name = is_dataset ? name.replace('data-', '').replace(/(-\w)/g, function (m) {
return m[1].toUpperCase();
}) : name;
if (this.node.isDynamic) {
if (this.node.is_dynamic) {
let value;
// TODO some of this code is repeated in Tag.ts — would be good to
@ -88,7 +88,7 @@ export default class AttributeWrapper {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
return chunk.getPrecedence() <= 13
return chunk.get_precedence() <= 13
? `(${chunk.render()})`
: chunk.render();
}
@ -96,32 +96,32 @@ export default class AttributeWrapper {
.join(' + ');
}
const isSelectValueAttribute =
const is_select_value_attribute =
name === 'value' && element.node.name === 'select';
const shouldCache = (this.node.shouldCache || isSelectValueAttribute);
const should_cache = (this.node.should_cache || is_select_value_attribute);
const last = shouldCache && block.getUniqueName(
const last = should_cache && block.get_unique_name(
`${element.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`
);
if (shouldCache) block.addVariable(last);
if (should_cache) block.add_variable(last);
let updater;
const init = shouldCache ? `${last} = ${value}` : value;
const init = should_cache ? `${last} = ${value}` : value;
if (isLegacyInputType) {
if (is_legacy_input_type) {
block.builders.hydrate.add_line(
`@set_input_type(${element.var}, ${init});`
);
updater = `@set_input_type(${element.var}, ${shouldCache ? last : value});`;
} else if (isSelectValueAttribute) {
updater = `@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) {
// annoying special case
const isMultipleSelect = element.getStaticAttributeValue('multiple');
const i = block.getUniqueName('i');
const option = block.getUniqueName('option');
const is_multiple_select = element.get_static_attribute_value('multiple');
const i = block.get_unique_name('i');
const option = block.get_unique_name('option');
const ifStatement = isMultipleSelect
const if_statement = is_multiple_select
? deindent`
${option}.selected = ~${last}.indexOf(${option}.__value);`
: deindent`
@ -134,7 +134,7 @@ export default class AttributeWrapper {
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
var ${option} = ${element.var}.options[${i}];
${ifStatement}
${if_statement}
}
`;
@ -142,36 +142,36 @@ export default class AttributeWrapper {
${last} = ${value};
${updater}
`);
} else if (propertyName) {
} else if (property_name) {
block.builders.hydrate.add_line(
`${element.var}.${propertyName} = ${init};`
`${element.var}.${property_name} = ${init};`
);
updater = `${element.var}.${propertyName} = ${shouldCache ? last : value};`;
} else if (isDataSet) {
updater = `${element.var}.${property_name} = ${should_cache ? last : value};`;
} else if (is_dataset) {
block.builders.hydrate.add_line(
`${element.var}.dataset.${camelCaseName} = ${init};`
`${element.var}.dataset.${camel_case_name} = ${init};`
);
updater = `${element.var}.dataset.${camelCaseName} = ${shouldCache ? last : value};`;
updater = `${element.var}.dataset.${camel_case_name} = ${should_cache ? last : value};`;
} else {
block.builders.hydrate.add_line(
`${method}(${element.var}, "${name}", ${init});`
);
updater = `${method}(${element.var}, "${name}", ${shouldCache ? last : value});`;
updater = `${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
}
// only add an update if mutations are involved (or it's a select?)
const dependencies = this.node.get_dependencies();
if (dependencies.length > 0 || isSelectValueAttribute) {
const changedCheck = (
(block.hasOutros ? `!#current || ` : '') +
if (dependencies.length > 0 || is_select_value_attribute) {
const changed_check = (
(block.has_outros ? `!#current || ` : '') +
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
);
const updateCachedValue = `${last} !== (${last} = ${value})`;
const update_cached_value = `${last} !== (${last} = ${value})`;
const condition = shouldCache
? (dependencies.length ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue)
: changedCheck;
const condition = should_cache
? (dependencies.length ? `(${changed_check}) && ${update_cached_value}` : update_cached_value)
: changed_check;
block.builders.update.add_conditional(
condition,
@ -179,36 +179,36 @@ export default class AttributeWrapper {
);
}
} else {
const value = this.node.getValue(block);
const value = this.node.get_value(block);
const statement = (
isLegacyInputType
is_legacy_input_type
? `@set_input_type(${element.var}, ${value});`
: propertyName
? `${element.var}.${propertyName} = ${value};`
: isDataSet
? `${element.var}.dataset.${camelCaseName} = ${value};`
: property_name
? `${element.var}.${property_name} = ${value};`
: is_dataset
? `${element.var}.dataset.${camel_case_name} = ${value};`
: `${method}(${element.var}, "${name}", ${value === true ? '""' : value});`
);
block.builders.hydrate.add_line(statement);
// special case autofocus. has to be handled in a bit of a weird way
if (this.node.isTrue && name === 'autofocus') {
if (this.node.is_true && name === 'autofocus') {
block.autofocus = element.var;
}
}
if (isIndirectlyBoundValue) {
const updateValue = `${element.var}.value = ${element.var}.__value;`;
if (is_indirectly_bound_value) {
const update_value = `${element.var}.value = ${element.var}.__value;`;
block.builders.hydrate.add_line(updateValue);
if (this.node.isDynamic) block.builders.update.add_line(updateValue);
block.builders.hydrate.add_line(update_value);
if (this.node.is_dynamic) block.builders.update.add_line(update_value);
}
}
stringify() {
if (this.node.isTrue) return '';
if (this.node.is_true) return '';
const value = this.node.chunks;
if (value.length === 0) return `=""`;
@ -222,13 +222,13 @@ export default class AttributeWrapper {
}
// source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
const attributeLookup = {
accept: { appliesTo: ['form', 'input'] },
'accept-charset': { propertyName: 'acceptCharset', appliesTo: ['form'] },
accesskey: { propertyName: 'accessKey' },
action: { appliesTo: ['form'] },
const attribute_lookup = {
accept: { applies_to: ['form', 'input'] },
'accept-charset': { property_name: 'acceptCharset', applies_to: ['form'] },
accesskey: { property_name: 'accessKey' },
action: { applies_to: ['form'] },
align: {
appliesTo: [
applies_to: [
'applet',
'caption',
'col',
@ -245,16 +245,16 @@ const attributeLookup = {
'tr',
],
},
allowfullscreen: { propertyName: 'allowFullscreen', appliesTo: ['iframe'] },
alt: { appliesTo: ['applet', 'area', 'img', 'input'] },
async: { appliesTo: ['script'] },
autocomplete: { appliesTo: ['form', 'input'] },
autofocus: { appliesTo: ['button', 'input', 'keygen', 'select', 'textarea'] },
autoplay: { appliesTo: ['audio', 'video'] },
autosave: { appliesTo: ['input'] },
allowfullscreen: { property_name: 'allowFullscreen', applies_to: ['iframe'] },
alt: { applies_to: ['applet', 'area', 'img', 'input'] },
async: { applies_to: ['script'] },
autocomplete: { applies_to: ['form', 'input'] },
autofocus: { applies_to: ['button', 'input', 'keygen', 'select', 'textarea'] },
autoplay: { applies_to: ['audio', 'video'] },
autosave: { applies_to: ['input'] },
bgcolor: {
propertyName: 'bgColor',
appliesTo: [
property_name: 'bgColor',
applies_to: [
'body',
'col',
'colgroup',
@ -267,31 +267,31 @@ const attributeLookup = {
'tr',
],
},
border: { appliesTo: ['img', 'object', 'table'] },
buffered: { appliesTo: ['audio', 'video'] },
challenge: { appliesTo: ['keygen'] },
charset: { appliesTo: ['meta', 'script'] },
checked: { appliesTo: ['command', 'input'] },
cite: { appliesTo: ['blockquote', 'del', 'ins', 'q'] },
class: { propertyName: 'className' },
code: { appliesTo: ['applet'] },
codebase: { propertyName: 'codeBase', appliesTo: ['applet'] },
color: { appliesTo: ['basefont', 'font', 'hr'] },
cols: { appliesTo: ['textarea'] },
colspan: { propertyName: 'colSpan', appliesTo: ['td', 'th'] },
content: { appliesTo: ['meta'] },
contenteditable: { propertyName: 'contentEditable' },
border: { applies_to: ['img', 'object', 'table'] },
buffered: { applies_to: ['audio', 'video'] },
challenge: { applies_to: ['keygen'] },
charset: { applies_to: ['meta', 'script'] },
checked: { applies_to: ['command', 'input'] },
cite: { applies_to: ['blockquote', 'del', 'ins', 'q'] },
class: { property_name: 'className' },
code: { applies_to: ['applet'] },
codebase: { property_name: 'codeBase', applies_to: ['applet'] },
color: { applies_to: ['basefont', 'font', 'hr'] },
cols: { applies_to: ['textarea'] },
colspan: { property_name: 'colSpan', applies_to: ['td', 'th'] },
content: { applies_to: ['meta'] },
contenteditable: { property_name: 'contentEditable' },
contextmenu: {},
controls: { appliesTo: ['audio', 'video'] },
coords: { appliesTo: ['area'] },
data: { appliesTo: ['object'] },
datetime: { propertyName: 'dateTime', appliesTo: ['del', 'ins', 'time'] },
default: { appliesTo: ['track'] },
defer: { appliesTo: ['script'] },
controls: { applies_to: ['audio', 'video'] },
coords: { applies_to: ['area'] },
data: { applies_to: ['object'] },
datetime: { property_name: 'dateTime', applies_to: ['del', 'ins', 'time'] },
default: { applies_to: ['track'] },
defer: { applies_to: ['script'] },
dir: {},
dirname: { propertyName: 'dirName', appliesTo: ['input', 'textarea'] },
dirname: { property_name: 'dirName', applies_to: ['input', 'textarea'] },
disabled: {
appliesTo: [
applies_to: [
'button',
'command',
'fieldset',
@ -303,13 +303,13 @@ const attributeLookup = {
'textarea',
],
},
download: { appliesTo: ['a', 'area'] },
download: { applies_to: ['a', 'area'] },
draggable: {},
dropzone: {},
enctype: { appliesTo: ['form'] },
for: { propertyName: 'htmlFor', appliesTo: ['label', 'output'] },
enctype: { applies_to: ['form'] },
for: { property_name: 'htmlFor', applies_to: ['label', 'output'] },
form: {
appliesTo: [
applies_to: [
'button',
'fieldset',
'input',
@ -323,38 +323,38 @@ const attributeLookup = {
'textarea',
],
},
formaction: { appliesTo: ['input', 'button'] },
headers: { appliesTo: ['td', 'th'] },
formaction: { applies_to: ['input', 'button'] },
headers: { applies_to: ['td', 'th'] },
height: {
appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
applies_to: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
},
hidden: {},
high: { appliesTo: ['meter'] },
href: { appliesTo: ['a', 'area', 'base', 'link'] },
hreflang: { appliesTo: ['a', 'area', 'link'] },
'http-equiv': { propertyName: 'httpEquiv', appliesTo: ['meta'] },
icon: { appliesTo: ['command'] },
high: { applies_to: ['meter'] },
href: { applies_to: ['a', 'area', 'base', 'link'] },
hreflang: { applies_to: ['a', 'area', 'link'] },
'http-equiv': { property_name: 'httpEquiv', applies_to: ['meta'] },
icon: { applies_to: ['command'] },
id: {},
indeterminate: { appliesTo: ['input'] },
ismap: { propertyName: 'isMap', appliesTo: ['img'] },
indeterminate: { applies_to: ['input'] },
ismap: { property_name: 'isMap', applies_to: ['img'] },
itemprop: {},
keytype: { appliesTo: ['keygen'] },
kind: { appliesTo: ['track'] },
label: { appliesTo: ['track'] },
keytype: { applies_to: ['keygen'] },
kind: { applies_to: ['track'] },
label: { applies_to: ['track'] },
lang: {},
language: { appliesTo: ['script'] },
loop: { appliesTo: ['audio', 'bgsound', 'marquee', 'video'] },
low: { appliesTo: ['meter'] },
manifest: { appliesTo: ['html'] },
max: { appliesTo: ['input', 'meter', 'progress'] },
maxlength: { propertyName: 'maxLength', appliesTo: ['input', 'textarea'] },
media: { appliesTo: ['a', 'area', 'link', 'source', 'style'] },
method: { appliesTo: ['form'] },
min: { appliesTo: ['input', 'meter'] },
multiple: { appliesTo: ['input', 'select'] },
muted: { appliesTo: ['audio', 'video'] },
language: { applies_to: ['script'] },
loop: { applies_to: ['audio', 'bgsound', 'marquee', 'video'] },
low: { applies_to: ['meter'] },
manifest: { applies_to: ['html'] },
max: { applies_to: ['input', 'meter', 'progress'] },
maxlength: { property_name: 'maxLength', applies_to: ['input', 'textarea'] },
media: { applies_to: ['a', 'area', 'link', 'source', 'style'] },
method: { applies_to: ['form'] },
min: { applies_to: ['input', 'meter'] },
multiple: { applies_to: ['input', 'select'] },
muted: { applies_to: ['audio', 'video'] },
name: {
appliesTo: [
applies_to: [
'button',
'form',
'fieldset',
@ -370,33 +370,33 @@ const attributeLookup = {
'param',
],
},
novalidate: { propertyName: 'noValidate', appliesTo: ['form'] },
open: { appliesTo: ['details'] },
optimum: { appliesTo: ['meter'] },
pattern: { appliesTo: ['input'] },
ping: { appliesTo: ['a', 'area'] },
placeholder: { appliesTo: ['input', 'textarea'] },
poster: { appliesTo: ['video'] },
preload: { appliesTo: ['audio', 'video'] },
radiogroup: { appliesTo: ['command'] },
readonly: { propertyName: 'readOnly', appliesTo: ['input', 'textarea'] },
rel: { appliesTo: ['a', 'area', 'link'] },
required: { appliesTo: ['input', 'select', 'textarea'] },
reversed: { appliesTo: ['ol'] },
rows: { appliesTo: ['textarea'] },
rowspan: { propertyName: 'rowSpan', appliesTo: ['td', 'th'] },
sandbox: { appliesTo: ['iframe'] },
scope: { appliesTo: ['th'] },
scoped: { appliesTo: ['style'] },
seamless: { appliesTo: ['iframe'] },
selected: { appliesTo: ['option'] },
shape: { appliesTo: ['a', 'area'] },
size: { appliesTo: ['input', 'select'] },
sizes: { appliesTo: ['link', 'img', 'source'] },
span: { appliesTo: ['col', 'colgroup'] },
novalidate: { property_name: 'noValidate', applies_to: ['form'] },
open: { applies_to: ['details'] },
optimum: { applies_to: ['meter'] },
pattern: { applies_to: ['input'] },
ping: { applies_to: ['a', 'area'] },
placeholder: { applies_to: ['input', 'textarea'] },
poster: { applies_to: ['video'] },
preload: { applies_to: ['audio', 'video'] },
radiogroup: { applies_to: ['command'] },
readonly: { property_name: 'readOnly', applies_to: ['input', 'textarea'] },
rel: { applies_to: ['a', 'area', 'link'] },
required: { applies_to: ['input', 'select', 'textarea'] },
reversed: { applies_to: ['ol'] },
rows: { applies_to: ['textarea'] },
rowspan: { property_name: 'rowSpan', applies_to: ['td', 'th'] },
sandbox: { applies_to: ['iframe'] },
scope: { applies_to: ['th'] },
scoped: { applies_to: ['style'] },
seamless: { applies_to: ['iframe'] },
selected: { applies_to: ['option'] },
shape: { applies_to: ['a', 'area'] },
size: { applies_to: ['input', 'select'] },
sizes: { applies_to: ['link', 'img', 'source'] },
span: { applies_to: ['col', 'colgroup'] },
spellcheck: {},
src: {
appliesTo: [
applies_to: [
'audio',
'embed',
'iframe',
@ -408,18 +408,18 @@ const attributeLookup = {
'video',
],
},
srcdoc: { appliesTo: ['iframe'] },
srclang: { appliesTo: ['track'] },
srcset: { appliesTo: ['img'] },
start: { appliesTo: ['ol'] },
step: { appliesTo: ['input'] },
style: { propertyName: 'style.cssText' },
summary: { appliesTo: ['table'] },
tabindex: { propertyName: 'tabIndex' },
target: { appliesTo: ['a', 'area', 'base', 'form'] },
srcdoc: { applies_to: ['iframe'] },
srclang: { applies_to: ['track'] },
srcset: { applies_to: ['img'] },
start: { applies_to: ['ol'] },
step: { applies_to: ['input'] },
style: { property_name: 'style.cssText' },
summary: { applies_to: ['table'] },
tabindex: { property_name: 'tabIndex' },
target: { applies_to: ['a', 'area', 'base', 'form'] },
title: {},
type: {
appliesTo: [
applies_to: [
'button',
'command',
'embed',
@ -430,9 +430,9 @@ const attributeLookup = {
'menu',
],
},
usemap: { propertyName: 'useMap', appliesTo: ['img', 'input', 'object'] },
usemap: { property_name: 'useMap', applies_to: ['img', 'input', 'object'] },
value: {
appliesTo: [
applies_to: [
'button',
'option',
'input',
@ -444,14 +444,14 @@ const attributeLookup = {
'textarea',
],
},
volume: { appliesTo: ['audio', 'video'] },
volume: { applies_to: ['audio', 'video'] },
width: {
appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
applies_to: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
},
wrap: { appliesTo: ['textarea'] },
wrap: { applies_to: ['textarea'] },
};
Object.keys(attributeLookup).forEach(name => {
const metadata = attributeLookup[name];
if (!metadata.propertyName) metadata.propertyName = name;
Object.keys(attribute_lookup).forEach(name => {
const metadata = attribute_lookup[name];
if (!metadata.property_name) metadata.property_name = name;
});

@ -10,7 +10,7 @@ import EachBlock from '../../../nodes/EachBlock';
import { Node as INode } from '../../../../interfaces';
// TODO this should live in a specific binding
const readOnlyMediaAttributes = new Set([
const read_only_media_attributes = new Set([
'duration',
'buffered',
'seekable',
@ -29,15 +29,14 @@ export default class BindingWrapper {
object: string;
handler: {
usesContext: boolean;
uses_context: boolean;
mutation: string;
contextual_dependencies: Set<string>,
snippet?: string
};
snippet: string;
initialUpdate: string;
isReadOnly: boolean;
needsLock: boolean;
is_readonly: boolean;
needs_lock: boolean;
constructor(block: Block, node: Binding, parent: ElementWrapper) {
this.node = node;
@ -45,23 +44,23 @@ export default class BindingWrapper {
const { dependencies } = this.node.expression;
block.addDependencies(dependencies);
block.add_dependencies(dependencies);
// TODO does this also apply to e.g. `<input type='checkbox' bind:group='foo'>`?
if (parent.node.name === 'select') {
parent.selectBindingDependencies = dependencies;
parent.select_binding_dependencies = dependencies;
dependencies.forEach((prop: string) => {
parent.renderer.component.indirectDependencies.set(prop, new Set());
parent.renderer.component.indirect_dependencies.set(prop, new Set());
});
}
if (node.isContextual) {
if (node.is_contextual) {
// we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced
const { name } = get_object(this.node.expression.node);
const eachBlock = this.parent.node.scope.getOwner(name);
const each_block = this.parent.node.scope.get_owner(name);
(eachBlock as EachBlock).has_binding = true;
(each_block as EachBlock).has_binding = true;
}
this.object = get_object(this.node.expression.node).name;
@ -71,28 +70,28 @@ export default class BindingWrapper {
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
// view to model
this.handler = getEventHandler(this, parent.renderer, block, this.object, contextless_snippet);
this.handler = get_event_handler(this, parent.renderer, block, this.object, contextless_snippet);
this.snippet = this.node.expression.render(block);
const type = parent.node.getStaticAttributeValue('type');
const type = parent.node.get_static_attribute_value('type');
this.isReadOnly = (
this.is_readonly = (
dimensions.test(this.node.name) ||
(parent.node.isMediaNode() && readOnlyMediaAttributes.has(this.node.name)) ||
(parent.node.is_media_node() && read_only_media_attributes.has(this.node.name)) ||
(parent.node.name === 'input' && type === 'file') // TODO others?
);
this.needsLock = this.node.name === 'currentTime'; // TODO others?
this.needs_lock = this.node.name === 'currentTime'; // TODO others?
}
get_dependencies() {
const dependencies = new Set(this.node.expression.dependencies);
this.node.expression.dependencies.forEach((prop: string) => {
const indirectDependencies = this.parent.renderer.component.indirectDependencies.get(prop);
if (indirectDependencies) {
indirectDependencies.forEach(indirectDependency => {
const indirect_dependencies = this.parent.renderer.component.indirect_dependencies.get(prop);
if (indirect_dependencies) {
indirect_dependencies.forEach(indirectDependency => {
dependencies.add(indirectDependency);
});
}
@ -101,34 +100,34 @@ export default class BindingWrapper {
return dependencies;
}
isReadOnlyMediaAttribute() {
return readOnlyMediaAttributes.has(this.node.name);
is_readonly_media_attribute() {
return read_only_media_attributes.has(this.node.name);
}
render(block: Block, lock: string) {
if (this.isReadOnly) return;
if (this.is_readonly) return;
const { parent } = this;
let updateConditions: string[] = this.needsLock ? [`!${lock}`] : [];
let update_conditions: string[] = this.needs_lock ? [`!${lock}`] : [];
const dependencyArray = [...this.node.expression.dependencies]
const dependency_array = [...this.node.expression.dependencies]
if (dependencyArray.length === 1) {
updateConditions.push(`changed.${dependencyArray[0]}`)
} else if (dependencyArray.length > 1) {
updateConditions.push(
`(${dependencyArray.map(prop => `changed.${prop}`).join(' || ')})`
if (dependency_array.length === 1) {
update_conditions.push(`changed.${dependency_array[0]}`)
} else if (dependency_array.length > 1) {
update_conditions.push(
`(${dependency_array.map(prop => `changed.${prop}`).join(' || ')})`
)
}
// model to view
let updateDom = getDomUpdater(parent, this);
let update_dom = get_dom_updater(parent, this);
// special cases
switch (this.node.name) {
case 'group':
const bindingGroup = getBindingGroup(parent.renderer, this.node.expression.node);
const bindingGroup = get_binding_group(parent.renderer, this.node.expression.node);
block.builders.hydrate.add_line(
`ctx.$$binding_groups[${bindingGroup}].push(${parent.var});`
@ -141,43 +140,43 @@ export default class BindingWrapper {
case 'currentTime':
case 'volume':
updateConditions.push(`!isNaN(${this.snippet})`);
update_conditions.push(`!isNaN(${this.snippet})`);
break;
case 'paused':
// this is necessary to prevent audio restarting by itself
const last = block.getUniqueName(`${parent.var}_is_paused`);
block.addVariable(last, 'true');
const last = block.get_unique_name(`${parent.var}_is_paused`);
block.add_variable(last, 'true');
updateConditions.push(`${last} !== (${last} = ${this.snippet})`);
updateDom = `${parent.var}[${last} ? "pause" : "play"]();`;
update_conditions.push(`${last} !== (${last} = ${this.snippet})`);
update_dom = `${parent.var}[${last} ? "pause" : "play"]();`;
break;
case 'value':
if (parent.getStaticAttributeValue('type') === 'file') {
updateDom = null;
if (parent.get_static_attribute_value('type') === 'file') {
update_dom = null;
}
}
if (updateDom) {
if (update_dom) {
block.builders.update.add_line(
updateConditions.length ? `if (${updateConditions.join(' && ')}) ${updateDom}` : updateDom
update_conditions.length ? `if (${update_conditions.join(' && ')}) ${update_dom}` : update_dom
);
}
if (!/(currentTime|paused)/.test(this.node.name)) {
block.builders.mount.add_block(updateDom);
block.builders.mount.add_block(update_dom);
}
}
}
function getDomUpdater(
function get_dom_updater(
element: ElementWrapper,
binding: BindingWrapper
) {
const { node } = element;
if (binding.isReadOnlyMediaAttribute()) {
if (binding.is_readonly_media_attribute()) {
return null;
}
@ -186,13 +185,13 @@ function getDomUpdater(
}
if (node.name === 'select') {
return node.getStaticAttributeValue('multiple') === true ?
return node.get_static_attribute_value('multiple') === true ?
`@select_options(${element.var}, ${binding.snippet})` :
`@select_option(${element.var}, ${binding.snippet})`;
}
if (binding.node.name === 'group') {
const type = node.getStaticAttributeValue('type');
const type = node.get_static_attribute_value('type');
const condition = type === 'checkbox'
? `~${binding.snippet}.indexOf(${element.var}.__value)`
@ -204,16 +203,16 @@ function getDomUpdater(
return `${element.var}.${binding.node.name} = ${binding.snippet};`;
}
function getBindingGroup(renderer: Renderer, value: Node) {
function get_binding_group(renderer: Renderer, value: Node) {
const { parts } = flatten_reference(value); // TODO handle cases involving computed member expressions
const keypath = parts.join('.');
// TODO handle contextual bindings — `keypath` should include unique ID of
// each block that provides context
let index = renderer.bindingGroups.indexOf(keypath);
let index = renderer.binding_groups.indexOf(keypath);
if (index === -1) {
index = renderer.bindingGroups.length;
renderer.bindingGroups.push(keypath);
index = renderer.binding_groups.length;
renderer.binding_groups.push(keypath);
}
return index;
@ -225,14 +224,14 @@ function mutate_store(store, value, tail) {
: `${store}.set(${value});`;
}
function getEventHandler(
function get_event_handler(
binding: BindingWrapper,
renderer: Renderer,
block: Block,
name: string,
snippet: string
) {
const value = getValueFromDom(renderer, binding.parent, binding);
const value = get_value_from_dom(renderer, binding.parent, binding);
const store = binding.object[0] === '$' ? binding.object.slice(1) : null;
let tail = '';
@ -241,11 +240,11 @@ function getEventHandler(
tail = renderer.component.source.slice(start, end);
}
if (binding.node.isContextual) {
if (binding.node.is_contextual) {
const { object, property, snippet } = block.bindings.get(name);
return {
usesContext: true,
uses_context: true,
mutation: store
? mutate_store(store, value, tail)
: `${snippet}${tail} = ${value};`,
@ -259,7 +258,7 @@ function getEventHandler(
if (binding.node.expression.node.type === 'MemberExpression') {
return {
usesContext: binding.node.expression.usesContext,
uses_context: binding.node.expression.uses_context,
mutation,
contextual_dependencies: binding.node.expression.contextual_dependencies,
snippet
@ -267,13 +266,13 @@ function getEventHandler(
}
return {
usesContext: false,
uses_context: false,
mutation,
contextual_dependencies: new Set()
};
}
function getValueFromDom(
function get_value_from_dom(
renderer: Renderer,
element: ElementWrapper,
binding: BindingWrapper
@ -287,16 +286,16 @@ function getValueFromDom(
// <select bind:value='selected>
if (node.name === 'select') {
return node.getStaticAttributeValue('multiple') === true ?
return node.get_static_attribute_value('multiple') === true ?
`@select_multiple_value(this)` :
`@select_value(this)`;
}
const type = node.getStaticAttributeValue('type');
const type = node.get_static_attribute_value('type');
// <input type='checkbox' bind:group='foo'>
if (name === 'group') {
const bindingGroup = getBindingGroup(renderer, binding.node.expression.node);
const bindingGroup = get_binding_group(renderer, binding.node.expression.node);
if (type === 'checkbox') {
return `@get_binding_group_value($$binding_groups[${bindingGroup}])`;
}

@ -16,15 +16,14 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
parent: ElementWrapper;
render(block: Block) {
const styleProps = optimizeStyle(this.node.chunks);
if (!styleProps) return super.render(block);
const style_props = optimize_style(this.node.chunks);
if (!style_props) return super.render(block);
styleProps.forEach((prop: StyleProp) => {
style_props.forEach((prop: StyleProp) => {
let value;
if (isDynamic(prop.value)) {
const propDependencies = new Set();
let shouldCache;
if (is_dynamic(prop.value)) {
const prop_dependencies = new Set();
value =
((prop.value.length === 1 || prop.value[0].type === 'Text') ? '' : `"" + `) +
@ -35,17 +34,17 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
} else {
const snippet = chunk.render();
add_to_set(propDependencies, chunk.dependencies);
add_to_set(prop_dependencies, chunk.dependencies);
return chunk.getPrecedence() <= 13 ? `(${snippet})` : snippet;
return chunk.get_precedence() <= 13 ? `(${snippet})` : snippet;
}
})
.join(' + ');
if (propDependencies.size) {
const dependencies = Array.from(propDependencies);
if (prop_dependencies.size) {
const dependencies = Array.from(prop_dependencies);
const condition = (
(block.hasOutros ? `!#current || ` : '') +
(block.has_outros ? `!#current || ` : '') +
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
);
@ -65,7 +64,7 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
}
}
function optimizeStyle(value: Node[]) {
function optimize_style(value: Node[]) {
const props: { key: string, value: Node[] }[] = [];
let chunks = value.slice();
@ -74,26 +73,26 @@ function optimizeStyle(value: Node[]) {
if (chunk.type !== 'Text') return null;
const keyMatch = /^\s*([\w-]+):\s*/.exec(chunk.data);
if (!keyMatch) return null;
const key_match = /^\s*([\w-]+):\s*/.exec(chunk.data);
if (!key_match) return null;
const key = keyMatch[1];
const key = key_match[1];
const offset = keyMatch.index + keyMatch[0].length;
const remainingData = chunk.data.slice(offset);
const offset = key_match.index + key_match[0].length;
const remaining_data = chunk.data.slice(offset);
if (remainingData) {
if (remaining_data) {
chunks[0] = {
start: chunk.start + offset,
end: chunk.end,
type: 'Text',
data: remainingData
data: remaining_data
};
} else {
chunks.shift();
}
const result = getStyleValue(chunks);
const result = get_style_value(chunks);
if (!result) return null;
props.push({ key, value: result.value });
@ -103,11 +102,11 @@ function optimizeStyle(value: Node[]) {
return props;
}
function getStyleValue(chunks: Node[]) {
function get_style_value(chunks: Node[]) {
const value: Node[] = [];
let inUrl = false;
let quoteMark = null;
let in_url = false;
let quote_mark = null;
let escaped = false;
while (chunks.length) {
@ -122,15 +121,15 @@ function getStyleValue(chunks: Node[]) {
escaped = false;
} else if (char === '\\') {
escaped = true;
} else if (char === quoteMark) {
quoteMark === null;
} else if (char === quote_mark) {
quote_mark === null;
} else if (char === '"' || char === "'") {
quoteMark = char;
} else if (char === ')' && inUrl) {
inUrl = false;
quote_mark = char;
} else if (char === ')' && in_url) {
in_url = false;
} else if (char === 'u' && chunk.data.slice(c, c + 4) === 'url(') {
inUrl = true;
} else if (char === ';' && !inUrl && !quoteMark) {
in_url = true;
} else if (char === ';' && !in_url && !quote_mark) {
break;
}
@ -147,14 +146,14 @@ function getStyleValue(chunks: Node[]) {
}
while (/[;\s]/.test(chunk.data[c])) c += 1;
const remainingData = chunk.data.slice(c);
const remaining_data = chunk.data.slice(c);
if (remainingData) {
if (remaining_data) {
chunks.unshift({
start: chunk.start + c,
end: chunk.end,
type: 'Text',
data: remainingData
data: remaining_data
});
break;
@ -172,6 +171,6 @@ function getStyleValue(chunks: Node[]) {
};
}
function isDynamic(value: Node[]) {
function is_dynamic(value: Node[]) {
return value.length > 1 || value[0].type !== 'Text';
}

@ -16,71 +16,71 @@ import { dimensions } from '../../../../utils/patterns';
import Binding from './Binding';
import InlineComponentWrapper from '../InlineComponent';
import add_to_set from '../../../utils/add_to_set';
import addEventHandlers from '../shared/addEventHandlers';
import addActions from '../shared/addActions';
import add_event_handlers from '../shared/add_event_handlers';
import add_actions from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_context_merger } from '../shared/get_context_merger';
const events = [
{
eventNames: ['input'],
event_names: ['input'],
filter: (node: Element, name: string) =>
node.name === 'textarea' ||
node.name === 'input' && !/radio|checkbox|range/.test(node.getStaticAttributeValue('type'))
node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type'))
},
{
eventNames: ['change'],
event_names: ['change'],
filter: (node: Element, name: string) =>
node.name === 'select' ||
node.name === 'input' && /radio|checkbox/.test(node.getStaticAttributeValue('type'))
node.name === 'input' && /radio|checkbox/.test(node.get_static_attribute_value('type'))
},
{
eventNames: ['change', 'input'],
event_names: ['change', 'input'],
filter: (node: Element, name: string) =>
node.name === 'input' && node.getStaticAttributeValue('type') === 'range'
node.name === 'input' && node.get_static_attribute_value('type') === 'range'
},
{
eventNames: ['resize'],
event_names: ['resize'],
filter: (node: Element, name: string) =>
dimensions.test(name)
},
// media events
{
eventNames: ['timeupdate'],
event_names: ['timeupdate'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
(name === 'currentTime' || name === 'played')
},
{
eventNames: ['durationchange'],
event_names: ['durationchange'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
name === 'duration'
},
{
eventNames: ['play', 'pause'],
event_names: ['play', 'pause'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
name === 'paused'
},
{
eventNames: ['progress'],
event_names: ['progress'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
name === 'buffered'
},
{
eventNames: ['loadedmetadata'],
event_names: ['loadedmetadata'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
(name === 'buffered' || name === 'seekable')
},
{
eventNames: ['volumechange'],
event_names: ['volumechange'],
filter: (node: Element, name: string) =>
node.isMediaNode() &&
node.is_media_node() &&
name === 'volume'
}
];
@ -90,10 +90,10 @@ export default class ElementWrapper extends Wrapper {
fragment: FragmentWrapper;
attributes: AttributeWrapper[];
bindings: Binding[];
classDependencies: string[];
class_dependencies: string[];
slot_block: Block;
selectBindingDependencies?: Set<string>;
select_binding_dependencies?: Set<string>;
var: string;
@ -102,13 +102,13 @@ export default class ElementWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: Element,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.var = node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
this.classDependencies = [];
this.class_dependencies = [];
this.attributes = this.node.attributes.map(attribute => {
if (attribute.name === 'slot') {
@ -127,12 +127,12 @@ export default class ElementWrapper extends Wrapper {
}
if (owner && owner.node.type === 'InlineComponent') {
const name = attribute.getStaticValue();
const name = attribute.get_static_value();
if (!(owner as InlineComponentWrapper).slots.has(name)) {
const child_block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.getUniqueName(`create_${sanitize(name)}_slot`)
name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`)
});
const fn = get_context_merger(this.node.lets);
@ -161,46 +161,46 @@ export default class ElementWrapper extends Wrapper {
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
if (node.intro || node.outro) {
if (node.intro) block.addIntro(node.intro.is_local);
if (node.outro) block.addOutro(node.outro.is_local);
if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local);
}
if (node.animation) {
block.addAnimation();
block.add_animation();
}
// add directive and handler dependencies
[node.animation, node.outro, ...node.actions, ...node.classes].forEach(directive => {
if (directive && directive.expression) {
block.addDependencies(directive.expression.dependencies);
block.add_dependencies(directive.expression.dependencies);
}
});
node.handlers.forEach(handler => {
if (handler.expression) {
block.addDependencies(handler.expression.dependencies);
block.add_dependencies(handler.expression.dependencies);
}
});
if (this.parent) {
if (node.actions.length > 0) this.parent.cannotUseInnerHTML();
if (node.animation) this.parent.cannotUseInnerHTML();
if (node.bindings.length > 0) this.parent.cannotUseInnerHTML();
if (node.classes.length > 0) this.parent.cannotUseInnerHTML();
if (node.intro || node.outro) this.parent.cannotUseInnerHTML();
if (node.handlers.length > 0) this.parent.cannotUseInnerHTML();
if (node.actions.length > 0) this.parent.cannot_use_innerhtml();
if (node.animation) this.parent.cannot_use_innerhtml();
if (node.bindings.length > 0) this.parent.cannot_use_innerhtml();
if (node.classes.length > 0) this.parent.cannot_use_innerhtml();
if (node.intro || node.outro) this.parent.cannot_use_innerhtml();
if (node.handlers.length > 0) this.parent.cannot_use_innerhtml();
if (this.node.name === 'option') this.parent.cannotUseInnerHTML();
if (this.node.name === 'option') this.parent.cannot_use_innerhtml();
if (renderer.options.dev) {
this.parent.cannotUseInnerHTML(); // need to use add_location
this.parent.cannot_use_innerhtml(); // need to use add_location
}
}
this.fragment = new FragmentWrapper(renderer, block, node.children, this, stripWhitespace, nextSibling);
this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling);
if (this.slot_block) {
block.parent.addDependencies(block.dependencies);
block.parent.add_dependencies(block.dependencies);
// appalling hack
const index = block.parent.wrappers.indexOf(this);
@ -209,11 +209,11 @@ export default class ElementWrapper extends Wrapper {
}
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
const { renderer } = this;
if (this.node.name === 'slot') {
const slotName = this.getStaticAttributeValue('name') || 'default';
const slotName = this.get_static_attribute_value('name') || 'default';
renderer.slots.add(slotName);
}
@ -224,33 +224,33 @@ export default class ElementWrapper extends Wrapper {
}
const node = this.var;
const nodes = parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null
const nodes = parent_nodes && block.get_unique_name(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parent_nodes is null
block.addVariable(node);
const renderStatement = this.getRenderStatement();
block.add_variable(node);
const render_statement = this.get_render_statement();
block.builders.create.add_line(
`${node} = ${renderStatement};`
`${node} = ${render_statement};`
);
if (renderer.options.hydratable) {
if (parentNodes) {
if (parent_nodes) {
block.builders.claim.add_block(deindent`
${node} = ${this.getClaimStatement(parentNodes)};
${node} = ${this.get_claim_statement(parent_nodes)};
var ${nodes} = @children(${this.node.name === 'template' ? `${node}.content` : node});
`);
} else {
block.builders.claim.add_line(
`${node} = ${renderStatement};`
`${node} = ${render_statement};`
);
}
}
if (parentNode) {
if (parent_node) {
block.builders.mount.add_line(
`@append(${parentNode}, ${node});`
`@append(${parent_node}, ${node});`
);
if (parentNode === 'document.head') {
if (parent_node === 'document.head') {
block.builders.destroy.add_line(`@detach(${node});`);
}
} else {
@ -262,20 +262,20 @@ export default class ElementWrapper extends Wrapper {
}
// insert static children with textContent or innerHTML
if (!this.node.namespace && this.canUseInnerHTML && this.fragment.nodes.length > 0) {
if (!this.node.namespace && this.can_use_innerhtml && this.fragment.nodes.length > 0) {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.builders.create.add_line(
`${node}.textContent = ${stringify(this.fragment.nodes[0].data)};`
);
} else {
const innerHTML = escape(
const inner_html = escape(
this.fragment.nodes
.map(toHTML)
.map(to_html)
.join('')
);
block.builders.create.add_line(
`${node}.innerHTML = \`${innerHTML}\`;`
`${node}.innerHTML = \`${inner_html}\`;`
);
}
} else {
@ -288,23 +288,23 @@ export default class ElementWrapper extends Wrapper {
});
}
const eventHandlerOrBindingUsesContext = (
this.bindings.some(binding => binding.handler.usesContext) ||
this.node.handlers.some(handler => handler.usesContext) ||
this.node.actions.some(action => action.usesContext)
const event_handler_or_binding_uses_context = (
this.bindings.some(binding => binding.handler.uses_context) ||
this.node.handlers.some(handler => handler.uses_context) ||
this.node.actions.some(action => action.uses_context)
);
if (eventHandlerOrBindingUsesContext) {
block.maintainContext = true;
if (event_handler_or_binding_uses_context) {
block.maintain_context = true;
}
this.addBindings(block);
this.addEventHandlers(block);
this.addAttributes(block);
this.addTransitions(block);
this.addAnimation(block);
this.addActions(block);
this.addClasses(block);
this.add_bindings(block);
this.add_event_handlers(block);
this.add_attributes(block);
this.add_transitions(block);
this.add_animation(block);
this.add_actions(block);
this.add_classes(block);
if (nodes && this.renderer.options.hydratable) {
block.builders.claim.add_line(
@ -312,7 +312,7 @@ export default class ElementWrapper extends Wrapper {
);
}
function toHTML(wrapper: ElementWrapper | TextWrapper) {
function to_html(wrapper: ElementWrapper | TextWrapper) {
if (wrapper.node.type === 'Text') {
const { parent } = wrapper.node;
@ -339,18 +339,18 @@ export default class ElementWrapper extends Wrapper {
if (is_void(wrapper.node.name)) return open + '>';
return `${open}>${wrapper.fragment.nodes.map(toHTML).join('')}</${wrapper.node.name}>`;
return `${open}>${wrapper.fragment.nodes.map(to_html).join('')}</${wrapper.node.name}>`;
}
if (renderer.options.dev) {
const loc = renderer.locate(this.node.start);
block.builders.hydrate.add_line(
`@add_location(${this.var}, ${renderer.fileVar}, ${loc.line}, ${loc.column}, ${this.node.start});`
`@add_location(${this.var}, ${renderer.file_var}, ${loc.line}, ${loc.column}, ${this.node.start});`
);
}
}
getRenderStatement() {
get_render_statement() {
const { name, namespace } = this.node;
if (namespace === 'http://www.w3.org/2000/svg') {
@ -364,7 +364,7 @@ export default class ElementWrapper extends Wrapper {
return `@element("${name}")`;
}
getClaimStatement(nodes: string) {
get_claim_statement(nodes: string) {
const attributes = this.node.attributes
.filter((attr: Node) => attr.type === 'Attribute')
.map((attr: Node) => `${quote_name_if_necessary(attr.name)}: true`)
@ -379,22 +379,22 @@ export default class ElementWrapper extends Wrapper {
: `{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
}
addBindings(block: Block) {
add_bindings(block: Block) {
const { renderer } = this;
if (this.bindings.length === 0) return;
renderer.component.has_reactive_assignments = true;
const lock = this.bindings.some(binding => binding.needsLock) ?
block.getUniqueName(`${this.var}_updating`) :
const lock = this.bindings.some(binding => binding.needs_lock) ?
block.get_unique_name(`${this.var}_updating`) :
null;
if (lock) block.addVariable(lock, 'false');
if (lock) block.add_variable(lock, 'false');
const groups = events
.map(event => ({
events: event.eventNames,
events: event.event_names,
bindings: this.bindings
.filter(binding => binding.node.name !== 'this')
.filter(binding => event.filter(this.node, binding.node.name))
@ -402,7 +402,7 @@ export default class ElementWrapper extends Wrapper {
.filter(group => group.bindings.length);
groups.forEach(group => {
const handler = renderer.component.getUniqueName(`${this.var}_${group.events.join('_')}_handler`);
const handler = renderer.component.get_unique_name(`${this.var}_${group.events.join('_')}_handler`);
renderer.component.add_var({
name: handler,
@ -411,7 +411,7 @@ export default class ElementWrapper extends Wrapper {
});
// TODO figure out how to handle locks
const needsLock = group.bindings.some(binding => binding.needsLock);
const needs_lock = group.bindings.some(binding => binding.needs_lock);
const dependencies = new Set();
const contextual_dependencies = new Set();
@ -430,11 +430,11 @@ export default class ElementWrapper extends Wrapper {
// own hands
let animation_frame;
if (group.events[0] === 'timeupdate') {
animation_frame = block.getUniqueName(`${this.var}_animationframe`);
block.addVariable(animation_frame);
animation_frame = block.get_unique_name(`${this.var}_animationframe`);
block.add_variable(animation_frame);
}
const has_local_function = contextual_dependencies.size > 0 || needsLock || animation_frame;
const has_local_function = contextual_dependencies.size > 0 || needs_lock || animation_frame;
let callee;
@ -446,7 +446,7 @@ export default class ElementWrapper extends Wrapper {
${animation_frame && deindent`
cancelAnimationFrame(${animation_frame});
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`}
${needsLock && `${lock} = true;`}
${needs_lock && `${lock} = true;`}
ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''});
}
`);
@ -466,8 +466,8 @@ export default class ElementWrapper extends Wrapper {
group.events.forEach(name => {
if (name === 'resize') {
// special case
const resize_listener = block.getUniqueName(`${this.var}_resize_listener`);
block.addVariable(resize_listener);
const resize_listener = block.get_unique_name(`${this.var}_resize_listener`);
block.add_variable(resize_listener);
block.builders.mount.add_line(
`${resize_listener} = @add_resize_listener(${this.var}, ${callee}.bind(${this.var}));`
@ -483,14 +483,14 @@ export default class ElementWrapper extends Wrapper {
}
});
const someInitialStateIsUndefined = group.bindings
const some_initial_state_is_undefined = group.bindings
.map(binding => `${binding.snippet} === void 0`)
.join(' || ');
if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.isReadOnlyMediaAttribute())) {
if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.is_readonly_media_attribute())) {
const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`;
block.builders.hydrate.add_line(
`if (${someInitialStateIsUndefined}) @add_render_callback(${callback});`
`if (${some_initial_state_is_undefined}) @add_render_callback(${callback});`
);
}
@ -507,7 +507,7 @@ export default class ElementWrapper extends Wrapper {
const this_binding = this.bindings.find(b => b.node.name === 'this');
if (this_binding) {
const name = renderer.component.getUniqueName(`${this.var}_binding`);
const name = renderer.component.get_unique_name(`${this.var}_binding`);
renderer.component.add_var({
name,
@ -520,7 +520,7 @@ export default class ElementWrapper extends Wrapper {
const args = [];
for (const arg of handler.contextual_dependencies) {
args.push(arg);
block.addVariable(arg, `ctx.${arg}`);
block.add_variable(arg, `ctx.${arg}`);
}
renderer.component.partly_hoisted.push(deindent`
@ -542,25 +542,25 @@ export default class ElementWrapper extends Wrapper {
}
}
addAttributes(block: Block) {
add_attributes(block: Block) {
if (this.node.attributes.find(attr => attr.type === 'Spread')) {
this.addSpreadAttributes(block);
this.add_spread_attributes(block);
return;
}
this.attributes.forEach((attribute: Attribute) => {
if (attribute.node.name === 'class' && attribute.node.isDynamic) {
this.classDependencies.push(...attribute.node.dependencies);
if (attribute.node.name === 'class' && attribute.node.is_dynamic) {
this.class_dependencies.push(...attribute.node.dependencies);
}
attribute.render(block);
});
}
addSpreadAttributes(block: Block) {
const levels = block.getUniqueName(`${this.var}_levels`);
const data = block.getUniqueName(`${this.var}_data`);
add_spread_attributes(block: Block) {
const levels = block.get_unique_name(`${this.var}_levels`);
const data = block.get_unique_name(`${this.var}_data`);
const initialProps = [];
const initial_props = [];
const updates = [];
this.node.attributes
@ -570,15 +570,15 @@ export default class ElementWrapper extends Wrapper {
? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
: null;
if (attr.isSpread) {
if (attr.is_spread) {
const snippet = attr.expression.render(block);
initialProps.push(snippet);
initial_props.push(snippet);
updates.push(condition ? `${condition} && ${snippet}` : snippet);
} else {
const snippet = `{ ${quote_name_if_necessary(attr.name)}: ${attr.getValue(block)} }`;
initialProps.push(snippet);
const snippet = `{ ${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)} }`;
initial_props.push(snippet);
updates.push(condition ? `${condition} && ${snippet}` : snippet);
}
@ -586,7 +586,7 @@ export default class ElementWrapper extends Wrapper {
block.builders.init.add_block(deindent`
var ${levels} = [
${initialProps.join(',\n')}
${initial_props.join(',\n')}
];
var ${data} = {};
@ -606,11 +606,11 @@ export default class ElementWrapper extends Wrapper {
`);
}
addEventHandlers(block: Block) {
addEventHandlers(block, this.var, this.node.handlers);
add_event_handlers(block: Block) {
add_event_handlers(block, this.var, this.node.handlers);
}
addTransitions(
add_transitions(
block: Block
) {
const { intro, outro } = this.node;
@ -620,12 +620,12 @@ export default class ElementWrapper extends Wrapper {
if (intro === outro) {
// bidirectional transition
const name = block.getUniqueName(`${this.var}_transition`);
const name = block.get_unique_name(`${this.var}_transition`);
const snippet = intro.expression
? intro.expression.render(block)
: '{}';
block.addVariable(name);
block.add_variable(name);
const fn = component.qualify(intro.name);
@ -662,11 +662,11 @@ export default class ElementWrapper extends Wrapper {
}
else {
const introName = intro && block.getUniqueName(`${this.var}_intro`);
const outroName = outro && block.getUniqueName(`${this.var}_outro`);
const intro_name = intro && block.get_unique_name(`${this.var}_intro`);
const outro_name = outro && block.get_unique_name(`${this.var}_outro`);
if (intro) {
block.addVariable(introName);
block.add_variable(intro_name);
const snippet = intro.expression
? intro.expression.render(block)
: '{}';
@ -678,19 +678,19 @@ export default class ElementWrapper extends Wrapper {
if (outro) {
intro_block = deindent`
@add_render_callback(() => {
if (${outroName}) ${outroName}.end(1);
if (!${introName}) ${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet});
${introName}.start();
if (${outro_name}) ${outro_name}.end(1);
if (!${intro_name}) ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet});
${intro_name}.start();
});
`;
block.builders.outro.add_line(`if (${introName}) ${introName}.invalidate();`);
block.builders.outro.add_line(`if (${intro_name}) ${intro_name}.invalidate();`);
} else {
intro_block = deindent`
if (!${introName}) {
if (!${intro_name}) {
@add_render_callback(() => {
${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet});
${introName}.start();
${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet});
${intro_name}.start();
});
}
`;
@ -708,7 +708,7 @@ export default class ElementWrapper extends Wrapper {
}
if (outro) {
block.addVariable(outroName);
block.add_variable(outro_name);
const snippet = outro.expression
? outro.expression.render(block)
: '{}';
@ -717,14 +717,14 @@ export default class ElementWrapper extends Wrapper {
if (!intro) {
block.builders.intro.add_block(deindent`
if (${outroName}) ${outroName}.end(1);
if (${outro_name}) ${outro_name}.end(1);
`);
}
// TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM
let outro_block = deindent`
${outroName} = @create_out_transition(${this.var}, ${fn}, ${snippet});
${outro_name} = @create_out_transition(${this.var}, ${fn}, ${snippet});
`;
if (outro_block) {
@ -737,21 +737,21 @@ export default class ElementWrapper extends Wrapper {
block.builders.outro.add_block(outro_block);
block.builders.destroy.add_conditional('detaching', `if (${outroName}) ${outroName}.end();`);
block.builders.destroy.add_conditional('detaching', `if (${outro_name}) ${outro_name}.end();`);
}
}
}
addAnimation(block: Block) {
add_animation(block: Block) {
if (!this.node.animation) return;
const { component } = this.renderer;
const rect = block.getUniqueName('rect');
const stop_animation = block.getUniqueName('stop_animation');
const rect = block.get_unique_name('rect');
const stop_animation = block.get_unique_name('stop_animation');
block.addVariable(rect);
block.addVariable(stop_animation, '@noop');
block.add_variable(rect);
block.add_variable(stop_animation, '@noop');
block.builders.measure.add_block(deindent`
${rect} = ${this.var}.getBoundingClientRect();
@ -772,11 +772,11 @@ export default class ElementWrapper extends Wrapper {
`);
}
addActions(block: Block) {
addActions(this.renderer.component, block, this.var, this.node.actions);
add_actions(block: Block) {
add_actions(this.renderer.component, block, this.var, this.node.actions);
}
addClasses(block: Block) {
add_classes(block: Block) {
this.node.classes.forEach(classDir => {
const { expression, name } = classDir;
let snippet, dependencies;
@ -791,10 +791,10 @@ export default class ElementWrapper extends Wrapper {
block.builders.hydrate.add_line(updater);
if ((dependencies && dependencies.size > 0) || this.classDependencies.length) {
const allDeps = this.classDependencies.concat(...dependencies);
const deps = allDeps.map(dependency => `changed${quote_prop_if_necessary(dependency)}`).join(' || ');
const condition = allDeps.length > 1 ? `(${deps})` : deps;
if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
const deps = all_dependencies.map(dependency => `changed${quote_prop_if_necessary(dependency)}`).join(' || ');
const condition = all_dependencies.length > 1 ? `(${deps})` : deps;
block.builders.update.add_conditional(
condition,
@ -804,14 +804,14 @@ export default class ElementWrapper extends Wrapper {
});
}
getStaticAttributeValue(name: string) {
get_static_attribute_value(name: string) {
const attribute = this.node.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.isTrue) return true;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
@ -821,13 +821,13 @@ export default class ElementWrapper extends Wrapper {
return null;
}
addCssClass(className = this.component.stylesheet.id) {
const classAttribute = this.attributes.find(a => a.name === 'class');
if (classAttribute && !classAttribute.isTrue) {
if (classAttribute.chunks.length === 1 && classAttribute.chunks[0].type === 'Text') {
(classAttribute.chunks[0] as Text).data += ` ${className}`;
add_css_class(className = this.component.stylesheet.id) {
const class_attribute = this.attributes.find(a => a.name === 'class');
if (class_attribute && !class_attribute.is_true) {
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
(class_attribute.chunks[0] as Text).data += ` ${className}`;
} else {
(classAttribute.chunks as Node[]).push(
(class_attribute.chunks as Node[]).push(
new Text(this.component, this, this.scope, {
type: 'Text',
data: ` ${className}`

@ -14,10 +14,10 @@ import Text from './Text';
import Title from './Title';
import Window from './Window';
import Node from '../../nodes/shared/Node';
import { trim_start, trim_end } from '../../../utils/trim';
import TextWrapper from './Text';
import Renderer from '../Renderer';
import Block from '../Block';
import { trim_start, trim_end } from '../../../utils/trim';
const wrappers = {
AwaitBlock,
@ -51,13 +51,13 @@ export default class FragmentWrapper {
block: Block,
nodes: Node[],
parent: Wrapper,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
this.nodes = [];
let lastChild: Wrapper;
let windowWrapper;
let last_child: Wrapper;
let window_wrapper;
let i = nodes.length;
while (i--) {
@ -74,7 +74,7 @@ export default class FragmentWrapper {
// special case — this is an easy way to remove whitespace surrounding
// <svelte:window/>. lil hacky but it works
if (child.type === 'Window') {
windowWrapper = new Window(renderer, block, parent, child);
window_wrapper = new Window(renderer, block, parent, child);
continue;
}
@ -84,19 +84,19 @@ export default class FragmentWrapper {
// We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling
if (this.nodes.length === 0) {
const shouldTrim = (
nextSibling ? (nextSibling.node.type === 'Text' && /^\s/.test(nextSibling.data)) : !child.hasAncestor('EachBlock')
const should_trim = (
next_sibling ? (next_sibling.node.type === 'Text' && /^\s/.test(next_sibling.data)) : !child.has_ancestor('EachBlock')
);
if (shouldTrim) {
if (should_trim) {
data = trim_end(data);
if (!data) continue;
}
}
// glue text nodes (which could e.g. be separated by comments) together
if (lastChild && lastChild.node.type === 'Text') {
lastChild.data = data + lastChild.data;
if (last_child && last_child.node.type === 'Text') {
last_child.data = data + last_child.data;
continue;
}
@ -105,19 +105,19 @@ export default class FragmentWrapper {
this.nodes.unshift(wrapper);
link(lastChild, lastChild = wrapper);
link(last_child, last_child = wrapper);
} else {
const Wrapper = wrappers[child.type];
if (!Wrapper) continue;
const wrapper = new Wrapper(renderer, block, parent, child, stripWhitespace, lastChild || nextSibling);
const wrapper = new Wrapper(renderer, block, parent, child, strip_whitespace, last_child || next_sibling);
this.nodes.unshift(wrapper);
link(lastChild, lastChild = wrapper);
link(last_child, last_child = wrapper);
}
}
if (stripWhitespace) {
if (strip_whitespace) {
const first = this.nodes[0] as TextWrapper;
if (first && first.node.type === 'Text') {
@ -133,15 +133,15 @@ export default class FragmentWrapper {
}
}
if (windowWrapper) {
this.nodes.unshift(windowWrapper);
link(lastChild, windowWrapper);
if (window_wrapper) {
this.nodes.unshift(window_wrapper);
link(last_child, window_wrapper);
}
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parentNode: string, parent_nodes: string) {
for (let i = 0; i < this.nodes.length; i += 1) {
this.nodes[i].render(block, parentNode, parentNodes);
this.nodes[i].render(block, parentNode, parent_nodes);
}
}
}

@ -12,24 +12,24 @@ export default class HeadWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: Head,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.canUseInnerHTML = false;
this.can_use_innerhtml = false;
this.fragment = new FragmentWrapper(
renderer,
block,
node.children,
this,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
this.fragment.render(block, 'document.head', null);
}
}

@ -8,7 +8,7 @@ import ElseBlock from '../../nodes/ElseBlock';
import FragmentWrapper from './Fragment';
import deindent from '../../utils/deindent';
function isElseIf(node: ElseBlock) {
function is_else_if(node: ElseBlock) {
return (
node && node.children.length === 1 && node.children[0].type === 'IfBlock'
);
@ -18,7 +18,7 @@ class IfBlockBranch extends Wrapper {
block: Block;
fragment: FragmentWrapper;
condition: string;
isDynamic: boolean;
is_dynamic: boolean;
var = null;
@ -27,8 +27,8 @@ class IfBlockBranch extends Wrapper {
block: Block,
parent: IfBlockWrapper,
node: IfBlock | ElseBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
@ -36,14 +36,14 @@ class IfBlockBranch extends Wrapper {
this.block = block.child({
comment: create_debugging_comment(node, parent.renderer.component),
name: parent.renderer.component.getUniqueName(
name: parent.renderer.component.get_unique_name(
(node as IfBlock).expression ? `create_if_block` : `create_else_block`
)
});
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, stripWhitespace, nextSibling);
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling);
this.isDynamic = this.block.dependencies.size > 0;
this.is_dynamic = this.block.dependencies.size > 0;
}
}
@ -58,55 +58,53 @@ export default class IfBlockWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: EachBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
const { component } = renderer;
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
this.branches = [];
const blocks: Block[] = [];
let isDynamic = false;
let hasIntros = false;
let hasOutros = false;
let is_dynamic = false;
let has_intros = false;
let has_outros = false;
const createBranches = (node: IfBlock) => {
const create_branches = (node: IfBlock) => {
const branch = new IfBlockBranch(
renderer,
block,
this,
node,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
this.branches.push(branch);
blocks.push(branch.block);
block.addDependencies(node.expression.dependencies);
block.add_dependencies(node.expression.dependencies);
if (branch.block.dependencies.size > 0) {
isDynamic = true;
block.addDependencies(branch.block.dependencies);
is_dynamic = true;
block.add_dependencies(branch.block.dependencies);
}
if (branch.block.hasIntros) hasIntros = true;
if (branch.block.hasOutros) hasOutros = true;
if (branch.block.has_intros) has_intros = true;
if (branch.block.has_outros) has_outros = true;
if (isElseIf(node.else)) {
createBranches(node.else.children[0]);
if (is_else_if(node.else)) {
create_branches(node.else.children[0]);
} else if (node.else) {
const branch = new IfBlockBranch(
renderer,
block,
this,
node.else,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
this.branches.push(branch);
@ -114,21 +112,21 @@ export default class IfBlockWrapper extends Wrapper {
blocks.push(branch.block);
if (branch.block.dependencies.size > 0) {
isDynamic = true;
block.addDependencies(branch.block.dependencies);
is_dynamic = true;
block.add_dependencies(branch.block.dependencies);
}
if (branch.block.hasIntros) hasIntros = true;
if (branch.block.hasOutros) hasOutros = true;
if (branch.block.has_intros) has_intros = true;
if (branch.block.has_outros) has_outros = true;
}
};
createBranches(this.node);
create_branches(this.node);
blocks.forEach(block => {
block.hasUpdateMethod = isDynamic;
block.hasIntroMethod = hasIntros;
block.hasOutroMethod = hasOutros;
block.has_update_method = is_dynamic;
block.has_intro_method = has_intros;
block.has_outro_method = has_outros;
});
renderer.blocks.push(...blocks);
@ -136,60 +134,60 @@ export default class IfBlockWrapper extends Wrapper {
render(
block: Block,
parentNode: string,
parentNodes: string
parent_node: string,
parent_nodes: string
) {
const name = this.var;
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
const anchor = needsAnchor
? block.getUniqueName(`${name}_anchor`)
const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node();
const anchor = needs_anchor
? block.get_unique_name(`${name}_anchor`)
: (this.next && this.next.var) || 'null';
const hasElse = !(this.branches[this.branches.length - 1].condition);
const if_name = hasElse ? '' : `if (${name}) `;
const has_else = !(this.branches[this.branches.length - 1].condition);
const if_name = has_else ? '' : `if (${name}) `;
const dynamic = this.branches[0].block.hasUpdateMethod; // can use [0] as proxy for all, since they necessarily have the same value
const hasIntros = this.branches[0].block.hasIntroMethod;
const hasOutros = this.branches[0].block.hasOutroMethod;
const has_transitions = hasIntros || hasOutros;
const dynamic = this.branches[0].block.has_update_method; // can use [0] as proxy for all, since they necessarily have the same value
const has_intros = this.branches[0].block.has_intro_method;
const has_outros = this.branches[0].block.has_outro_method;
const has_transitions = has_intros || has_outros;
const vars = { name, anchor, if_name, hasElse, has_transitions };
const vars = { name, anchor, if_name, has_else, has_transitions };
if (this.node.else) {
if (hasOutros) {
this.renderCompoundWithOutros(block, parentNode, parentNodes, dynamic, vars);
if (has_outros) {
this.render_compound_with_outros(block, parent_node, parent_nodes, dynamic, vars);
block.builders.outro.add_line(`if (${name}) ${name}.o();`);
} else {
this.renderCompound(block, parentNode, parentNodes, dynamic, vars);
this.render_compound(block, parent_node, parent_nodes, dynamic, vars);
}
} else {
this.renderSimple(block, parentNode, parentNodes, dynamic, vars);
this.render_simple(block, parent_node, parent_nodes, dynamic, vars);
if (hasOutros) {
if (has_outros) {
block.builders.outro.add_line(`if (${name}) ${name}.o();`);
}
}
block.builders.create.add_line(`${if_name}${name}.c();`);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_line(
`${if_name}${name}.l(${parentNodes});`
`${if_name}${name}.l(${parent_nodes});`
);
}
if (hasIntros || hasOutros) {
if (has_intros || has_outros) {
block.builders.intro.add_line(`if (${name}) ${name}.i();`);
}
if (needsAnchor) {
block.addElement(
if (needs_anchor) {
block.add_element(
anchor,
`@comment()`,
parentNodes && `@comment()`,
parentNode
parent_nodes && `@comment()`,
parent_node
);
}
@ -198,16 +196,16 @@ export default class IfBlockWrapper extends Wrapper {
});
}
renderCompound(
render_compound(
block: Block,
parentNode: string,
parentNodes: string,
parent_node: string,
parent_nodes: string,
dynamic,
{ name, anchor, hasElse, if_name, has_transitions }
{ name, anchor, has_else, if_name, has_transitions }
) {
const select_block_type = this.renderer.component.getUniqueName(`select_block_type`);
const current_block_type = block.getUniqueName(`current_block_type`);
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
const select_block_type = this.renderer.component.get_unique_name(`select_block_type`);
const current_block_type = block.get_unique_name(`current_block_type`);
const current_block_type_and = has_else ? '' : `${current_block_type} && `;
block.builders.init.add_block(deindent`
function ${select_block_type}(ctx) {
@ -222,21 +220,21 @@ export default class IfBlockWrapper extends Wrapper {
var ${name} = ${current_block_type_and}${current_block_type}(ctx);
`);
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const anchor_node = parent_node ? 'null' : 'anchor';
block.builders.mount.add_line(
`${if_name}${name}.m(${initialMountNode}, ${anchorNode});`
`${if_name}${name}.m(${initial_mount_node}, ${anchor_node});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const update_mount_node = this.get_update_mount_node(anchor);
const changeBlock = deindent`
const change_block = deindent`
${if_name}${name}.d(1);
${name} = ${current_block_type_and}${current_block_type}(ctx);
if (${name}) {
${name}.c();
${has_transitions && `${name}.i(1);`}
${name}.m(${updateMountNode}, ${anchor});
${name}.m(${update_mount_node}, ${anchor});
}
`;
@ -245,41 +243,41 @@ export default class IfBlockWrapper extends Wrapper {
if (${current_block_type} === (${current_block_type} = ${select_block_type}(ctx)) && ${name}) {
${name}.p(changed, ctx);
} else {
${changeBlock}
${change_block}
}
`);
} else {
block.builders.update.add_block(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(ctx))) {
${changeBlock}
${change_block}
}
`);
}
block.builders.destroy.add_line(`${if_name}${name}.d(${parentNode ? '' : 'detaching'});`);
block.builders.destroy.add_line(`${if_name}${name}.d(${parent_node ? '' : 'detaching'});`);
}
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
renderCompoundWithOutros(
render_compound_with_outros(
block: Block,
parentNode: string,
parentNodes: string,
parent_node: string,
parent_nodes: string,
dynamic,
{ name, anchor, hasElse, has_transitions }
{ name, anchor, has_else, has_transitions }
) {
const select_block_type = this.renderer.component.getUniqueName(`select_block_type`);
const current_block_type_index = block.getUniqueName(`current_block_type_index`);
const previous_block_index = block.getUniqueName(`previous_block_index`);
const if_block_creators = block.getUniqueName(`if_block_creators`);
const if_blocks = block.getUniqueName(`if_blocks`);
const select_block_type = this.renderer.component.get_unique_name(`select_block_type`);
const current_block_type_index = block.get_unique_name(`current_block_type_index`);
const previous_block_index = block.get_unique_name(`previous_block_index`);
const if_block_creators = block.get_unique_name(`if_block_creators`);
const if_blocks = block.get_unique_name(`if_blocks`);
const if_current_block_type_index = hasElse
const if_current_block_type_index = has_else
? ''
: `if (~${current_block_type_index}) `;
block.addVariable(current_block_type_index);
block.addVariable(name);
block.add_variable(current_block_type_index);
block.add_variable(name);
block.builders.init.add_block(deindent`
var ${if_block_creators} = [
@ -292,11 +290,11 @@ export default class IfBlockWrapper extends Wrapper {
${this.branches
.map(({ condition }, i) => `${condition ? `if (${condition}) ` : ''}return ${i};`)
.join('\n')}
${!hasElse && `return -1;`}
${!has_else && `return -1;`}
}
`);
if (hasElse) {
if (has_else) {
block.builders.init.add_block(deindent`
${current_block_type_index} = ${select_block_type}(ctx);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx);
@ -309,16 +307,16 @@ export default class IfBlockWrapper extends Wrapper {
`);
}
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const anchor_node = parent_node ? 'null' : 'anchor';
block.builders.mount.add_line(
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].m(${initialMountNode}, ${anchorNode});`
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].m(${initial_mount_node}, ${anchor_node});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const update_mount_node = this.get_update_mount_node(anchor);
const destroyOldBlock = deindent`
const destroy_old_block = deindent`
@group_outros();
@on_outro(() => {
${if_blocks}[${previous_block_index}].d(1);
@ -328,29 +326,29 @@ export default class IfBlockWrapper extends Wrapper {
@check_outros();
`;
const createNewBlock = deindent`
const create_new_block = deindent`
${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx);
${name}.c();
}
${has_transitions && `${name}.i(1);`}
${name}.m(${updateMountNode}, ${anchor});
${name}.m(${update_mount_node}, ${anchor});
`;
const changeBlock = hasElse
const change_block = has_else
? deindent`
${destroyOldBlock}
${destroy_old_block}
${createNewBlock}
${create_new_block}
`
: deindent`
if (${name}) {
${destroyOldBlock}
${destroy_old_block}
}
if (~${current_block_type_index}) {
${createNewBlock}
${create_new_block}
} else {
${name} = null;
}
@ -363,7 +361,7 @@ export default class IfBlockWrapper extends Wrapper {
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, ctx);
} else {
${changeBlock}
${change_block}
}
`);
} else {
@ -371,20 +369,20 @@ export default class IfBlockWrapper extends Wrapper {
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(ctx);
if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
${change_block}
}
`);
}
block.builders.destroy.add_line(deindent`
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].d(${parentNode ? '' : 'detaching'});
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].d(${parent_node ? '' : 'detaching'});
`);
}
renderSimple(
render_simple(
block: Block,
parentNode: string,
parentNodes: string,
parent_node: string,
parent_nodes: string,
dynamic,
{ name, anchor, if_name, has_transitions }
) {
@ -394,14 +392,14 @@ export default class IfBlockWrapper extends Wrapper {
var ${name} = (${branch.condition}) && ${branch.block.name}(ctx);
`);
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const anchor_node = parent_node ? 'null' : 'anchor';
block.builders.mount.add_line(
`if (${name}) ${name}.m(${initialMountNode}, ${anchorNode});`
`if (${name}) ${name}.m(${initial_mount_node}, ${anchor_node});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const update_mount_node = this.get_update_mount_node(anchor);
const enter = dynamic
? deindent`
@ -412,7 +410,7 @@ export default class IfBlockWrapper extends Wrapper {
${name} = ${branch.block.name}(ctx);
${name}.c();
${has_transitions && `${name}.i(1);`}
${name}.m(${updateMountNode}, ${anchor});
${name}.m(${update_mount_node}, ${anchor});
}
`
: deindent`
@ -420,7 +418,7 @@ export default class IfBlockWrapper extends Wrapper {
${name} = ${branch.block.name}(ctx);
${name}.c();
${has_transitions && `${name}.i(1);`}
${name}.m(${updateMountNode}, ${anchor});
${name}.m(${update_mount_node}, ${anchor});
${has_transitions && `} else {
${name}.i(1);`}
}
@ -428,7 +426,7 @@ export default class IfBlockWrapper extends Wrapper {
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch.block.hasOutroMethod
const exit = branch.block.has_outro_method
? deindent`
@group_outros();
@on_outro(() => {
@ -452,6 +450,6 @@ export default class IfBlockWrapper extends Wrapper {
}
`);
block.builders.destroy.add_line(`${if_name}${name}.d(${parentNode ? '' : 'detaching'});`);
block.builders.destroy.add_line(`${if_name}${name}.d(${parent_node ? '' : 'detaching'});`);
}
}

@ -26,37 +26,37 @@ export default class InlineComponentWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: InlineComponent,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
if (this.node.expression) {
block.addDependencies(this.node.expression.dependencies);
block.add_dependencies(this.node.expression.dependencies);
}
this.node.attributes.forEach(attr => {
block.addDependencies(attr.dependencies);
block.add_dependencies(attr.dependencies);
});
this.node.bindings.forEach(binding => {
if (binding.isContextual) {
if (binding.is_contextual) {
// we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced
const { name } = get_object(binding.expression.node);
const eachBlock = this.node.scope.getOwner(name);
const each_block = this.node.scope.get_owner(name);
(eachBlock as EachBlock).has_binding = true;
(each_block as EachBlock).has_binding = true;
}
block.addDependencies(binding.expression.dependencies);
block.add_dependencies(binding.expression.dependencies);
});
this.node.handlers.forEach(handler => {
if (handler.expression) {
block.addDependencies(handler.expression.dependencies);
block.add_dependencies(handler.expression.dependencies);
}
});
@ -69,7 +69,7 @@ export default class InlineComponentWrapper extends Wrapper {
if (this.node.children.length) {
const default_slot = block.child({
comment: create_debugging_comment(node, renderer.component),
name: renderer.component.getUniqueName(`create_default_slot`)
name: renderer.component.get_unique_name(`create_default_slot`)
});
this.renderer.blocks.push(default_slot);
@ -81,7 +81,7 @@ export default class InlineComponentWrapper extends Wrapper {
scope: this.node.scope,
fn
});
this.fragment = new FragmentWrapper(renderer, default_slot, node.children, this, stripWhitespace, nextSibling);
this.fragment = new FragmentWrapper(renderer, default_slot, node.children, this, strip_whitespace, next_sibling);
const dependencies = new Set();
@ -92,16 +92,16 @@ export default class InlineComponentWrapper extends Wrapper {
}
});
block.addDependencies(dependencies);
block.add_dependencies(dependencies);
}
block.addOutro();
block.add_outro();
}
render(
block: Block,
parentNode: string,
parentNodes: string
parent_nodes: string
) {
const { renderer } = this;
const { component } = renderer;
@ -115,24 +115,24 @@ export default class InlineComponentWrapper extends Wrapper {
const postupdates: string[] = [];
let props;
const name_changes = block.getUniqueName(`${name}_changes`);
const name_changes = block.get_unique_name(`${name}_changes`);
const usesSpread = !!this.node.attributes.find(a => a.isSpread);
const uses_spread = !!this.node.attributes.find(a => a.is_spread);
const slot_props = Array.from(this.slots).map(([name, slot]) => `$$slot_${sanitize(name)}: [${slot.block.name}${slot.fn ? `, ${slot.fn}` : ''}]`);
if (slot_props.length > 0) slot_props.push(`$$scope: { ctx }`);
const attributeObject = usesSpread
const attributeObject = uses_spread
? stringify_props(slot_props)
: stringify_props(
this.node.attributes.map(attr => `${quote_name_if_necessary(attr.name)}: ${attr.getValue(block)}`).concat(slot_props)
this.node.attributes.map(attr => `${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)}`).concat(slot_props)
);
if (this.node.attributes.length || this.node.bindings.length || slot_props.length) {
if (!usesSpread && this.node.bindings.length === 0) {
if (!uses_spread && this.node.bindings.length === 0) {
component_opts.push(`props: ${attributeObject}`);
} else {
props = block.getUniqueName(`${name}_props`);
props = block.get_unique_name(`${name}_props`);
component_opts.push(`props: ${props}`);
}
}
@ -145,7 +145,7 @@ export default class InlineComponentWrapper extends Wrapper {
});
}
if (component.compileOptions.dev) {
if (component.compile_options.dev) {
// TODO this is a terrible hack, but without it the component
// will complain that options.target is missing. This would
// work better if components had separate public and private
@ -169,38 +169,38 @@ export default class InlineComponentWrapper extends Wrapper {
const non_let_dependencies = Array.from(fragment_dependencies).filter(name => !this.node.scope.is_let(name));
if (!usesSpread && (this.node.attributes.filter(a => a.isDynamic).length || this.node.bindings.length || non_let_dependencies.length > 0)) {
if (!uses_spread && (this.node.attributes.filter(a => a.is_dynamic).length || this.node.bindings.length || non_let_dependencies.length > 0)) {
updates.push(`var ${name_changes} = {};`);
}
if (this.node.attributes.length) {
if (usesSpread) {
const levels = block.getUniqueName(`${this.var}_spread_levels`);
if (uses_spread) {
const levels = block.get_unique_name(`${this.var}_spread_levels`);
const initialProps = [];
const initial_props = [];
const changes = [];
const allDependencies = new Set();
const all_dependencies = new Set();
this.node.attributes.forEach(attr => {
add_to_set(allDependencies, attr.dependencies);
add_to_set(all_dependencies, attr.dependencies);
});
this.node.attributes.forEach(attr => {
const { name, dependencies } = attr;
const condition = dependencies.size > 0 && (dependencies.size !== allDependencies.size)
const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size)
? `(${Array.from(dependencies).map(d => `changed.${d}`).join(' || ')})`
: null;
if (attr.isSpread) {
if (attr.is_spread) {
const value = attr.expression.render(block);
initialProps.push(value);
initial_props.push(value);
changes.push(condition ? `${condition} && ${value}` : value);
} else {
const obj = `{ ${quote_name_if_necessary(name)}: ${attr.getValue(block)} }`;
initialProps.push(obj);
const obj = `{ ${quote_name_if_necessary(name)}: ${attr.get_value(block)} }`;
initial_props.push(obj);
changes.push(condition ? `${condition} && ${obj}` : obj);
}
@ -208,7 +208,7 @@ export default class InlineComponentWrapper extends Wrapper {
block.builders.init.add_block(deindent`
var ${levels} = [
${initialProps.join(',\n')}
${initial_props.join(',\n')}
];
`);
@ -218,22 +218,22 @@ export default class InlineComponentWrapper extends Wrapper {
}
`);
const conditions = Array.from(allDependencies).map(dep => `changed.${dep}`).join(' || ');
const conditions = Array.from(all_dependencies).map(dep => `changed.${dep}`).join(' || ');
updates.push(deindent`
var ${name_changes} = ${allDependencies.size === 1 ? `${conditions}` : `(${conditions})`} ? @get_spread_update(${levels}, [
var ${name_changes} = ${all_dependencies.size === 1 ? `${conditions}` : `(${conditions})`} ? @get_spread_update(${levels}, [
${changes.join(',\n')}
]) : {};
`);
} else {
this.node.attributes
.filter((attribute: Attribute) => attribute.isDynamic)
.filter((attribute: Attribute) => attribute.is_dynamic)
.forEach((attribute: Attribute) => {
if (attribute.dependencies.size > 0) {
updates.push(deindent`
if (${[...attribute.dependencies]
.map(dependency => `changed.${dependency}`)
.join(' || ')}) ${name_changes}${quote_prop_if_necessary(attribute.name)} = ${attribute.getValue(block)};
.join(' || ')}) ${name_changes}${quote_prop_if_necessary(attribute.name)} = ${attribute.get_value(block)};
`);
}
});
@ -248,7 +248,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.has_reactive_assignments = true;
if (binding.name === 'this') {
const fn = component.getUniqueName(`${this.var}_binding`);
const fn = component.get_unique_name(`${this.var}_binding`);
component.add_var({
name: fn,
@ -259,7 +259,7 @@ export default class InlineComponentWrapper extends Wrapper {
let lhs;
let object;
if (binding.isContextual && binding.expression.node.type === 'Identifier') {
if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
// bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x;
const { name } = binding.expression.node;
@ -283,7 +283,7 @@ export default class InlineComponentWrapper extends Wrapper {
return `@add_binding_callback(() => ctx.${fn}(${this.var}));`;
}
const name = component.getUniqueName(`${this.var}_${binding.name}_binding`);
const name = component.get_unique_name(`${this.var}_${binding.name}_binding`);
component.add_var({
name,
@ -291,8 +291,8 @@ export default class InlineComponentWrapper extends Wrapper {
referenced: true
});
const updating = block.getUniqueName(`updating_${binding.name}`);
block.addVariable(updating);
const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating);
const snippet = binding.expression.render(block);
@ -315,7 +315,7 @@ export default class InlineComponentWrapper extends Wrapper {
let lhs = component.source.slice(binding.expression.node.start, binding.expression.node.end).trim();
if (binding.isContextual && binding.expression.node.type === 'Identifier') {
if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
// bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x;
const { name } = binding.expression.node;
@ -336,7 +336,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
`);
block.maintainContext = true; // TODO put this somewhere more logical
block.maintain_context = true; // TODO put this somewhere more logical
} else {
block.builders.init.add_block(deindent`
function ${name}(value) {
@ -365,8 +365,8 @@ export default class InlineComponentWrapper extends Wrapper {
});
if (this.node.name === 'svelte:component') {
const switch_value = block.getUniqueName('switch_value');
const switch_props = block.getUniqueName('switch_props');
const switch_value = block.get_unique_name('switch_value');
const switch_props = block.get_unique_name('switch_props');
const snippet = this.node.expression.render(block);
@ -392,9 +392,9 @@ export default class InlineComponentWrapper extends Wrapper {
`if (${name}) ${name}.$$.fragment.c();`
);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_line(
`if (${name}) ${name}.$$.fragment.l(${parentNodes});`
`if (${name}) ${name}.$$.fragment.l(${parent_nodes});`
);
}
@ -404,8 +404,8 @@ export default class InlineComponentWrapper extends Wrapper {
}
`);
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor);
const anchor = this.get_or_create_anchor(block, parentNode, parent_nodes);
const update_mount_node = this.get_update_mount_node(anchor);
if (updates.length) {
block.builders.update.add_block(deindent`
@ -433,7 +433,7 @@ export default class InlineComponentWrapper extends Wrapper {
${name}.$$.fragment.c();
${name}.$$.fragment.i(1);
@mount_component(${name}, ${updateMountNode}, ${anchor});
@mount_component(${name}, ${update_mount_node}, ${anchor});
} else {
${name} = null;
}
@ -476,9 +476,9 @@ export default class InlineComponentWrapper extends Wrapper {
block.builders.create.add_line(`${name}.$$.fragment.c();`);
if (parentNodes && this.renderer.options.hydratable) {
if (parent_nodes && this.renderer.options.hydratable) {
block.builders.claim.add_line(
`${name}.$$.fragment.l(${parentNodes});`
`${name}.$$.fragment.l(${parent_nodes});`
);
}

@ -9,20 +9,20 @@ export default class MustacheTagWrapper extends Tag {
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: Node) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
}
render(block: Block, parentNode: string, parentNodes: string) {
const { init } = this.renameThisMethod(
render(block: Block, parent_node: string, parent_nodes: string) {
const { init } = this.rename_this_method(
block,
value => `@set_data(${this.var}, ${value});`
);
block.addElement(
block.add_element(
this.var,
`@create_text(${init})`,
parentNodes && `@claim_text(${parentNodes}, ${init})`,
parentNode
parent_nodes && `@claim_text(${parent_nodes}, ${init})`,
parent_node
);
}
}

@ -15,89 +15,89 @@ export default class RawMustacheTagWrapper extends Tag {
node: Node
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
const name = this.var;
// TODO use isDomNode instead of type === 'Element'?
const needsAnchorBefore = this.prev ? this.prev.node.type !== 'Element' : !parentNode;
const needsAnchorAfter = this.next ? this.next.node.type !== 'Element' : !parentNode;
// TODO use is_dom_node instead of type === 'Element'?
const needs_anchor_before = this.prev ? this.prev.node.type !== 'Element' : !parent_node;
const needs_anchor_after = this.next ? this.next.node.type !== 'Element' : !parent_node;
const anchorBefore = needsAnchorBefore
? block.getUniqueName(`${name}_before`)
const anchor_before = needs_anchor_before
? block.get_unique_name(`${name}_before`)
: (this.prev && this.prev.var) || 'null';
const anchorAfter = needsAnchorAfter
? block.getUniqueName(`${name}_after`)
const anchor_after = needs_anchor_after
? block.get_unique_name(`${name}_after`)
: (this.next && this.next.var) || 'null';
let detach: string;
let insert: (content: string) => string;
let useInnerHTML = false;
let use_innerhtml = false;
if (anchorBefore === 'null' && anchorAfter === 'null') {
useInnerHTML = true;
detach = `${parentNode}.innerHTML = '';`;
insert = content => `${parentNode}.innerHTML = ${content};`;
} else if (anchorBefore === 'null') {
detach = `@detach_before(${anchorAfter});`;
insert = content => `${anchorAfter}.insertAdjacentHTML("beforebegin", ${content});`;
} else if (anchorAfter === 'null') {
detach = `@detach_after(${anchorBefore});`;
insert = content => `${anchorBefore}.insertAdjacentHTML("afterend", ${content});`;
if (anchor_before === 'null' && anchor_after === 'null') {
use_innerhtml = true;
detach = `${parent_node}.innerHTML = '';`;
insert = content => `${parent_node}.innerHTML = ${content};`;
} else if (anchor_before === 'null') {
detach = `@detach_before(${anchor_after});`;
insert = content => `${anchor_after}.insertAdjacentHTML("beforebegin", ${content});`;
} else if (anchor_after === 'null') {
detach = `@detach_after(${anchor_before});`;
insert = content => `${anchor_before}.insertAdjacentHTML("afterend", ${content});`;
} else {
detach = `@detach_between(${anchorBefore}, ${anchorAfter});`;
insert = content => `${anchorBefore}.insertAdjacentHTML("afterend", ${content});`;
detach = `@detach_between(${anchor_before}, ${anchor_after});`;
insert = content => `${anchor_before}.insertAdjacentHTML("afterend", ${content});`;
}
const { init } = this.renameThisMethod(
const { init } = this.rename_this_method(
block,
content => deindent`
${!useInnerHTML && detach}
${!use_innerhtml && detach}
${insert(content)}
`
);
// we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s.
if (needsAnchorBefore) {
block.addElement(
anchorBefore,
if (needs_anchor_before) {
block.add_element(
anchor_before,
`@element('noscript')`,
parentNodes && `@element('noscript')`,
parentNode,
parent_nodes && `@element('noscript')`,
parent_node,
true
);
}
function addAnchorAfter() {
block.addElement(
anchorAfter,
function add_anchor_after() {
block.add_element(
anchor_after,
`@element('noscript')`,
parentNodes && `@element('noscript')`,
parentNode
parent_nodes && `@element('noscript')`,
parent_node
);
}
if (needsAnchorAfter && anchorBefore === 'null') {
// anchorAfter needs to be in the DOM before we
if (needs_anchor_after && anchor_before === 'null') {
// anchor_after needs to be in the DOM before we
// insert the HTML...
addAnchorAfter();
add_anchor_after();
}
block.builders.mount.add_line(insert(init));
if (!parentNode) {
block.builders.destroy.add_conditional('detaching', needsAnchorBefore
? `${detach}\n@detach(${anchorBefore});`
if (!parent_node) {
block.builders.destroy.add_conditional('detaching', needs_anchor_before
? `${detach}\n@detach(${anchor_before});`
: detach);
}
if (needsAnchorAfter && anchorBefore !== 'null') {
if (needs_anchor_after && anchor_before !== 'null') {
// ...otherwise it should go afterwards
addAnchorAfter();
add_anchor_after();
}
}
}

@ -22,36 +22,36 @@ export default class SlotWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: Slot,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
this.fragment = new FragmentWrapper(
renderer,
block,
node.children,
parent,
stripWhitespace,
nextSibling
strip_whitespace,
next_sibling
);
this.node.attributes.forEach(attribute => {
add_to_set(this.dependencies, attribute.dependencies);
});
block.addDependencies(this.dependencies);
block.add_dependencies(this.dependencies);
}
render(
block: Block,
parentNode: string,
parentNodes: string
parent_node: string,
parent_nodes: string
) {
const { renderer } = this;
const slot_name = this.node.getStaticAttributeValue('name') || 'default';
const slot_name = this.node.get_static_attribute_value('name') || 'default';
renderer.slots.add(slot_name);
let get_slot_changes;
@ -60,8 +60,8 @@ export default class SlotWrapper extends Wrapper {
const attributes = this.node.attributes.filter(attribute => attribute.name !== 'name');
if (attributes.length > 0) {
get_slot_changes = renderer.component.getUniqueName(`get_${slot_name}_slot_changes`);
get_slot_context = renderer.component.getUniqueName(`get_${slot_name}_slot_context`);
get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`);
get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`);
const context_props = get_slot_data(attributes, false);
const changes_props = [];
@ -91,8 +91,8 @@ export default class SlotWrapper extends Wrapper {
get_slot_context = 'null';
}
const slot = block.getUniqueName(`${sanitize(slot_name)}_slot`);
const slot_definition = block.getUniqueName(`${sanitize(slot_name)}_slot`);
const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`);
const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot`);
block.builders.init.add_block(deindent`
const ${slot_definition} = ctx.$$slot_${sanitize(slot_name)};
@ -109,8 +109,8 @@ export default class SlotWrapper extends Wrapper {
const listeners = block.event_listeners;
block.event_listeners = [];
this.fragment.render(block, parentNode, parentNodes);
block.renderListeners(`_${slot}`);
this.fragment.render(block, parent_node, parent_nodes);
block.render_listeners(`_${slot}`);
block.event_listeners = listeners;
block.builders.create.pop_condition();
@ -124,16 +124,16 @@ export default class SlotWrapper extends Wrapper {
);
block.builders.claim.add_line(
`if (${slot}) ${slot}.l(${parentNodes});`
`if (${slot}) ${slot}.l(${parent_nodes});`
);
const mountLeadin = block.builders.mount.toString() !== mountBefore
const mount_leadin = block.builders.mount.toString() !== mountBefore
? `else`
: `if (${slot})`;
block.builders.mount.add_block(deindent`
${mountLeadin} {
${slot}.m(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});
${mount_leadin} {
${slot}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'});
}
`);

@ -7,7 +7,7 @@ import { stringify } from '../../utils/stringify';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elementsWithoutText = new Set([
const elements_without_text = new Set([
'audio',
'datalist',
'dl',
@ -17,16 +17,16 @@ const elementsWithoutText = new Set([
]);
// TODO this should probably be in Fragment
function shouldSkip(node: Text) {
function should_skip(node: Text) {
if (/\S/.test(node.data)) return false;
const parentElement = node.findNearest(/(?:Element|InlineComponent|Head)/);
if (!parentElement) return false;
const parent_element = node.find_nearest(/(?:Element|InlineComponent|Head)/);
if (!parent_element) return false;
if (parentElement.type === 'Head') return true;
if (parentElement.type === 'InlineComponent') return parentElement.children.length === 1 && node === parentElement.children[0];
if (parent_element.type === 'Head') return true;
if (parent_element.type === 'InlineComponent') return parent_element.children.length === 1 && node === parent_element.children[0];
return parentElement.namespace || elementsWithoutText.has(parentElement.name);
return parent_element.namespace || elements_without_text.has(parent_element.name);
}
export default class TextWrapper extends Wrapper {
@ -44,19 +44,19 @@ export default class TextWrapper extends Wrapper {
) {
super(renderer, block, parent, node);
this.skip = shouldSkip(this.node);
this.skip = should_skip(this.node);
this.data = data;
this.var = this.skip ? null : 't';
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
if (this.skip) return;
block.addElement(
block.add_element(
this.var,
`@create_text(${stringify(this.data)})`,
parentNodes && `@claim_text(${parentNodes}, ${stringify(this.data)})`,
parentNode
parent_nodes && `@claim_text(${parent_nodes}, ${stringify(this.data)})`,
parent_node
);
}
}

@ -13,19 +13,19 @@ export default class TitleWrapper extends Wrapper {
block: Block,
parent: Wrapper,
node: Title,
stripWhitespace: boolean,
nextSibling: Wrapper
strip_whitespace: boolean,
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
}
render(block: Block, parentNode: string, parentNodes: string) {
const isDynamic = !!this.node.children.find(node => node.type !== 'Text');
render(block: Block, parent_node: string, parent_nodes: string) {
const is_dynamic = !!this.node.children.find(node => node.type !== 'Text');
if (isDynamic) {
if (is_dynamic) {
let value;
const allDependencies = new Set();
const all_dependencies = new Set();
// TODO some of this code is repeated in Tag.ts — would be good to
// DRY it out if that's possible without introducing crazy indirection
@ -33,7 +33,7 @@ export default class TitleWrapper extends Wrapper {
// single {tag} — may be a non-string
const { expression } = this.node.children[0];
value = expression.render(block);
add_to_set(allDependencies, expression.dependencies);
add_to_set(all_dependencies, expression.dependencies);
} else {
// '{foo} {bar}' — treat as string concatenation
value =
@ -46,41 +46,41 @@ export default class TitleWrapper extends Wrapper {
const snippet = chunk.expression.render(block);
chunk.expression.dependencies.forEach(d => {
allDependencies.add(d);
all_dependencies.add(d);
});
return chunk.expression.getPrecedence() <= 13 ? `(${snippet})` : snippet;
return chunk.expression.get_precedence() <= 13 ? `(${snippet})` : snippet;
}
})
.join(' + ');
}
const last = this.node.shouldCache && block.getUniqueName(
const last = this.node.should_cache && block.get_unique_name(
`title_value`
);
if (this.node.shouldCache) block.addVariable(last);
if (this.node.should_cache) block.add_variable(last);
let updater;
const init = this.node.shouldCache ? `${last} = ${value}` : value;
const init = this.node.should_cache ? `${last} = ${value}` : value;
block.builders.init.add_line(
`document.title = ${init};`
);
updater = `document.title = ${this.node.shouldCache ? last : value};`;
updater = `document.title = ${this.node.should_cache ? last : value};`;
if (allDependencies.size) {
const dependencies = Array.from(allDependencies);
const changedCheck = (
( block.hasOutros ? `!#current || ` : '' ) +
if (all_dependencies.size) {
const dependencies = Array.from(all_dependencies);
const changed_check = (
(block.has_outros ? `!#current || ` : '') +
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
);
const updateCachedValue = `${last} !== (${last} = ${value})`;
const update_cached_value = `${last} !== (${last} = ${value})`;
const condition = this.node.shouldCache ?
( dependencies.length ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue ) :
changedCheck;
const condition = this.node.should_cache ?
(dependencies.length ? `(${changed_check}) && ${update_cached_value}` : update_cached_value) :
changed_check;
block.builders.update.add_conditional(
condition,

@ -3,11 +3,11 @@ import Block from '../Block';
import Node from '../../nodes/shared/Node';
import Wrapper from './shared/Wrapper';
import deindent from '../../utils/deindent';
import addEventHandlers from './shared/addEventHandlers';
import add_event_handlers from './shared/add_event_handlers';
import Window from '../../nodes/Window';
import addActions from './shared/addActions';
import add_actions from './shared/add_actions';
const associatedEvents = {
const associated_events = {
innerWidth: 'resize',
innerHeight: 'resize',
outerWidth: 'resize',
@ -37,15 +37,15 @@ export default class WindowWrapper extends Wrapper {
super(renderer, block, parent, node);
}
render(block: Block, parentNode: string, parentNodes: string) {
render(block: Block, parent_node: string, parent_nodes: string) {
const { renderer } = this;
const { component } = renderer;
const events = {};
const bindings: Record<string, string> = {};
addActions(component, block, 'window', this.node.actions);
addEventHandlers(block, 'window', this.node.handlers);
add_actions(component, block, 'window', this.node.actions);
add_event_handlers(block, 'window', this.node.handlers);
this.node.bindings.forEach(binding => {
// in dev mode, throw if read-only values are written to
@ -58,29 +58,29 @@ export default class WindowWrapper extends Wrapper {
// bind:online is a special case, we need to listen for two separate events
if (binding.name === 'online') return;
const associatedEvent = associatedEvents[binding.name];
const associated_event = associated_events[binding.name];
const property = properties[binding.name] || binding.name;
if (!events[associatedEvent]) events[associatedEvent] = [];
events[associatedEvent].push({
if (!events[associated_event]) events[associated_event] = [];
events[associated_event].push({
name: binding.expression.node.name,
value: property
});
});
const scrolling = block.getUniqueName(`scrolling`);
const clear_scrolling = block.getUniqueName(`clear_scrolling`);
const scrolling_timeout = block.getUniqueName(`scrolling_timeout`);
const scrolling = block.get_unique_name(`scrolling`);
const clear_scrolling = block.get_unique_name(`clear_scrolling`);
const scrolling_timeout = block.get_unique_name(`scrolling_timeout`);
Object.keys(events).forEach(event => {
const handler_name = block.getUniqueName(`onwindow${event}`);
const handler_name = block.get_unique_name(`onwindow${event}`);
const props = events[event];
if (event === 'scroll') {
// TODO other bidirectional bindings...
block.addVariable(scrolling, 'false');
block.addVariable(clear_scrolling, `() => { ${scrolling} = false }`);
block.addVariable(scrolling_timeout);
block.add_variable(scrolling, 'false');
block.add_variable(clear_scrolling, `() => { ${scrolling} = false }`);
block.add_variable(scrolling_timeout);
const condition = [
bindings.scrollX && `"${bindings.scrollX}" in this._state`,
@ -90,7 +90,7 @@ export default class WindowWrapper extends Wrapper {
const x = bindings.scrollX && `this._state.${bindings.scrollX}`;
const y = bindings.scrollY && `this._state.${bindings.scrollY}`;
renderer.metaBindings.add_block(deindent`
renderer.meta_bindings.add_block(deindent`
if (${condition}) {
window.scrollTo(${x || 'window.pageXOffset'}, ${y || 'window.pageYOffset'});
}
@ -108,7 +108,7 @@ export default class WindowWrapper extends Wrapper {
`);
} else {
props.forEach(prop => {
renderer.metaBindings.add_line(
renderer.meta_bindings.add_line(
`this._state.${prop.name} = window.${prop.value};`
);
});
@ -159,19 +159,19 @@ export default class WindowWrapper extends Wrapper {
// another special case. (I'm starting to think these are all special cases.)
if (bindings.online) {
const handler_name = block.getUniqueName(`onlinestatuschanged`);
const handler_name = block.get_unique_name(`onlinestatuschanged`);
block.builders.init.add_block(deindent`
function ${handler_name}(event) {
${component.compileOptions.dev && `component._updatingReadonlyProperty = true;`}
${component.compile_options.dev && `component._updatingReadonlyProperty = true;`}
#component.set({ ${bindings.online}: navigator.onLine });
${component.compileOptions.dev && `component._updatingReadonlyProperty = false;`}
${component.compile_options.dev && `component._updatingReadonlyProperty = false;`}
}
window.addEventListener("online", ${handler_name});
window.addEventListener("offline", ${handler_name});
`);
// add initial value
renderer.metaBindings.add_line(
renderer.meta_bindings.add_line(
`this._state.${bindings.online} = navigator.onLine;`
);

@ -9,34 +9,34 @@ export default class Tag extends Wrapper {
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: MustacheTag | RawMustacheTag) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
this.cannot_use_innerhtml();
block.addDependencies(node.expression.dependencies);
block.add_dependencies(node.expression.dependencies);
}
renameThisMethod(
rename_this_method(
block: Block,
update: ((value: string) => string)
) {
const dependencies = this.node.expression.dynamic_dependencies();
const snippet = this.node.expression.render(block);
const value = this.node.shouldCache && block.getUniqueName(`${this.var}_value`);
const content = this.node.shouldCache ? value : snippet;
const value = this.node.should_cache && block.get_unique_name(`${this.var}_value`);
const content = this.node.should_cache ? value : snippet;
if (this.node.shouldCache) block.addVariable(value, snippet);
if (this.node.should_cache) block.add_variable(value, snippet);
if (dependencies.length > 0) {
const changedCheck = (
(block.hasOutros ? `!#current || ` : '') +
const changed_check = (
(block.has_outros ? `!#current || ` : '') +
dependencies.map((dependency: string) => `changed.${dependency}`).join(' || ')
);
const updateCachedValue = `${value} !== (${value} = ${snippet})`;
const update_cached_value = `${value} !== (${value} = ${snippet})`;
const condition =this.node.shouldCache
? `(${changedCheck}) && ${updateCachedValue}`
: changedCheck;
const condition =this.node.should_cache
? `(${changed_check}) && ${update_cached_value}`
: changed_check;
block.builders.update.add_conditional(
condition,

@ -11,7 +11,7 @@ export default class Wrapper {
next: Wrapper | null;
var: string;
canUseInnerHTML: boolean;
can_use_innerhtml: boolean;
constructor(
renderer: Renderer,
@ -32,43 +32,43 @@ export default class Wrapper {
}
});
this.canUseInnerHTML = !renderer.options.hydratable;
this.can_use_innerhtml = !renderer.options.hydratable;
block.wrappers.push(this);
}
cannotUseInnerHTML() {
this.canUseInnerHTML = false;
if (this.parent) this.parent.cannotUseInnerHTML();
cannot_use_innerhtml() {
this.can_use_innerhtml = false;
if (this.parent) this.parent.cannot_use_innerhtml();
}
getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
get_or_create_anchor(block: Block, parent_node: string, parent_nodes: string) {
// TODO use this in EachBlock and IfBlock — tricky because
// children need to be created first
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
const anchor = needsAnchor
? block.getUniqueName(`${this.var}_anchor`)
const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node();
const anchor = needs_anchor
? block.get_unique_name(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null';
if (needsAnchor) {
block.addElement(
if (needs_anchor) {
block.add_element(
anchor,
`@comment()`,
parentNodes && `@comment()`,
parentNode
parent_nodes && `@comment()`,
parent_node
);
}
return anchor;
}
getUpdateMountNode(anchor: string) {
return (this.parent && this.parent.isDomNode())
get_update_mount_node(anchor: string) {
return (this.parent && this.parent.is_dom_node())
? this.parent.var
: `${anchor}.parentNode`;
}
isDomNode() {
is_dom_node() {
return (
this.node.type === 'Element' ||
this.node.type === 'Text' ||

@ -2,7 +2,7 @@ import Block from '../../Block';
import Action from '../../../nodes/Action';
import Component from '../../../Component';
export default function addActions(
export default function add_actions(
component: Component,
block: Block,
target: string,
@ -17,11 +17,11 @@ export default function addActions(
dependencies = expression.dynamic_dependencies();
}
const name = block.getUniqueName(
const name = block.get_unique_name(
`${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action`
);
block.addVariable(name);
block.add_variable(name);
const fn = component.qualify(action.name);

@ -1,7 +1,7 @@
import Block from '../../Block';
import EventHandler from '../../../nodes/EventHandler';
export default function addEventHandlers(
export default function add_event_handlers(
block: Block,
target: string,
handlers: EventHandler[]
@ -14,12 +14,12 @@ export default function addEventHandlers(
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
if (opts.length) {
const optString = (opts.length === 1 && opts[0] === 'capture')
const opts_string = (opts.length === 1 && opts[0] === 'capture')
? 'true'
: `{ ${opts.map(opt => `${opt}: true`).join(', ')} }`;
block.event_listeners.push(
`@listen(${target}, "${handler.name}", ${snippet}, ${optString})`
`@listen(${target}, "${handler.name}", ${snippet}, ${opts_string})`
);
} else {
block.event_listeners.push(

@ -46,7 +46,7 @@ export default class Renderer {
append(code: string) {
if (this.targets.length) {
const target = this.targets[this.targets.length - 1];
const slotName = target.slotStack[target.slotStack.length - 1];
const slotName = target.slot_stack[target.slot_stack.length - 1];
target.slots[slotName] += code;
} else {
this.code += code;

@ -47,38 +47,38 @@ const boolean_attributes = new Set([
]);
export default function(node, renderer, options) {
let openingTag = `<${node.name}`;
let textareaContents; // awkward special case
let opening_tag = `<${node.name}`;
let textarea_contents; // awkward special case
const slot = node.getStaticAttributeValue('slot');
if (slot && node.hasAncestor('InlineComponent')) {
const slot = node.get_static_attribute_value('slot');
if (slot && node.has_ancestor('InlineComponent')) {
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
const slotName = slot.chunks[0].data;
const target = renderer.targets[renderer.targets.length - 1];
target.slotStack.push(slotName);
target.slot_stack.push(slotName);
target.slots[slotName] = '';
options.slot_scopes.set(slotName, get_slot_scope(node.lets));
}
const classExpr = node.classes.map((classDir: Class) => {
const { expression, name } = classDir;
const class_expression = node.classes.map((class_directive: Class) => {
const { expression, name } = class_directive;
const snippet = expression ? snip(expression) : `ctx${quote_prop_if_necessary(name)}`;
return `${snippet} ? "${name}" : ""`;
}).join(', ');
let addClassAttribute = classExpr ? true : false;
let add_class_attribute = class_expression ? true : false;
if (node.attributes.find(attr => attr.isSpread)) {
if (node.attributes.find(attr => attr.is_spread)) {
// TODO dry this out
const args = [];
node.attributes.forEach(attribute => {
if (attribute.isSpread) {
if (attribute.is_spread) {
args.push(snip(attribute.expression));
} else {
if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = stringify_attribute(attribute, true);
} else if (attribute.isTrue) {
textarea_contents = stringify_attribute(attribute, true);
} else if (attribute.is_true) {
args.push(`{ ${quote_name_if_necessary(attribute.name)}: true }`);
} else if (
boolean_attributes.has(attribute.name) &&
@ -93,32 +93,32 @@ export default function(node, renderer, options) {
}
});
openingTag += "${@spread([" + args.join(', ') + "])}";
opening_tag += "${@spread([" + args.join(', ') + "])}";
} else {
node.attributes.forEach((attribute: Attribute) => {
if (attribute.type !== 'Attribute') return;
if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = stringify_attribute(attribute, true);
} else if (attribute.isTrue) {
openingTag += ` ${attribute.name}`;
textarea_contents = stringify_attribute(attribute, true);
} else if (attribute.is_true) {
opening_tag += ` ${attribute.name}`;
} else if (
boolean_attributes.has(attribute.name) &&
attribute.chunks.length === 1 &&
attribute.chunks[0].type !== 'Text'
) {
// a boolean attribute with one non-Text chunk
openingTag += '${' + snip(attribute.chunks[0]) + ' ? " ' + attribute.name + '" : "" }';
} else if (attribute.name === 'class' && classExpr) {
addClassAttribute = false;
openingTag += ` class="\${[\`${stringify_attribute(attribute, true)}\`, ${classExpr}].join(' ').trim() }"`;
opening_tag += '${' + snip(attribute.chunks[0]) + ' ? " ' + attribute.name + '" : "" }';
} else if (attribute.name === 'class' && class_expression) {
add_class_attribute = false;
opening_tag += ` class="\${[\`${stringify_attribute(attribute, true)}\`, ${class_expression}].join(' ').trim() }"`;
} else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') {
const { name } = attribute;
const snippet = snip(attribute.chunks[0]);
openingTag += '${(v => v == null ? "" : ` ' + name + '="${@escape(' + snippet + ')}"`)(' + snippet + ')}';
opening_tag += '${(v => v == null ? "" : ` ' + name + '="${@escape(' + snippet + ')}"`)(' + snippet + ')}';
} else {
openingTag += ` ${attribute.name}="${stringify_attribute(attribute, true)}"`;
opening_tag += ` ${attribute.name}="${stringify_attribute(attribute, true)}"`;
}
});
}
@ -130,20 +130,20 @@ export default function(node, renderer, options) {
// TODO server-render group bindings
} else {
const snippet = snip(expression);
openingTag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}';
opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}';
}
});
if (addClassAttribute) {
openingTag += `\${((v) => v ? ' class="' + v + '"' : '')([${classExpr}].join(' ').trim())}`;
if (add_class_attribute) {
opening_tag += `\${((v) => v ? ' class="' + v + '"' : '')([${class_expression}].join(' ').trim())}`;
}
openingTag += '>';
opening_tag += '>';
renderer.append(openingTag);
renderer.append(opening_tag);
if (node.name === 'textarea' && textareaContents !== undefined) {
renderer.append(textareaContents);
if (node.name === 'textarea' && textarea_contents !== undefined) {
renderer.append(textarea_contents);
} else {
renderer.render(node.children, options);
}

@ -7,16 +7,16 @@ import { get_slot_scope } from './shared/get_slot_scope';
type AppendTarget = any; // TODO
function stringifyAttribute(chunk: Node) {
function stringify_attribute(chunk: Node) {
if (chunk.type === 'Text') {
return escape_template(escape(chunk.data));
}
return '${@escape( ' + snip(chunk) + ')}';
return '${@escape(' + snip(chunk) + ')}';
}
function getAttributeValue(attribute) {
if (attribute.isTrue) return `true`;
function get_attribute_value(attribute) {
if (attribute.is_true) return `true`;
if (attribute.chunks.length === 0) return `''`;
if (attribute.chunks.length === 1) {
@ -28,7 +28,7 @@ function getAttributeValue(attribute) {
return snip(chunk);
}
return '`' + attribute.chunks.map(stringifyAttribute).join('') + '`';
return '`' + attribute.chunks.map(stringify_attribute).join('') + '`';
}
export default function(node, renderer: Renderer, options) {
@ -45,18 +45,18 @@ export default function(node, renderer: Renderer, options) {
binding_fns.push(`${binding.name}: $$value => { ${snippet} = $$value; $$settled = false }`);
});
const usesSpread = node.attributes.find(attr => attr.isSpread);
const uses_spread = node.attributes.find(attr => attr.is_spread);
let props;
if (usesSpread) {
if (uses_spread) {
props = `Object.assign(${
node.attributes
.map(attribute => {
if (attribute.isSpread) {
if (attribute.is_spread) {
return snip(attribute.expression);
} else {
return `{ ${attribute.name}: ${getAttributeValue(attribute)} }`;
return `{ ${attribute.name}: ${get_attribute_value(attribute)} }`;
}
})
.concat(binding_props.map(p => `{ ${p} }`))
@ -65,7 +65,7 @@ export default function(node, renderer: Renderer, options) {
} else {
props = stringify_props(
node.attributes
.map(attribute => `${attribute.name}: ${getAttributeValue(attribute)}`)
.map(attribute => `${attribute.name}: ${get_attribute_value(attribute)}`)
.concat(binding_props)
);
}
@ -85,7 +85,7 @@ export default function(node, renderer: Renderer, options) {
if (node.children.length) {
const target: AppendTarget = {
slots: { default: '' },
slotStack: ['default']
slot_stack: ['default']
};
renderer.targets.push(target);

@ -31,7 +31,7 @@ export default function ssr(
const assignment = `${name} = @get_store_value(${store_name});`;
return component.compileOptions.dev
return component.compile_options.dev
? `@validate_store(${store_name}, '${store_name}'); ${assignment}`
: assignment;
});
@ -46,7 +46,7 @@ export default function ssr(
const get_store_value = component.helper('get_store_value');
let insert = `${value} = ${get_store_value}(${name})`;
if (component.compileOptions.dev) {
if (component.compile_options.dev) {
const validate_store = component.helper('validate_store');
insert = `${validate_store}(${name}, '${name}'); ${insert}`;
}

@ -102,7 +102,7 @@ describe('CodeBuilder', () => {
builder.add_line('// line 1');
builder.add_line('// line 2');
builder.add_block(deindent`
if ( foo ) {
if (foo) {
bar();
}
`);
@ -115,7 +115,7 @@ describe('CodeBuilder', () => {
// line 1
// line 2
if ( foo ) {
if (foo) {
bar();
}
@ -139,7 +139,7 @@ describe('CodeBuilder', () => {
builder.add_line('// line 1');
builder.add_line('// line 2');
builder.add_block(deindent`
if ( foo ) {
if (foo) {
${child}
}
`);
@ -152,7 +152,7 @@ describe('CodeBuilder', () => {
// line 1
// line 2
if ( foo ) {
if (foo) {
var obj = {
answer: 42
};

@ -5,7 +5,7 @@ export default function get_slot_data(attributes, is_ssr: boolean) {
return attributes
.filter(attribute => attribute.name !== 'name')
.map(attribute => {
const value = attribute.isTrue
const value = attribute.is_true
? 'true'
: attribute.chunks.length === 0
? '""'

@ -67,7 +67,7 @@ export interface Visitor {
export interface AppendTarget {
slots: Record<string, string>;
slotStack: string[]
slot_stack: string[]
}
export interface Var {

@ -5,7 +5,7 @@ export const xlink = 'http://www.w3.org/1999/xlink';
export const xml = 'http://www.w3.org/XML/1998/namespace';
export const xmlns = 'http://www.w3.org/2000/xmlns';
export const validNamespaces = [
export const valid_namespaces = [
'html',
'mathml',
'svg',

Loading…
Cancel
Save