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 { test } from '../config';
import Fragment from './nodes/Fragment'; import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports'; 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 error from '../utils/error';
import get_code_frame from '../utils/get_code_frame'; import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference'; import flatten_reference from './utils/flatten_reference';
@ -90,7 +90,7 @@ export default class Component {
ast: Ast; ast: Ast;
source: string; source: string;
code: MagicString; code: MagicString;
name: string; name: Identifier;
compile_options: CompileOptions; compile_options: CompileOptions;
fragment: Fragment; fragment: Fragment;
module_scope: Scope; module_scope: Scope;
@ -99,7 +99,7 @@ export default class Component {
component_options: ComponentOptions; component_options: ComponentOptions;
namespace: string; namespace: string;
tag: string; tag: Identifier;
accessors: boolean; accessors: boolean;
vars: Var[] = []; vars: Var[] = [];
@ -122,8 +122,8 @@ export default class Component {
reactive_declaration_nodes: Set<Node> = new Set(); reactive_declaration_nodes: Set<Node> = new Set();
has_reactive_assignments = false; has_reactive_assignments = false;
injected_reactive_declaration_vars: Set<string> = new Set(); injected_reactive_declaration_vars: Set<string> = new Set();
helpers: Map<string, string> = new Map(); helpers: Map<string, Identifier> = new Map();
globals: Map<string, string> = new Map(); globals: Map<string, Identifier> = new Map();
indirect_dependencies: Map<string, Set<string>> = new Map(); indirect_dependencies: Map<string, Set<string>> = new Map();
@ -141,7 +141,7 @@ export default class Component {
stylesheet: Stylesheet; stylesheet: Stylesheet;
aliases: Map<string, string> = new Map(); aliases: Map<string, Identifier> = new Map();
used_names: Set<string> = new Set(); used_names: Set<string> = new Set();
globally_used_names: Set<string> = new Set(); globally_used_names: Set<string> = new Set();
@ -156,7 +156,7 @@ export default class Component {
stats: Stats, stats: Stats,
warnings: Warning[] warnings: Warning[]
) { ) {
this.name = name; this.name = { type: 'Identifier', name };
this.stats = stats; this.stats = stats;
this.warnings = warnings; 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}/>`, 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 { } else {
this.tag = this.name; this.tag = this.name;
} }
@ -335,7 +338,7 @@ export default class Component {
const referenced_globals = Array.from( const referenced_globals = Array.from(
this.globals, this.globals,
([name, alias]) => name !== alias && { name, alias } ([name, alias]) => name !== alias.name && { name, alias }
).filter(Boolean); ).filter(Boolean);
if (referenced_globals.length) { if (referenced_globals.length) {
this.helper('globals'); this.helper('globals');
@ -348,7 +351,7 @@ export default class Component {
const module = create_module( const module = create_module(
printed.code, printed.code,
format, format,
name, name.name,
banner, banner,
compile_options.sveltePath, compile_options.sveltePath,
imported_helpers, 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}$`; if (test) name = `${name}$`;
let alias = name; let alias = name;
for ( for (
@ -449,7 +452,7 @@ export default class Component {
alias = `${name}_${i++}` alias = `${name}_${i++}`
); );
this.used_names.add(alias); this.used_names.add(alias);
return alias; return { type: 'Identifier', name: alias };
} }
get_unique_name_maker() { get_unique_name_maker() {
@ -463,7 +466,7 @@ export default class Component {
internal_exports.forEach(add); internal_exports.forEach(add);
this.var_lookup.forEach((_value, key) => add(key)); this.var_lookup.forEach((_value, key) => add(key));
return (name: string) => { return (name: string): Identifier => {
if (test) name = `${name}$`; if (test) name = `${name}$`;
let alias = name; let alias = name;
for ( for (
@ -473,7 +476,11 @@ export default class Component {
); );
local_used_names.add(alias); local_used_names.add(alias);
this.globally_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 deindent from './utils/deindent';
import list from '../utils/list'; import list from '../utils/list';
import { ModuleFormat, Node } from '../interfaces'; import { ModuleFormat, Node, Identifier } from '../interfaces';
import { stringify_props } from './utils/stringify_props'; import { stringify_props } from './utils/stringify_props';
const wrappers = { esm, cjs }; const wrappers = { esm, cjs };
@ -16,8 +16,8 @@ export default function create_module(
name: string, name: string,
banner: string, banner: string,
sveltePath = 'svelte', sveltePath = 'svelte',
helpers: Array<{ name: string; alias: string }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: string }>, globals: Array<{ name: string; alias: Identifier }>,
imports: Node[], imports: Node[],
module_exports: Export[], module_exports: Export[],
source: string source: string
@ -45,14 +45,14 @@ function esm(
banner: string, banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: string }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: string }>, globals: Array<{ name: string; alias: Identifier }>,
imports: Node[], imports: Node[],
module_exports: Export[], module_exports: Export[],
source: string source: string
) { ) {
const internal_imports = helpers.length > 0 && ( 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 internal_globals = globals.length > 0 && (
`const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};` `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, banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: string }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: string }>, globals: Array<{ name: string; alias: Identifier }>,
imports: Node[], imports: Node[],
module_exports: Export[] 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 internal_imports = helpers.length > 0 && (
`const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n` `const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n`

@ -4,13 +4,14 @@ import Component from '../Component';
import deindent from '../utils/deindent'; import deindent from '../utils/deindent';
import Block from '../render_dom/Block'; import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names'; import { sanitize } from '../../utils/names';
import { Identifier } from '../../interfaces';
export default class EventHandler extends Node { export default class EventHandler extends Node {
type: 'EventHandler'; type: 'EventHandler';
name: string; name: string;
modifiers: Set<string>; modifiers: Set<string>;
expression: Expression; expression: Expression;
handler_name: string; handler_name: Identifier;
uses_context = false; uses_context = false;
can_make_passive = false; can_make_passive = false;
@ -42,21 +43,21 @@ export default class EventHandler extends Node {
} }
} }
} else { } else {
const name = component.get_unique_name(`${sanitize(this.name)}_handler`); const id = component.get_unique_name(`${sanitize(this.name)}_handler`);
component.add_var({ component.add_var({
name, name: id.name,
internal: true, internal: true,
referenced: true referenced: true
}); });
component.partly_hoisted.push(deindent` component.partly_hoisted.push(deindent`
function ${name}(event) { function ${id}(event) {
@bubble($$self, 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 (map.has(node)) scope = scope.parent;
if (node === function_expression) { if (node === function_expression) {
const name = component.get_unique_name( const id = component.get_unique_name(
sanitize(get_function_name(node, owner)) 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 body = code.slice(node.body.start, node.body.end).trim();
const fn = node.type === 'FunctionExpression' const fn = node.type === 'FunctionExpression'
? `${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${name}(${args.join(', ')}) ${body}` ? `${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${id}(${args.join(', ')}) ${body}`
: `const ${name} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`; : `const ${id} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`;
if (dependencies.size === 0 && contextual_dependencies.size === 0) { if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely // we can hoist this out of the component completely
component.fully_hoisted.push(fn); component.fully_hoisted.push(fn);
code.overwrite(node.start, node.end, name); code.overwrite(node.start, node.end, id.name);
component.add_var({ component.add_var({
name, name: id.name,
internal: true, internal: true,
hoistable: true, hoistable: true,
referenced: true referenced: true
@ -344,10 +344,10 @@ export default class Expression {
else if (contextual_dependencies.size === 0) { else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init // function can be hoisted inside the component init
component.partly_hoisted.push(fn); component.partly_hoisted.push(fn);
code.overwrite(node.start, node.end, `ctx.${name}`); code.overwrite(node.start, node.end, `ctx.${id}`);
component.add_var({ component.add_var({
name, name: id.name,
internal: true, internal: true,
referenced: true referenced: true
}); });
@ -356,17 +356,17 @@ export default class Expression {
else { else {
// we need a combo block/init recipe // we need a combo block/init recipe
component.partly_hoisted.push(fn); component.partly_hoisted.push(fn);
code.overwrite(node.start, node.end, name); code.overwrite(node.start, node.end, id.name);
component.add_var({ component.add_var({
name, name: id.name,
internal: true, internal: true,
referenced: true referenced: true
}); });
declarations.push(b` declarations.push(b`
function ${name}(${original_params ? '...args' : ''}) { function ${id}(${original_params ? '...args' : ''}) {
return ctx.${name}(ctx${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 Wrapper from './wrappers/shared/Wrapper';
import { escape } from '../utils/stringify'; import { escape } from '../utils/stringify';
import { b, x } from 'code-red'; import { b, x } from 'code-red';
import { Node } from '../../interfaces'; import { Node, Identifier } from '../../interfaces';
export interface BlockOptions { export interface BlockOptions {
parent?: Block; parent?: Block;
name: string; name: Identifier;
type: string; type: string;
renderer?: Renderer; renderer?: Renderer;
comment?: string; comment?: string;
key?: string; key?: Identifier;
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;
}>;
dependencies?: Set<string>; dependencies?: Set<string>;
} }
export default class Block { export default class Block {
parent?: Block; parent?: Block;
renderer: Renderer; renderer: Renderer;
name: string; name: Identifier;
type: string; type: string;
comment?: string; comment?: string;
wrappers: Wrapper[]; wrappers: Wrapper[];
key: string; key: Identifier;
first: string; first: Identifier;
dependencies: Set<string>; 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: { chunks: {
init: Node[]; init: Node[];
@ -56,9 +68,9 @@ export default class Block {
has_outro_method: boolean; has_outro_method: boolean;
outros: number; outros: number;
aliases: Map<string, string>; aliases: Map<string, Identifier>;
variables: Map<string, string>; variables: Map<string, string>;
get_unique_name: (name: string) => string; get_unique_name: (name: string) => Identifier;
has_update_method = false; has_update_method = false;
autofocus: string; autofocus: string;
@ -108,6 +120,7 @@ export default class Block {
} }
assign_variable_names() { assign_variable_names() {
// TODO reimplement for #3539
const seen = new Set(); const seen = new Set();
const dupes = new Set(); const dupes = new Set();
@ -139,7 +152,7 @@ export default class Block {
counts.set(wrapper.var, i + 1); counts.set(wrapper.var, i + 1);
wrapper.var = this.get_unique_name(wrapper.var + i); wrapper.var = this.get_unique_name(wrapper.var + i);
} else { } 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( add_element(
name: string, name: string,
render_statement: string, render_statement: Node,
claim_statement: string, claim_statement: Node,
parent_node: string, parent_node: string,
no_detach?: boolean no_detach?: boolean
) { ) {
@ -192,7 +205,7 @@ export default class Block {
add_variable(name: string, init?: string) { add_variable(name: string, init?: string) {
if (name[0] === '#') { 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) { if (this.variables.has(name) && this.variables.get(name) !== init) {
@ -281,7 +294,7 @@ export default class Block {
properties.mount = noop; properties.mount = noop;
} else { } else {
properties.mount = x`function mount(#target, anchor) { 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}, key: ${properties.key},
first: ${properties.first}, first: ${properties.first},
c: ${properties.create}, c: ${properties.create},
@ -354,13 +367,17 @@ export default class Block {
d: ${properties.destroy} d: ${properties.destroy}
}`; }`;
return_value.properties = return_value.properties.filter(prop => prop.value);
/* eslint-disable @typescript-eslint/indent,indent */ /* eslint-disable @typescript-eslint/indent,indent */
return b` 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 id_node = { type: 'Identifier', name: id };
const init_node = { type: 'Identifier', name: init }; 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} ${this.chunks.init}

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

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

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

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

@ -152,7 +152,7 @@ export default class BindingWrapper {
{ {
// this is necessary to prevent audio restarting by itself // this is necessary to prevent audio restarting by itself
const last = block.get_unique_name(`${parent.var}_is_paused`); 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_conditions.push(`${last} !== (${last} = ${this.snippet})`);
update_dom = b`${parent.var}[${last} ? "pause" : "play"]();`; update_dom = b`${parent.var}[${last} ? "pause" : "play"]();`;
@ -272,7 +272,7 @@ function get_event_handler(
mutation: store mutation: store
? mutate_store(store, value, tail) ? mutate_store(store, value, tail)
: `${snippet}${tail} = ${value};`, : `${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 Block from '../../Block';
import { is_void, quote_prop_if_necessary, quote_name_if_necessary, sanitize } from '../../../../utils/names'; import { is_void, quote_prop_if_necessary, quote_name_if_necessary, sanitize } from '../../../../utils/names';
import FragmentWrapper from '../Fragment'; 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 TextWrapper from '../Text';
import fix_attribute_casing from './fix_attribute_casing'; import fix_attribute_casing from './fix_attribute_casing';
import { b, x } from 'code-red'; import { b, x } from 'code-red';
@ -114,7 +114,7 @@ export default class ElementWrapper extends Wrapper {
slot_block: Block; slot_block: Block;
select_binding_dependencies?: Set<string>; select_binding_dependencies?: Set<string>;
var: string; var: any;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -125,7 +125,10 @@ export default class ElementWrapper extends Wrapper {
next_sibling: Wrapper next_sibling: Wrapper
) { ) {
super(renderer, block, parent, node); 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 = []; 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') { if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.chunks.create.push( block.chunks.create.push(
// @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead? // @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 { } else {
const inner_html = escape( const inner_html = escape(
@ -306,7 +309,7 @@ export default class ElementWrapper extends Wrapper {
child.render( child.render(
block, block,
this.node.name === 'template' ? `${node}.content` : node, 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; const { name, namespace } = this.node;
if (namespace === 'http://www.w3.org/2000/svg') { if (namespace === 'http://www.w3.org/2000/svg') {
return `@svg_element("${name}")`; return x`@svg_element("${name}")`;
} }
if (namespace) { if (namespace) {
return `@_document.createElementNS("${namespace}", "${name}")`; return x`@_document.createElementNS("${namespace}", "${name}")`;
} }
const is = this.attributes.find(attr => attr.node.name === 'is'); const is = this.attributes.find(attr => attr.node.name === 'is');
if (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) { get_claim_statement(nodes: string) {
@ -402,9 +405,9 @@ export default class ElementWrapper extends Wrapper {
? this.node.name ? this.node.name
: this.node.name.toUpperCase(); : this.node.name.toUpperCase();
return `@claim_element(${nodes}, "${name}", ${attributes return x`@claim_element(${nodes}, "${name}", ${attributes
? `{ ${attributes} }` ? x`{ ${attributes} }`
: `{}`}, ${this.node.namespace === namespaces.svg ? true : false})`; : x`{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
} }
add_bindings(block: Block) { add_bindings(block: Block) {
@ -418,7 +421,7 @@ export default class ElementWrapper extends Wrapper {
block.get_unique_name(`${this.var}_updating`) : block.get_unique_name(`${this.var}_updating`) :
null; null;
if (lock) block.add_variable(lock, 'false'); if (lock) block.add_variable(lock.name, 'false');
const groups = events const groups = events
.map(event => ({ .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`); const handler = renderer.component.get_unique_name(`${this.var}_${group.events.join('_')}_handler`);
renderer.component.add_var({ renderer.component.add_var({
name: handler, name: handler.name,
internal: true, internal: true,
referenced: 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.node.expression.contextual_dependencies);
add_to_set(contextual_dependencies, binding.handler.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 // media bindings — awkward special case. The native timeupdate events
@ -505,7 +508,7 @@ export default class ElementWrapper extends Wrapper {
if (name === 'resize') { if (name === 'resize') {
// special case // special case
const resize_listener = block.get_unique_name(`${this.var}_resize_listener`); 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( block.chunks.mount.push(
b`${resize_listener} = @add_resize_listener(${this.var}, ${callee}.bind(${this.var}));` 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) ? intro.expression.render(block)
: '{}'; : '{}';
block.add_variable(name); block.add_variable(name.name);
const fn = component.qualify(intro.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`); const outro_name = outro && block.get_unique_name(`${this.var}_outro`);
if (intro) { if (intro) {
block.add_variable(intro_name); block.add_variable(intro_name.name);
const snippet = intro.expression const snippet = intro.expression
? intro.expression.render(block) ? intro.expression.render(block)
: '{}'; : '{}';
@ -737,7 +740,7 @@ export default class ElementWrapper extends Wrapper {
} }
if (outro) { if (outro) {
block.add_variable(outro_name); block.add_variable(outro_name.name);
const snippet = outro.expression const snippet = outro.expression
? outro.expression.render(block) ? outro.expression.render(block)
: '{}'; : '{}';
@ -780,8 +783,8 @@ export default class ElementWrapper extends Wrapper {
const rect = block.get_unique_name('rect'); const rect = block.get_unique_name('rect');
const stop_animation = block.get_unique_name('stop_animation'); const stop_animation = block.get_unique_name('stop_animation');
block.add_variable(rect); block.add_variable(rect.name);
block.add_variable(stop_animation, '@noop'); block.add_variable(stop_animation.name, '@noop');
block.chunks.measure.push(b` block.chunks.measure.push(b`
${rect} = ${this.var}.getBoundingClientRect(); ${rect} = ${this.var}.getBoundingClientRect();

@ -6,8 +6,10 @@ import IfBlock from '../../nodes/IfBlock';
import create_debugging_comment from './shared/create_debugging_comment'; import create_debugging_comment from './shared/create_debugging_comment';
import ElseBlock from '../../nodes/ElseBlock'; import ElseBlock from '../../nodes/ElseBlock';
import FragmentWrapper from './Fragment'; import FragmentWrapper from './Fragment';
import { b } from 'code-red'; import { b, x } from 'code-red';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { Identifier } from '../../../interfaces';
import Node from '../../nodes/shared/Node';
function is_else_if(node: ElseBlock) { function is_else_if(node: ElseBlock) {
return ( return (
@ -19,8 +21,8 @@ class IfBlockBranch extends Wrapper {
block: Block; block: Block;
fragment: FragmentWrapper; fragment: FragmentWrapper;
dependencies?: string[]; dependencies?: string[];
condition?: string; condition?: any;
snippet?: string; snippet?: Node;
is_dynamic: boolean; is_dynamic: boolean;
var = null; var = null;
@ -54,9 +56,9 @@ class IfBlockBranch extends Wrapper {
if (should_cache) { if (should_cache) {
this.condition = block.get_unique_name(`show_if`); this.condition = block.get_unique_name(`show_if`);
this.snippet = expression.render(block); this.snippet = expression.node;
} else { } else {
this.condition = expression.render(block); this.condition = expression.node;
} }
} }
@ -79,7 +81,7 @@ export default class IfBlockWrapper extends Wrapper {
branches: IfBlockBranch[]; branches: IfBlockBranch[];
needs_update = false; needs_update = false;
var = 'if_block'; var: Identifier = { type: 'Identifier', name: 'if_block' };
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -224,9 +226,9 @@ export default class IfBlockWrapper extends Wrapper {
if (needs_anchor) { if (needs_anchor) {
block.add_element( block.add_element(
anchor, (anchor as Identifier).name,
`@empty()`, x`@empty()`,
parent_nodes && `@empty()`, parent_nodes && x`@empty()`,
parent_node parent_node
); );
} }
@ -340,7 +342,7 @@ export default class IfBlockWrapper extends Wrapper {
? '' ? ''
: `if (~${current_block_type_index}) `; : `if (~${current_block_type_index}) `;
block.add_variable(current_block_type_index); block.add_variable(current_block_type_index.name);
block.add_variable(name); block.add_variable(name);
/* eslint-disable @typescript-eslint/indent,indent */ /* eslint-disable @typescript-eslint/indent,indent */
@ -470,7 +472,7 @@ export default class IfBlockWrapper extends Wrapper {
) { ) {
const branch = this.branches[0]; 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` block.chunks.init.push(b`
var ${name} = (${branch.condition}) && ${branch.block.name}(ctx); 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 { quote_name_if_necessary, quote_prop_if_necessary, sanitize } from '../../../../utils/names';
import { stringify_props } from '../../../utils/stringify_props'; import { stringify_props } from '../../../utils/stringify_props';
import add_to_set from '../../../utils/add_to_set'; 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 Attribute from '../../../nodes/Attribute';
import get_object from '../../../utils/get_object'; import get_object from '../../../utils/get_object';
import create_debugging_comment from '../shared/create_debugging_comment'; 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 TemplateScope from '../../../nodes/shared/TemplateScope';
import is_dynamic from '../shared/is_dynamic'; import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this'; import bind_this from '../shared/bind_this';
import { Identifier } from '../../../../interfaces';
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: string; var: Identifier;
slots: Map<string, { block: Block; scope: TemplateScope; fn?: string }> = new Map(); slots: Map<string, { block: Block; scope: TemplateScope; fn?: string }> = new Map();
node: InlineComponent; node: InlineComponent;
fragment: FragmentWrapper; fragment: FragmentWrapper;
@ -61,11 +62,14 @@ export default class InlineComponentWrapper extends Wrapper {
} }
}); });
this.var = ( this.var = {
this.node.name === 'svelte:self' ? renderer.component.name : type: 'Identifier',
this.node.name === 'svelte:component' ? 'switch_instance' : name: (
sanitize(this.node.name) this.node.name === 'svelte:self' ? renderer.component.name.name :
).toLowerCase(); this.node.name === 'svelte:component' ? 'switch_instance' :
sanitize(this.node.name)
).toLowerCase()
};
if (this.node.children.length) { if (this.node.children.length) {
const default_slot = block.child({ const default_slot = block.child({
@ -253,19 +257,19 @@ export default class InlineComponentWrapper extends Wrapper {
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (binding.name === 'this') { 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({ component.add_var({
name, name: id.name,
internal: true, internal: true,
referenced: true referenced: true
}); });
const updating = block.get_unique_name(`updating_${binding.name}`); const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating); block.add_variable(updating.name);
const snippet = binding.expression.render(block); const snippet = binding.expression.render(block);
@ -292,13 +296,13 @@ export default class InlineComponentWrapper extends Wrapper {
const { name } = binding.expression.node; const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name); const { object, property, snippet } = block.bindings.get(name);
lhs = snippet; lhs = snippet;
contextual_dependencies.push(object, property); contextual_dependencies.push(object.name, property.name);
} }
const value = block.get_unique_name('value'); const value = block.get_unique_name('value');
const args = [value]; const args: any[] = [value];
if (contextual_dependencies.length > 0) { if (contextual_dependencies.length > 0) {
args.push(`{ ${contextual_dependencies.join(', ')} }`); args.push(x`{ ${contextual_dependencies.join(', ')} }`);
block.chunks.init.push(b` block.chunks.init.push(b`
function ${name}(${value}) { function ${name}(${value}) {
@ -311,7 +315,7 @@ export default class InlineComponentWrapper extends Wrapper {
block.maintain_context = true; // TODO put this somewhere more logical block.maintain_context = true; // TODO put this somewhere more logical
} else { } else {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${name}(${value}) { function ${id}(${value}) {
ctx.${name}.call(null, ${value}); ctx.${name}.call(null, ${value});
${updating} = true; ${updating} = true;
@add_flush_callback(() => ${updating} = false); @add_flush_callback(() => ${updating} = false);
@ -320,7 +324,7 @@ export default class InlineComponentWrapper extends Wrapper {
} }
const body = b` const body = b`
function ${name}(${args.join(', ')}) { function ${id}(${args.join(', ')}) {
${lhs} = ${value}; ${lhs} = ${value};
${component.invalidate(dependencies[0])}; ${component.invalidate(dependencies[0])};
} }
@ -328,7 +332,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.partly_hoisted.push(body); 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 => { const munged_handlers = this.node.handlers.map(handler => {

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

@ -1,13 +1,14 @@
import { b } from 'code-red'; import { b, x } from 'code-red';
import Renderer from '../Renderer'; import Renderer from '../Renderer';
import Block from '../Block'; import Block from '../Block';
import Tag from './shared/Tag'; import Tag from './shared/Tag';
import Wrapper from './shared/Wrapper'; import Wrapper from './shared/Wrapper';
import MustacheTag from '../../nodes/MustacheTag'; import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag'; import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Identifier } from '../../../interfaces';
export default class RawMustacheTagWrapper extends Tag { export default class RawMustacheTagWrapper extends Tag {
var = 'raw'; var: Identifier = { type: 'Identifier', name: 'raw' };
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -41,7 +42,7 @@ export default class RawMustacheTagWrapper extends Tag {
const html_tag = block.get_unique_name('html_tag'); const html_tag = block.get_unique_name('html_tag');
const html_anchor = needs_anchor && block.get_unique_name('html_anchor'); 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( const { init } = this.rename_this_method(
block, 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'});`); block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}${parent_node ? '' : ', anchor'});`);
if (needs_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) { 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 { stringify_props } from '../../utils/stringify_props';
import Expression from '../../nodes/shared/Expression'; import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic'; import is_dynamic from './shared/is_dynamic';
import { Identifier } from '../../../interfaces';
export default class SlotWrapper extends Wrapper { export default class SlotWrapper extends Wrapper {
node: Slot; node: Slot;
fragment: FragmentWrapper; fragment: FragmentWrapper;
var = 'slot'; var: Identifier = { type: 'Identifier', name: 'slot' };
dependencies: Set<string> = new Set(['$$scope']); dependencies: Set<string> = new Set(['$$scope']);
constructor( constructor(

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

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

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

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

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

@ -2,6 +2,13 @@ export function stringify(data: string, options = {}) {
return JSON.stringify(escape(data, 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 } = {}) { export function escape(data: string, { only_escape_at_symbol = false } = {}) {
return data.replace(only_escape_at_symbol ? /@+/g : /(@+|#+)/g, (match: string) => { return data.replace(only_escape_at_symbol ? /@+/g : /(@+|#+)/g, (match: string) => {
return match + match[0]; return match + match[0];

@ -1,3 +1,8 @@
export interface Identifier {
type: 'Identifier';
name: string;
}
interface BaseNode { interface BaseNode {
start: number; start: number;
end: 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