more progress. everything still broken

pull/3539/head
Richard Harris 6 years ago
parent 93c7e1de11
commit e876f604a4

@ -16,7 +16,7 @@ import Stylesheet from './css/Stylesheet';
import { test } from '../config';
import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports';
import { Node, Ast, CompileOptions, Var, Warning } from '../interfaces';
import { Node, Ast, CompileOptions, Var, Warning, Identifier } from '../interfaces';
import error from '../utils/error';
import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference';
@ -90,7 +90,7 @@ export default class Component {
ast: Ast;
source: string;
code: MagicString;
name: string;
name: Identifier;
compile_options: CompileOptions;
fragment: Fragment;
module_scope: Scope;
@ -99,7 +99,7 @@ export default class Component {
component_options: ComponentOptions;
namespace: string;
tag: string;
tag: Identifier;
accessors: boolean;
vars: Var[] = [];
@ -122,8 +122,8 @@ export default class Component {
reactive_declaration_nodes: Set<Node> = new Set();
has_reactive_assignments = false;
injected_reactive_declaration_vars: Set<string> = new Set();
helpers: Map<string, string> = new Map();
globals: Map<string, string> = new Map();
helpers: Map<string, Identifier> = new Map();
globals: Map<string, Identifier> = new Map();
indirect_dependencies: Map<string, Set<string>> = new Map();
@ -141,7 +141,7 @@ export default class Component {
stylesheet: Stylesheet;
aliases: Map<string, string> = new Map();
aliases: Map<string, Identifier> = new Map();
used_names: Set<string> = new Set();
globally_used_names: Set<string> = new Set();
@ -156,7 +156,7 @@ export default class Component {
stats: Stats,
warnings: Warning[]
) {
this.name = name;
this.name = { type: 'Identifier', name };
this.stats = stats;
this.warnings = warnings;
@ -205,7 +205,10 @@ export default class Component {
message: `No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/>`,
});
}
this.tag = this.component_options.tag || compile_options.tag;
this.tag = {
type: 'Identifier',
name: this.component_options.tag || compile_options.tag
};
} else {
this.tag = this.name;
}
@ -335,7 +338,7 @@ export default class Component {
const referenced_globals = Array.from(
this.globals,
([name, alias]) => name !== alias && { name, alias }
([name, alias]) => name !== alias.name && { name, alias }
).filter(Boolean);
if (referenced_globals.length) {
this.helper('globals');
@ -348,7 +351,7 @@ export default class Component {
const module = create_module(
printed.code,
format,
name,
name.name,
banner,
compile_options.sveltePath,
imported_helpers,
@ -437,7 +440,7 @@ export default class Component {
};
}
get_unique_name(name: string) {
get_unique_name(name: string): Identifier {
if (test) name = `${name}$`;
let alias = name;
for (
@ -449,7 +452,7 @@ export default class Component {
alias = `${name}_${i++}`
);
this.used_names.add(alias);
return alias;
return { type: 'Identifier', name: alias };
}
get_unique_name_maker() {
@ -463,7 +466,7 @@ export default class Component {
internal_exports.forEach(add);
this.var_lookup.forEach((_value, key) => add(key));
return (name: string) => {
return (name: string): Identifier => {
if (test) name = `${name}$`;
let alias = name;
for (
@ -473,7 +476,11 @@ export default class Component {
);
local_used_names.add(alias);
this.globally_used_names.add(alias);
return alias;
return {
type: 'Identifier',
name: alias
};
};
}

@ -1,6 +1,6 @@
import deindent from './utils/deindent';
import list from '../utils/list';
import { ModuleFormat, Node } from '../interfaces';
import { ModuleFormat, Node, Identifier } from '../interfaces';
import { stringify_props } from './utils/stringify_props';
const wrappers = { esm, cjs };
@ -16,8 +16,8 @@ export default function create_module(
name: string,
banner: string,
sveltePath = 'svelte',
helpers: Array<{ name: string; alias: string }>,
globals: Array<{ name: string; alias: string }>,
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
module_exports: Export[],
source: string
@ -45,14 +45,14 @@ function esm(
banner: string,
sveltePath: string,
internal_path: string,
helpers: Array<{ name: string; alias: string }>,
globals: Array<{ name: string; alias: string }>,
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
module_exports: Export[],
source: string
) {
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)};`
`import ${stringify_props(helpers.map(h => h.name === h.alias.name ? h.name : `${h.name} as ${h.alias}`).sort())} from ${JSON.stringify(internal_path)};`
);
const internal_globals = globals.length > 0 && (
`const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};`
@ -90,12 +90,12 @@ function cjs(
banner: string,
sveltePath: string,
internal_path: string,
helpers: Array<{ name: string; alias: string }>,
globals: Array<{ name: string; alias: string }>,
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: Node[],
module_exports: Export[]
) {
const declarations = helpers.map(h => `${h.alias === h.name ? h.name : `${h.name}: ${h.alias}`}`).sort();
const declarations = helpers.map(h => `${h.alias.name === h.name ? h.name : `${h.name}: ${h.alias}`}`).sort();
const internal_imports = helpers.length > 0 && (
`const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n`

@ -4,13 +4,14 @@ import Component from '../Component';
import deindent from '../utils/deindent';
import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names';
import { Identifier } from '../../interfaces';
export default class EventHandler extends Node {
type: 'EventHandler';
name: string;
modifiers: Set<string>;
expression: Expression;
handler_name: string;
handler_name: Identifier;
uses_context = false;
can_make_passive = false;
@ -42,21 +43,21 @@ export default class EventHandler extends Node {
}
}
} else {
const name = component.get_unique_name(`${sanitize(this.name)}_handler`);
const id = component.get_unique_name(`${sanitize(this.name)}_handler`);
component.add_var({
name,
name: id.name,
internal: true,
referenced: true
});
component.partly_hoisted.push(deindent`
function ${name}(event) {
function ${id}(event) {
@bubble($$self, event);
}
`);
this.handler_name = name;
this.handler_name = id;
}
}

@ -307,7 +307,7 @@ export default class Expression {
if (map.has(node)) scope = scope.parent;
if (node === function_expression) {
const name = component.get_unique_name(
const id = component.get_unique_name(
sanitize(get_function_name(node, owner))
);
@ -325,16 +325,16 @@ export default class Expression {
const body = code.slice(node.body.start, node.body.end).trim();
const fn = node.type === 'FunctionExpression'
? `${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${name}(${args.join(', ')}) ${body}`
: `const ${name} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`;
? `${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${id}(${args.join(', ')}) ${body}`
: `const ${id} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`;
if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(fn);
code.overwrite(node.start, node.end, name);
code.overwrite(node.start, node.end, id.name);
component.add_var({
name,
name: id.name,
internal: true,
hoistable: true,
referenced: true
@ -344,10 +344,10 @@ export default class Expression {
else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(fn);
code.overwrite(node.start, node.end, `ctx.${name}`);
code.overwrite(node.start, node.end, `ctx.${id}`);
component.add_var({
name,
name: id.name,
internal: true,
referenced: true
});
@ -356,17 +356,17 @@ export default class Expression {
else {
// we need a combo block/init recipe
component.partly_hoisted.push(fn);
code.overwrite(node.start, node.end, name);
code.overwrite(node.start, node.end, id.name);
component.add_var({
name,
name: id.name,
internal: true,
referenced: true
});
declarations.push(b`
function ${name}(${original_params ? '...args' : ''}) {
return ctx.${name}(ctx${original_params ? ', ...args' : ''});
function ${id}(${original_params ? '...args' : ''}) {
return ctx.${id}(ctx${original_params ? ', ...args' : ''});
}
`);
}

@ -2,34 +2,46 @@ import Renderer from './Renderer';
import Wrapper from './wrappers/shared/Wrapper';
import { escape } from '../utils/stringify';
import { b, x } from 'code-red';
import { Node } from '../../interfaces';
import { Node, Identifier } from '../../interfaces';
export interface BlockOptions {
parent?: Block;
name: string;
name: Identifier;
type: string;
renderer?: Renderer;
comment?: string;
key?: string;
bindings?: Map<string, { object: string; property: string; snippet: string; store: string; tail: string }>;
key?: Identifier;
bindings?: Map<string, {
object: Identifier;
property: Identifier;
snippet: string;
store: string;
tail: string;
}>;
dependencies?: Set<string>;
}
export default class Block {
parent?: Block;
renderer: Renderer;
name: string;
name: Identifier;
type: string;
comment?: string;
wrappers: Wrapper[];
key: string;
first: string;
key: Identifier;
first: Identifier;
dependencies: Set<string>;
bindings: Map<string, { object: string; property: string; snippet: string; store: string; tail: string }>;
bindings: Map<string, {
object: Identifier;
property: Identifier;
snippet: string;
store: string;
tail: string
}>;
chunks: {
init: Node[];
@ -56,9 +68,9 @@ export default class Block {
has_outro_method: boolean;
outros: number;
aliases: Map<string, string>;
aliases: Map<string, Identifier>;
variables: Map<string, string>;
get_unique_name: (name: string) => string;
get_unique_name: (name: string) => Identifier;
has_update_method = false;
autofocus: string;
@ -108,6 +120,7 @@ export default class Block {
}
assign_variable_names() {
// TODO reimplement for #3539
const seen = new Set();
const dupes = new Set();
@ -139,7 +152,7 @@ export default class Block {
counts.set(wrapper.var, i + 1);
wrapper.var = this.get_unique_name(wrapper.var + i);
} else {
wrapper.var = this.get_unique_name(wrapper.var);
wrapper.var = this.get_unique_name(wrapper.var.name);
}
}
}
@ -154,8 +167,8 @@ export default class Block {
add_element(
name: string,
render_statement: string,
claim_statement: string,
render_statement: Node,
claim_statement: Node,
parent_node: string,
no_detach?: boolean
) {
@ -192,7 +205,7 @@ export default class Block {
add_variable(name: string, init?: string) {
if (name[0] === '#') {
name = this.alias(name.slice(1));
name = this.alias(name.slice(1)).name;
}
if (this.variables.has(name) && this.variables.get(name) !== init) {
@ -281,7 +294,7 @@ export default class Block {
properties.mount = noop;
} else {
properties.mount = x`function mount(#target, anchor) {
${this.chunks.mount}
//${this.chunks.mount}
}`;
}
@ -338,7 +351,7 @@ export default class Block {
}`;
}
const return_value = x`{
const return_value: any = x`{
key: ${properties.key},
first: ${properties.first},
c: ${properties.create},
@ -354,13 +367,17 @@ export default class Block {
d: ${properties.destroy}
}`;
return_value.properties = return_value.properties.filter(prop => prop.value);
/* eslint-disable @typescript-eslint/indent,indent */
return b`
${Array.from(this.variables.entries()).map(([init, id]) => {
${Array.from(this.variables.entries()).map(([id, init]) => {
const id_node = { type: 'Identifier', name: id };
const init_node = { type: 'Identifier', name: init };
return b`let ${id_node} = ${init_node}`;
return init
? b`let ${id_node} = ${init_node}`
: b`let ${id_node}`;
})}
${this.chunks.init}

@ -1,5 +1,5 @@
import Block from './Block';
import { CompileOptions, Node } from '../../interfaces';
import { CompileOptions, Node, Identifier } from '../../interfaces';
import Component from '../Component';
import FragmentWrapper from './wrappers/Fragment';
@ -15,7 +15,7 @@ export default class Renderer {
block: Block;
fragment: FragmentWrapper;
file_var: string;
file_var: Identifier;
locate: (c: number) => { line: number; column: number };
constructor(component: Component, options: CompileOptions) {

@ -8,6 +8,7 @@ import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
import { Identifier } from '../../../interfaces';
class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
@ -54,7 +55,7 @@ export default class AwaitBlockWrapper extends Wrapper {
then: AwaitBlockBranch;
catch: AwaitBlockBranch;
var = 'await_block';
var: Identifier = { type: 'Identifier', name: 'await_block' };
constructor(
renderer: Renderer,
@ -131,7 +132,7 @@ export default class AwaitBlockWrapper extends Wrapper {
const info = block.get_unique_name(`info`);
const promise = block.get_unique_name(`promise`);
block.add_variable(promise);
block.add_variable(promise.name);
block.maintain_context = true;

@ -4,9 +4,10 @@ import Wrapper from './shared/Wrapper';
import create_debugging_comment from './shared/create_debugging_comment';
import EachBlock from '../../nodes/EachBlock';
import FragmentWrapper from './Fragment';
import { b } from 'code-red';
import { b, x } from 'code-red';
import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
import { Identifier } from '../../../interfaces';
export class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
@ -51,10 +52,10 @@ export default class EachBlockWrapper extends Wrapper {
fragment: FragmentWrapper;
else?: ElseBlockWrapper;
vars: {
create_each_block: string;
each_block_value: string;
get_each_context: string;
iterations: string;
create_each_block: Identifier;
each_block_value: Identifier;
get_each_context: Identifier;
iterations: Identifier;
fixed_length: number;
data_length: string;
view_length: string;
@ -62,9 +63,9 @@ export default class EachBlockWrapper extends Wrapper {
}
context_props: string[];
index_name: string;
index_name: Identifier;
var = 'each';
var: Identifier = { type: 'Identifier', name: 'each' };
constructor(
renderer: Renderer,
@ -93,7 +94,9 @@ export default class EachBlockWrapper extends Wrapper {
// TODO this seems messy
this.block.has_animation = this.node.has_animation;
this.index_name = this.node.index || renderer.component.get_unique_name(`${this.node.context}_index`);
this.index_name = this.node.index
? { type: 'Identifier', name: this.node.index }
: renderer.component.get_unique_name(`${this.node.context}_index`);
const fixed_length =
node.expression.node.type === 'ArrayExpression' &&
@ -198,12 +201,12 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
const initial_anchor_node = parent_node ? 'null' : 'anchor';
const initial_mount_node = parent_node || '#target';
const initial_anchor_node: Identifier = { type: 'Identifier', name: parent_node ? 'null' : 'anchor' };
const initial_mount_node: Identifier = { type: 'Identifier', name: parent_node || '#target' };
const update_anchor_node = needs_anchor
? block.get_unique_name(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null';
const update_mount_node = this.get_update_mount_node(update_anchor_node);
: (this.next && this.next.var) || { type: 'Identifier', name: 'null' };
const update_mount_node: Identifier = this.get_update_mount_node((update_anchor_node as Identifier));
const args = {
block,
@ -232,9 +235,9 @@ export default class EachBlockWrapper extends Wrapper {
if (needs_anchor) {
block.add_element(
update_anchor_node,
`@empty()`,
parent_nodes && `@empty()`,
(update_anchor_node as Identifier).name,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node
);
}
@ -312,10 +315,10 @@ export default class EachBlockWrapper extends Wrapper {
parent_node: string;
parent_nodes: string;
snippet: string;
initial_anchor_node: string;
initial_mount_node: string;
update_anchor_node: string;
update_mount_node: string;
initial_anchor_node: Identifier;
initial_mount_node: Identifier;
update_anchor_node: Identifier;
update_mount_node: Identifier;
}) {
const {
create_each_block,
@ -327,17 +330,17 @@ export default class EachBlockWrapper extends Wrapper {
const get_key = block.get_unique_name('get_key');
const lookup = block.get_unique_name(`${this.var}_lookup`);
block.add_variable(iterations, '[]');
block.add_variable(lookup, `new @_Map()`);
block.add_variable(iterations.name, '[]');
block.add_variable(lookup.name, `new @_Map()`);
if (this.fragment.nodes[0].is_dom_node()) {
this.block.first = this.fragment.nodes[0].var;
} else {
this.block.first = this.block.get_unique_name('first');
this.block.add_element(
this.block.first,
`@empty()`,
parent_nodes && `@empty()`,
this.block.first.name,
x`@empty()`,
parent_nodes && x`@empty()`,
null
);
}
@ -421,10 +424,10 @@ export default class EachBlockWrapper extends Wrapper {
block: Block;
parent_nodes: string;
snippet: string;
initial_anchor_node: string;
initial_mount_node: string;
update_anchor_node: string;
update_mount_node: string;
initial_anchor_node: Identifier;
initial_mount_node: Identifier;
update_anchor_node: Identifier;
update_mount_node: Identifier;
}) {
const {
create_each_block,

@ -99,7 +99,7 @@ export default class AttributeWrapper {
`${element.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`
);
if (should_cache) block.add_variable(last);
if (should_cache) block.add_variable(last.name);
let updater;
const init = should_cache ? `${last} = ${value}` : value;

@ -152,7 +152,7 @@ export default class BindingWrapper {
{
// this is necessary to prevent audio restarting by itself
const last = block.get_unique_name(`${parent.var}_is_paused`);
block.add_variable(last, 'true');
block.add_variable(last.name, 'true');
update_conditions.push(`${last} !== (${last} = ${this.snippet})`);
update_dom = b`${parent.var}[${last} ? "pause" : "play"]();`;
@ -272,7 +272,7 @@ function get_event_handler(
mutation: store
? mutate_store(store, value, tail)
: `${snippet}${tail} = ${value};`,
contextual_dependencies: new Set([object, property])
contextual_dependencies: new Set([object.name, property.name])
};
}

@ -4,7 +4,7 @@ import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import { is_void, quote_prop_if_necessary, quote_name_if_necessary, sanitize } from '../../../../utils/names';
import FragmentWrapper from '../Fragment';
import { stringify, escape_html, escape } from '../../../utils/stringify';
import { escape_html, escape, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text';
import fix_attribute_casing from './fix_attribute_casing';
import { b, x } from 'code-red';
@ -114,7 +114,7 @@ export default class ElementWrapper extends Wrapper {
slot_block: Block;
select_binding_dependencies?: Set<string>;
var: string;
var: any;
constructor(
renderer: Renderer,
@ -125,7 +125,10 @@ export default class ElementWrapper extends Wrapper {
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.var = node.name.replace(/[^a-zA-Z0-9_$]/g, '_');
this.var = {
type: 'Identifier',
name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
};
this.class_dependencies = [];
@ -288,7 +291,7 @@ export default class ElementWrapper extends Wrapper {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.chunks.create.push(
// @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead?
b`${node}.textContent = ${stringify(this.fragment.nodes[0].data)};`
b`${node}.textContent = ${string_literal(this.fragment.nodes[0].data)};`
);
} else {
const inner_html = escape(
@ -306,7 +309,7 @@ export default class ElementWrapper extends Wrapper {
child.render(
block,
this.node.name === 'template' ? `${node}.content` : node,
nodes
nodes.name
);
});
}
@ -377,19 +380,19 @@ export default class ElementWrapper extends Wrapper {
const { name, namespace } = this.node;
if (namespace === 'http://www.w3.org/2000/svg') {
return `@svg_element("${name}")`;
return x`@svg_element("${name}")`;
}
if (namespace) {
return `@_document.createElementNS("${namespace}", "${name}")`;
return x`@_document.createElementNS("${namespace}", "${name}")`;
}
const is = this.attributes.find(attr => attr.node.name === 'is');
if (is) {
return `@element_is("${name}", ${is.render_chunks().join(' + ')});`;
return x`@element_is("${name}", ${is.render_chunks().join(' + ')});`;
}
return `@element("${name}")`;
return x`@element("${name}")`;
}
get_claim_statement(nodes: string) {
@ -402,9 +405,9 @@ export default class ElementWrapper extends Wrapper {
? this.node.name
: this.node.name.toUpperCase();
return `@claim_element(${nodes}, "${name}", ${attributes
? `{ ${attributes} }`
: `{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
return x`@claim_element(${nodes}, "${name}", ${attributes
? x`{ ${attributes} }`
: x`{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
}
add_bindings(block: Block) {
@ -418,7 +421,7 @@ export default class ElementWrapper extends Wrapper {
block.get_unique_name(`${this.var}_updating`) :
null;
if (lock) block.add_variable(lock, 'false');
if (lock) block.add_variable(lock.name, 'false');
const groups = events
.map(event => ({
@ -433,7 +436,7 @@ export default class ElementWrapper extends Wrapper {
const handler = renderer.component.get_unique_name(`${this.var}_${group.events.join('_')}_handler`);
renderer.component.add_var({
name: handler,
name: handler.name,
internal: true,
referenced: true
});
@ -450,7 +453,7 @@ export default class ElementWrapper extends Wrapper {
add_to_set(contextual_dependencies, binding.node.expression.contextual_dependencies);
add_to_set(contextual_dependencies, binding.handler.contextual_dependencies);
binding.render(block, lock);
binding.render(block, lock.name);
});
// media bindings — awkward special case. The native timeupdate events
@ -505,7 +508,7 @@ export default class ElementWrapper extends Wrapper {
if (name === 'resize') {
// special case
const resize_listener = block.get_unique_name(`${this.var}_resize_listener`);
block.add_variable(resize_listener);
block.add_variable(resize_listener.name);
block.chunks.mount.push(
b`${resize_listener} = @add_resize_listener(${this.var}, ${callee}.bind(${this.var}));`
@ -654,7 +657,7 @@ export default class ElementWrapper extends Wrapper {
? intro.expression.render(block)
: '{}';
block.add_variable(name);
block.add_variable(name.name);
const fn = component.qualify(intro.name);
@ -695,7 +698,7 @@ export default class ElementWrapper extends Wrapper {
const outro_name = outro && block.get_unique_name(`${this.var}_outro`);
if (intro) {
block.add_variable(intro_name);
block.add_variable(intro_name.name);
const snippet = intro.expression
? intro.expression.render(block)
: '{}';
@ -737,7 +740,7 @@ export default class ElementWrapper extends Wrapper {
}
if (outro) {
block.add_variable(outro_name);
block.add_variable(outro_name.name);
const snippet = outro.expression
? outro.expression.render(block)
: '{}';
@ -780,8 +783,8 @@ export default class ElementWrapper extends Wrapper {
const rect = block.get_unique_name('rect');
const stop_animation = block.get_unique_name('stop_animation');
block.add_variable(rect);
block.add_variable(stop_animation, '@noop');
block.add_variable(rect.name);
block.add_variable(stop_animation.name, '@noop');
block.chunks.measure.push(b`
${rect} = ${this.var}.getBoundingClientRect();

@ -6,8 +6,10 @@ import IfBlock from '../../nodes/IfBlock';
import create_debugging_comment from './shared/create_debugging_comment';
import ElseBlock from '../../nodes/ElseBlock';
import FragmentWrapper from './Fragment';
import { b } from 'code-red';
import { b, x } from 'code-red';
import { walk } from 'estree-walker';
import { Identifier } from '../../../interfaces';
import Node from '../../nodes/shared/Node';
function is_else_if(node: ElseBlock) {
return (
@ -19,8 +21,8 @@ class IfBlockBranch extends Wrapper {
block: Block;
fragment: FragmentWrapper;
dependencies?: string[];
condition?: string;
snippet?: string;
condition?: any;
snippet?: Node;
is_dynamic: boolean;
var = null;
@ -54,9 +56,9 @@ class IfBlockBranch extends Wrapper {
if (should_cache) {
this.condition = block.get_unique_name(`show_if`);
this.snippet = expression.render(block);
this.snippet = expression.node;
} else {
this.condition = expression.render(block);
this.condition = expression.node;
}
}
@ -79,7 +81,7 @@ export default class IfBlockWrapper extends Wrapper {
branches: IfBlockBranch[];
needs_update = false;
var = 'if_block';
var: Identifier = { type: 'Identifier', name: 'if_block' };
constructor(
renderer: Renderer,
@ -224,9 +226,9 @@ export default class IfBlockWrapper extends Wrapper {
if (needs_anchor) {
block.add_element(
anchor,
`@empty()`,
parent_nodes && `@empty()`,
(anchor as Identifier).name,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node
);
}
@ -340,7 +342,7 @@ export default class IfBlockWrapper extends Wrapper {
? ''
: `if (~${current_block_type_index}) `;
block.add_variable(current_block_type_index);
block.add_variable(current_block_type_index.name);
block.add_variable(name);
/* eslint-disable @typescript-eslint/indent,indent */
@ -470,7 +472,7 @@ export default class IfBlockWrapper extends Wrapper {
) {
const branch = this.branches[0];
if (branch.snippet) block.add_variable(branch.condition, branch.snippet);
if (branch.snippet) block.add_variable(branch.condition, '' + branch.snippet);
block.chunks.init.push(b`
var ${name} = (${branch.condition}) && ${branch.block.name}(ctx);

@ -6,7 +6,7 @@ import FragmentWrapper from '../Fragment';
import { quote_name_if_necessary, quote_prop_if_necessary, sanitize } from '../../../../utils/names';
import { stringify_props } from '../../../utils/stringify_props';
import add_to_set from '../../../utils/add_to_set';
import { b } from 'code-red';
import { b, x } from 'code-red';
import Attribute from '../../../nodes/Attribute';
import get_object from '../../../utils/get_object';
import create_debugging_comment from '../shared/create_debugging_comment';
@ -15,9 +15,10 @@ import EachBlock from '../../../nodes/EachBlock';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this';
import { Identifier } from '../../../../interfaces';
export default class InlineComponentWrapper extends Wrapper {
var: string;
var: Identifier;
slots: Map<string, { block: Block; scope: TemplateScope; fn?: string }> = new Map();
node: InlineComponent;
fragment: FragmentWrapper;
@ -61,11 +62,14 @@ export default class InlineComponentWrapper extends Wrapper {
}
});
this.var = (
this.node.name === 'svelte:self' ? renderer.component.name :
this.node.name === 'svelte:component' ? 'switch_instance' :
sanitize(this.node.name)
).toLowerCase();
this.var = {
type: 'Identifier',
name: (
this.node.name === 'svelte:self' ? renderer.component.name.name :
this.node.name === 'svelte:component' ? 'switch_instance' :
sanitize(this.node.name)
).toLowerCase()
};
if (this.node.children.length) {
const default_slot = block.child({
@ -253,19 +257,19 @@ export default class InlineComponentWrapper extends Wrapper {
component.has_reactive_assignments = true;
if (binding.name === 'this') {
return bind_this(component, block, binding, this.var);
return bind_this(component, block, binding, this.var.name);
}
const name = component.get_unique_name(`${this.var}_${binding.name}_binding`);
const id = component.get_unique_name(`${this.var}_${binding.name}_binding`);
component.add_var({
name,
name: id.name,
internal: true,
referenced: true
});
const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating);
block.add_variable(updating.name);
const snippet = binding.expression.render(block);
@ -292,13 +296,13 @@ export default class InlineComponentWrapper extends Wrapper {
const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name);
lhs = snippet;
contextual_dependencies.push(object, property);
contextual_dependencies.push(object.name, property.name);
}
const value = block.get_unique_name('value');
const args = [value];
const args: any[] = [value];
if (contextual_dependencies.length > 0) {
args.push(`{ ${contextual_dependencies.join(', ')} }`);
args.push(x`{ ${contextual_dependencies.join(', ')} }`);
block.chunks.init.push(b`
function ${name}(${value}) {
@ -311,7 +315,7 @@ export default class InlineComponentWrapper extends Wrapper {
block.maintain_context = true; // TODO put this somewhere more logical
} else {
block.chunks.init.push(b`
function ${name}(${value}) {
function ${id}(${value}) {
ctx.${name}.call(null, ${value});
${updating} = true;
@add_flush_callback(() => ${updating} = false);
@ -320,7 +324,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
const body = b`
function ${name}(${args.join(', ')}) {
function ${id}(${args.join(', ')}) {
${lhs} = ${value};
${component.invalidate(dependencies[0])};
}
@ -328,7 +332,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.partly_hoisted.push(body);
return `@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${name}));`;
return `@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`;
});
const munged_handlers = this.node.handlers.map(handler => {

@ -4,9 +4,11 @@ import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Identifier } from '../../../interfaces';
import { x } from 'code-red';
export default class MustacheTagWrapper extends Tag {
var = 't';
var: Identifier = { type: 'Identifier', name: 't' };
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: MustacheTag | RawMustacheTag) {
super(renderer, block, parent, node);
@ -20,9 +22,9 @@ export default class MustacheTagWrapper extends Tag {
);
block.add_element(
this.var,
`@text(${init})`,
parent_nodes && `@claim_text(${parent_nodes}, ${init})`,
this.var.name,
x`@text(${init})`,
parent_nodes && x`@claim_text(${parent_nodes}, ${init})`,
parent_node
);
}

@ -1,13 +1,14 @@
import { b } from 'code-red';
import { b, x } from 'code-red';
import Renderer from '../Renderer';
import Block from '../Block';
import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Identifier } from '../../../interfaces';
export default class RawMustacheTagWrapper extends Tag {
var = 'raw';
var: Identifier = { type: 'Identifier', name: 'raw' };
constructor(
renderer: Renderer,
@ -41,7 +42,7 @@ export default class RawMustacheTagWrapper extends Tag {
const html_tag = block.get_unique_name('html_tag');
const html_anchor = needs_anchor && block.get_unique_name('html_anchor');
block.add_variable(html_tag);
block.add_variable(html_tag.name);
const { init } = this.rename_this_method(
block,
@ -54,7 +55,7 @@ export default class RawMustacheTagWrapper extends Tag {
block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}${parent_node ? '' : ', anchor'});`);
if (needs_anchor) {
block.add_element(html_anchor, '@empty()', '@empty()', parent_node);
block.add_element(html_anchor.name, x`@empty()`, x`@empty()`, parent_node);
}
if (!parent_node || in_head) {

@ -10,12 +10,13 @@ import get_slot_data from '../../utils/get_slot_data';
import { stringify_props } from '../../utils/stringify_props';
import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic';
import { Identifier } from '../../../interfaces';
export default class SlotWrapper extends Wrapper {
node: Slot;
fragment: FragmentWrapper;
var = 'slot';
var: Identifier = { type: 'Identifier', name: 'slot' };
dependencies: Set<string> = new Set(['$$scope']);
constructor(

@ -3,6 +3,8 @@ import Block from '../Block';
import Text from '../../nodes/Text';
import Wrapper from './shared/Wrapper';
import { stringify } from '../../utils/stringify';
import { Identifier } from '../../../interfaces';
import { x } from 'code-red';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
@ -33,7 +35,7 @@ export default class TextWrapper extends Wrapper {
node: Text;
data: string;
skip: boolean;
var: string;
var: Identifier;
constructor(
renderer: Renderer,
@ -46,7 +48,7 @@ export default class TextWrapper extends Wrapper {
this.skip = should_skip(this.node);
this.data = data;
this.var = this.skip ? null : 't';
this.var = (this.skip ? null : x`t`) as unknown as Identifier;
}
use_space() {
@ -69,9 +71,9 @@ export default class TextWrapper extends Wrapper {
const use_space = this.use_space();
block.add_element(
this.var,
use_space ? `@space()` : `@text(${stringify(this.data)})`,
parent_nodes && (use_space ? `@claim_space(${parent_nodes})` : `@claim_text(${parent_nodes}, ${stringify(this.data)})`),
this.var.name,
use_space ? x`@space()` : x`@text(${stringify(this.data)})`,
parent_nodes && (use_space ? x`@claim_space(${parent_nodes})` : x`@claim_text(${parent_nodes}, ${stringify(this.data)})`),
parent_node
);
}

@ -64,7 +64,7 @@ export default class TitleWrapper extends Wrapper {
`title_value`
);
if (this.node.should_cache) block.add_variable(last);
if (this.node.should_cache) block.add_variable(last.name);
const init = this.node.should_cache ? `${last} = ${value}` : value;

@ -73,14 +73,14 @@ export default class WindowWrapper extends Wrapper {
const scrolling_timeout = block.get_unique_name(`scrolling_timeout`);
Object.keys(events).forEach(event => {
const handler_name = block.get_unique_name(`onwindow${event}`);
const id = block.get_unique_name(`onwindow${event}`);
const props = events[event];
if (event === 'scroll') {
// TODO other bidirectional bindings...
block.add_variable(scrolling, 'false');
block.add_variable(clear_scrolling, `() => { ${scrolling} = false }`);
block.add_variable(scrolling_timeout);
block.add_variable(scrolling.name, 'false');
block.add_variable(clear_scrolling.name, `() => { ${scrolling} = false }`);
block.add_variable(scrolling_timeout.name);
const condition = [
bindings.scrollX && `"${bindings.scrollX}" in this._state`,
@ -103,7 +103,7 @@ export default class WindowWrapper extends Wrapper {
${scrolling} = true;
@_clearTimeout(${scrolling_timeout});
${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100);
ctx.${handler_name}();
ctx.${id}();
})
`);
} else {
@ -114,24 +114,24 @@ export default class WindowWrapper extends Wrapper {
});
block.event_listeners.push(b`
@listen(@_window, "${event}", ctx.${handler_name})
@listen(@_window, "${event}", ctx.${id})
`);
}
component.add_var({
name: handler_name,
name: id.name,
internal: true,
referenced: true
});
component.partly_hoisted.push(b`
function ${handler_name}() {
function ${id}() {
${props.map(prop => `${prop.name} = @_window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)}
}
`);
block.chunks.init.push(b`
@add_render_callback(ctx.${handler_name});
@add_render_callback(ctx.${id});
`);
component.has_reactive_assignments = true;
@ -159,28 +159,28 @@ 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.get_unique_name(`onlinestatuschanged`);
const id = block.get_unique_name(`onlinestatuschanged`);
const name = bindings.online;
component.add_var({
name: handler_name,
name: id.name,
internal: true,
referenced: true
});
component.partly_hoisted.push(b`
function ${handler_name}() {
function ${id}() {
${name} = @_navigator.onLine; $$invalidate('${name}', ${name});
}
`);
block.chunks.init.push(b`
@add_render_callback(ctx.${handler_name});
@add_render_callback(ctx.${id});
`);
block.event_listeners.push(
`@listen(@_window, "online", ctx.${handler_name})`,
`@listen(@_window, "offline", ctx.${handler_name})`
`@listen(@_window, "online", ctx.${id})`,
`@listen(@_window, "offline", ctx.${id})`
);
component.has_reactive_assignments = true;

@ -20,12 +20,12 @@ export default class Tag extends Wrapper {
update: ((value: string) => string)
) {
const dependencies = this.node.expression.dynamic_dependencies();
const snippet = this.node.expression.render(block);
const snippet = this.node.expression.node;
const value = this.node.should_cache && block.get_unique_name(`${this.var}_value`);
const content = this.node.should_cache ? value : snippet;
if (this.node.should_cache) block.add_variable(value, `${snippet} + ""`);
if (this.node.should_cache) block.add_variable(value.name, `${snippet} + ""`);
if (dependencies.length > 0) {
const changed_check = (

@ -1,6 +1,8 @@
import Renderer from '../../Renderer';
import Block from '../../Block';
import { INode } from '../../../nodes/interfaces';
import { Identifier } from '../../../../interfaces';
import { x } from 'code-red';
export default class Wrapper {
renderer: Renderer;
@ -10,7 +12,7 @@ export default class Wrapper {
prev: Wrapper | null;
next: Wrapper | null;
var: string;
var: Identifier;
can_use_innerhtml: boolean;
constructor(
@ -48,13 +50,13 @@ export default class Wrapper {
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';
: (this.next && this.next.var) || { type: 'Identifier', name: 'null' };
if (needs_anchor) {
block.add_element(
anchor,
`@empty()`,
parent_nodes && `@empty()`,
anchor.name,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node
);
}
@ -62,10 +64,10 @@ export default class Wrapper {
return anchor;
}
get_update_mount_node(anchor: string) {
return (this.parent && this.parent.is_dom_node())
get_update_mount_node(anchor: Identifier): Identifier {
return ((this.parent && this.parent.is_dom_node())
? this.parent.var
: `${anchor}.parentNode`;
: x`${anchor}.parentNode`) as Identifier;
}
is_dom_node() {

@ -19,30 +19,30 @@ export default function add_actions(
dependencies = expression.dynamic_dependencies();
}
const name = block.get_unique_name(
const id = block.get_unique_name(
`${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action`
);
block.add_variable(name);
block.add_variable(id.name);
const fn = component.qualify(action.name);
block.chunks.mount.push(
b`${name} = ${fn}.call(null, ${target}${snippet ? `, ${snippet}` : ''}) || {};`
b`${id} = ${fn}.call(null, ${target}${snippet ? `, ${snippet}` : ''}) || {};`
);
if (dependencies && dependencies.length > 0) {
let conditional = `typeof ${name}.update === 'function' && `;
let conditional = `typeof ${id}.update === 'function' && `;
const deps = dependencies.map(dependency => `changed.${dependency}`).join(' || ');
conditional += dependencies.length > 1 ? `(${deps})` : deps;
block.chunks.update.push(
b`if (${conditional}) ${name}.update.call(null, ${snippet});`
b`if (${conditional}) ${id}.update.call(null, ${snippet});`
);
}
block.chunks.destroy.push(
b`if (${name} && typeof ${name}.destroy === 'function') ${name}.destroy();`
b`if (${id} && typeof ${id}.destroy === 'function') ${id}.destroy();`
);
});
}

@ -8,7 +8,7 @@ export default function bind_this(component: Component, block: Block, binding: B
const fn = component.get_unique_name(`${variable}_binding`);
component.add_var({
name: fn,
name: fn.name,
internal: true,
referenced: true
});

@ -2,6 +2,13 @@ export function stringify(data: string, options = {}) {
return JSON.stringify(escape(data, options));
}
export function string_literal(data: string) {
return {
type: 'Literal',
value: data
};
}
export function escape(data: string, { only_escape_at_symbol = false } = {}) {
return data.replace(only_escape_at_symbol ? /@+/g : /(@+|#+)/g, (match: string) => {
return match + match[0];

@ -1,3 +1,8 @@
export interface Identifier {
type: 'Identifier';
name: string;
}
interface BaseNode {
start: number;
end: number;

@ -0,0 +1,5 @@
export default {
solo: 1,
html: '<h1>Hello world!</h1>'
};

@ -0,0 +1 @@
<h1>Hello world!</h1>
Loading…
Cancel
Save