pull/3539/head
Richard Harris 6 years ago
parent d731766f34
commit 605c5d271c

@ -299,6 +299,11 @@ export default class Component {
walk(program, {
enter: (node) => {
if (node.type === 'Identifier' && !('name' in node)) {
console.log(node);
throw new Error('wtf');
}
if (node.type === 'Identifier' && node.name[0] === '@') {
// TODO temp
if (!/@\w+$/.test(node.name)) {

@ -202,11 +202,6 @@ export default class Block {
}
add_variable(id: Identifier, init?: Node) {
if (id.name[0] === '#') {
// TODO is this still necessary?
id.name = this.alias(id.name.slice(1)).name;
}
this.variables.forEach(v => {
if (v.id.name === id.name) {
throw new Error(
@ -371,8 +366,7 @@ export default class Block {
// TODO should code-red do this automatically? probably
return_value.properties = return_value.properties.filter(prop => prop.value);
/* eslint-disable @typescript-eslint/indent,indent */
return b`
const body = b`
${Array.from(this.variables.values()).map(({ id, init }) => {
return init
? b`let ${id} = ${init}`
@ -390,7 +384,8 @@ export default class Block {
return ${return_value};`
}
`;
/* eslint-enable @typescript-eslint/indent,indent */
return body;
}
render() {

@ -4,7 +4,6 @@ import Component from '../Component';
import Renderer from './Renderer';
import { CompileOptions } from '../../interfaces';
import { walk } from 'estree-walker';
import { stringify_props } from '../utils/stringify_props';
import add_to_set from '../utils/add_to_set';
import { extract_names } from '../utils/scope';
import { invalidate } from '../utils/invalidate';
@ -372,6 +371,19 @@ export default function dom(
`;
}
const return_value = {
type: 'ObjectExpression',
properties: filtered_declarations.map(name => {
const id = { type: 'Identifier', name };
return {
type: 'Property',
key: id,
value: id,
kind: 'init'
};
})
};
body.push(b`
function ${definition}(${args.join(', ')}) {
${reactive_store_declarations.length > 0 && `let ${reactive_store_declarations.join(', ')};`}
@ -406,7 +418,7 @@ export default function dom(
${fixed_reactive_declarations}
return ${stringify_props(filtered_declarations)};
return ${return_value};
}
`);
}
@ -476,9 +488,9 @@ export default function dom(
class ${name} extends ${superclass} {
constructor(options) {
super(${options.dev && `options`});
${should_add_css && `if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
@init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names});
${options.dev && `@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name}", options, id: create_fragment.name });`}
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name}", options, id: create_fragment.name });`}
${dev_props_check}
}

@ -2,7 +2,7 @@ import Attribute from '../../../nodes/Attribute';
import Block from '../../Block';
import fix_attribute_casing from './fix_attribute_casing';
import ElementWrapper from './index';
import { stringify } from '../../../utils/stringify';
import { string_literal } from '../../../utils/stringify';
import { b, x } from 'code-red';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
@ -172,8 +172,8 @@ export default class AttributeWrapper {
is_legacy_input_type
? b`@set_input_type(${element.var}, ${value});`
: property_name
? b`${element.var}.${property_name} = ${value};`
: b`${method}(${element.var}, "${name}", ${value === true ? '""' : value});`
? b`${element.var}.${property_name} = ${value === true ? { type: 'Literal', value: true } : value};`
: b`${method}(${element.var}, "${name}", ${value === true ? x`""` : value});`
);
block.chunks.hydrate.push(statement);
@ -207,7 +207,7 @@ export default class AttributeWrapper {
render_chunks() {
return this.node.chunks.map((chunk) => {
if (chunk.type === 'Text') {
return stringify(chunk.data);
return string_literal(chunk.data);
}
return chunk.manipulate();

@ -8,12 +8,7 @@ import Renderer from '../../Renderer';
import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { Node as INode, Identifier } from '../../../../interfaces';
function get_tail(node: INode) {
const end = node.end;
while (node.type === 'MemberExpression') node = node.object;
return { start: node.end, end };
}
import { changed } from '../shared/changed';
export default class BindingWrapper {
node: Binding;
@ -22,9 +17,9 @@ export default class BindingWrapper {
object: string;
handler: {
uses_context: boolean;
mutation: string;
mutation: Node;
contextual_dependencies: Set<string>;
snippet?: string;
snippet?: Node;
};
snippet: INode;
is_readonly: boolean;
@ -59,7 +54,7 @@ export default class BindingWrapper {
// TODO unfortunate code is necessary because we need to use `ctx`
// inside the fragment, but not inside the <script>
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
const contextless_snippet = JSON.parse(JSON.stringify(this.node.expression.node));
// view to model
this.handler = get_event_handler(this, parent.renderer, block, this.object, contextless_snippet);
@ -95,23 +90,19 @@ export default class BindingWrapper {
const { parent } = this;
const update_conditions: string[] = this.needs_lock ? [`!${lock}`] : [];
const update_conditions: any[] = this.needs_lock ? [x`!${lock}`] : [];
const dependency_array = [...this.node.expression.dependencies];
if (dependency_array.length === 1) {
update_conditions.push(`changed.${dependency_array[0]}`);
} else if (dependency_array.length > 1) {
update_conditions.push(
`(${dependency_array.map(prop => `changed.${prop}`).join(' || ')})`
);
if (dependency_array.length > 1) {
update_conditions.push(changed(dependency_array));
}
if (parent.node.name === 'input') {
const type = parent.node.get_static_attribute_value('type');
if (type === null || type === "" || type === "text" || type === "email" || type === "password") {
update_conditions.push(`(${parent.var}.${this.node.name} !== ${this.snippet})`);
update_conditions.push(x`(${parent.var}.${this.node.name} !== ${this.snippet})`);
}
}
@ -135,17 +126,17 @@ export default class BindingWrapper {
}
case 'textContent':
update_conditions.push(`${this.snippet} !== ${parent.var}.textContent`);
update_conditions.push(x`${this.snippet} !== ${parent.var}.textContent`);
break;
case 'innerHTML':
update_conditions.push(`${this.snippet} !== ${parent.var}.innerHTML`);
update_conditions.push(x`${this.snippet} !== ${parent.var}.innerHTML`);
break;
case 'currentTime':
case 'playbackRate':
case 'volume':
update_conditions.push(`!@_isNaN(${this.snippet})`);
update_conditions.push(x`!@_isNaN(${this.snippet})`);
break;
case 'paused':
@ -154,7 +145,7 @@ export default class BindingWrapper {
const last = block.get_unique_name(`${parent.var}_is_paused`);
block.add_variable(last, x`true`);
update_conditions.push(`${last} !== (${last} = ${this.snippet})`);
update_conditions.push(x`${last} !== (${last} = ${this.snippet})`);
update_dom = b`${parent.var}[${last} ? "pause" : "play"]();`;
break;
}
@ -173,16 +164,12 @@ export default class BindingWrapper {
}`
: update_dom
);
console.log('update_dom', JSON.stringify(block.chunks.update, null, ' '));
}
if (this.node.name === 'innerHTML' || this.node.name === 'textContent') {
block.chunks.mount.push(b`if (${this.snippet} !== void 0) ${update_dom}`);
} else if (!/(currentTime|paused)/.test(this.node.name)) {
block.chunks.mount.push(update_dom);
console.log('update_dom', JSON.stringify(block.chunks.mount, null, ' '));
}
}
}
@ -211,10 +198,10 @@ function get_dom_updater(
const type = node.get_static_attribute_value('type');
const condition = type === 'checkbox'
? b`~${binding.snippet}.indexOf(${element.var}.__value)`
: b`${element.var}.__value === ${binding.snippet}`;
? x`~${binding.snippet}.indexOf(${element.var}.__value)`
: x`${element.var}.__value === ${binding.snippet}`;
return `${element.var}.checked = ${condition};`;
return b`${element.var}.checked = ${condition};`;
}
if (binding.node.name === 'value') {
@ -250,20 +237,21 @@ function get_event_handler(
renderer: Renderer,
block: Block,
name: string,
snippet: string
snippet: Node
): {
uses_context: boolean;
mutation: string;
mutation: Node;
contextual_dependencies: Set<string>;
snippet?: string;
snippet?: Node;
} {
const value = get_value_from_dom(renderer, binding.parent, binding);
let store = binding.object[0] === '$' ? binding.object.slice(1) : null;
let tail = '';
let tail = null;
if (binding.node.expression.node.type === 'MemberExpression') {
const { start, end } = get_tail(binding.node.expression.node);
tail = renderer.component.source.slice(start, end);
// const { start, end } = get_tail(binding.node.expression.node);
// tail = renderer.component.source.slice(start, end);
tail = binding.node.expression.node.object;
}
if (binding.node.is_contextual) {
@ -279,14 +267,14 @@ function get_event_handler(
uses_context: true,
mutation: store
? mutate_store(store, value, tail)
: `${snippet}${tail} = ${value};`,
: b`${snippet}${tail} = ${value};`,
contextual_dependencies: new Set([object.name, property.name])
};
}
const mutation = store
? mutate_store(store, value, tail)
: `${snippet} = ${value};`;
: b`${snippet} = ${value};`;
if (binding.node.expression.node.type === 'MemberExpression') {
return {
@ -313,14 +301,14 @@ function get_value_from_dom(
const { name } = binding.node;
if (name === 'this') {
return `$$node`;
return x`$$node`;
}
// <select bind:value='selected>
if (node.name === 'select') {
return node.get_static_attribute_value('multiple') === true ?
`@select_multiple_value(this)` :
`@select_value(this)`;
x`@select_multiple_value(this)` :
x`@select_value(this)`;
}
const type = node.get_static_attribute_value('type');
@ -329,21 +317,21 @@ function get_value_from_dom(
if (name === 'group') {
const binding_group = get_binding_group(renderer, binding.node.expression.node);
if (type === 'checkbox') {
return `@get_binding_group_value($$binding_groups[${binding_group}])`;
return x`@get_binding_group_value($$binding_groups[${binding_group}])`;
}
return `this.__value`;
return x`this.__value`;
}
// <input type='range|number' bind:value>
if (type === 'range' || type === 'number') {
return `@to_number(this.${name})`;
return x`@to_number(this.${name})`;
}
if ((name === 'buffered' || name === 'seekable' || name === 'played')) {
return `@time_ranges_to_array(this.${name})`;
return x`@time_ranges_to_array(this.${name})`;
}
// everything else
return `this.${name}`;
return x`this.${name}`;
}

@ -20,6 +20,7 @@ import add_actions from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_context_merger } from '../shared/get_context_merger';
import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed';
const events = [
{
@ -413,7 +414,7 @@ export default class ElementWrapper extends Wrapper {
.filter(group => group.bindings.length);
groups.forEach(group => {
const handler = renderer.component.get_unique_name(`${this.var}_${group.events.join('_')}_handler`);
const handler = renderer.component.get_unique_name(`${this.var.name}_${group.events.join('_')}_handler`);
renderer.component.add_var({
name: handler.name,
@ -480,7 +481,7 @@ export default class ElementWrapper extends Wrapper {
this.renderer.component.partly_hoisted.push(b`
function ${handler}(${contextual_dependencies.size > 0 ? `{ ${Array.from(contextual_dependencies).join(', ')} }` : ``}) {
${group.bindings.map(b => b.handler.mutation)}
${Array.from(dependencies).filter(dep => dep[0] !== '$').map(dep => `${this.renderer.component.invalidate(dep)};`)}
${Array.from(dependencies).filter(dep => dep[0] !== '$').map(dep => b`${this.renderer.component.invalidate(dep)};`)}
}
`);
@ -567,8 +568,8 @@ export default class ElementWrapper extends Wrapper {
}
add_spread_attributes(block: Block) {
const levels = block.get_unique_name(`${this.var}_levels`);
const data = block.get_unique_name(`${this.var}_data`);
const levels = block.get_unique_name(`${this.var.name}_levels`);
const data = block.get_unique_name(`${this.var.name}_data`);
const initial_props = [];
const updates = [];
@ -577,7 +578,7 @@ export default class ElementWrapper extends Wrapper {
.filter(attr => attr.type === 'Attribute' || attr.type === 'Spread')
.forEach(attr => {
const condition = attr.dependencies.size > 0
? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
? changed(Array.from(attr.dependencies))
: null;
if (attr.is_spread) {
@ -585,19 +586,17 @@ export default class ElementWrapper extends Wrapper {
initial_props.push(snippet);
updates.push(condition ? `${condition} && ${snippet}` : snippet);
updates.push(condition ? x`${condition} && ${snippet}` : snippet);
} else {
const snippet = `{ ${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)} }`;
const snippet = x`{ ${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)} }`;
initial_props.push(snippet);
updates.push(condition ? `${condition} && ${snippet}` : snippet);
updates.push(condition ? x`${condition} && ${snippet}` : snippet);
}
});
block.chunks.init.push(b`
var ${levels} = [
${initial_props.join(',\n')}
];
var ${levels} = [${initial_props}];
var ${data} = {};
for (var #i = 0; #i < ${levels}.length; #i += 1) {
@ -613,7 +612,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.update.push(b`
${fn}(${this.var}, @get_spread_update(${levels}, [
${updates.join(',\n')}
${updates}
]));
`);
}
@ -811,7 +810,10 @@ export default class ElementWrapper extends Wrapper {
const deps = all_dependencies.map(dependency => `changed${quote_prop_if_necessary(dependency)}`).join(' || ');
const condition = all_dependencies.length > 1 ? `(${deps})` : deps;
block.chunks.update.push(b`if (${condition}) ${updater}`);
block.chunks.update.push(b`
if (${condition}) {
${updater}
}`);
}
});
}

@ -4,7 +4,6 @@ import Block from '../../Block';
import InlineComponent from '../../../nodes/InlineComponent';
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, x } from 'code-red';
import Attribute from '../../../nodes/Attribute';
@ -114,7 +113,7 @@ export default class InlineComponentWrapper extends Wrapper {
const name = this.var;
const component_opts = [];
const component_opts: any = x`{}`;
const statements: string[] = [];
const updates: string[] = [];
@ -126,22 +125,56 @@ export default class InlineComponentWrapper extends Wrapper {
const slot_props = Array.from(this.slots).map(([name, slot]) => `${quote_name_if_necessary(name)}: [${slot.block.name}${slot.fn ? `, ${slot.fn}` : ''}]`);
const initial_props = slot_props.length > 0
? [`$$slots: ${stringify_props(slot_props)}`, `$$scope: { ctx }`]
: [];
let attribute_object;
const attribute_object = uses_spread
? stringify_props(initial_props)
: stringify_props(
this.node.attributes.map(attr => `${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)}`).concat(initial_props)
);
if (this.node.attributes.length || this.node.bindings.length || initial_props.length) {
if (this.node.attributes.length > 0 || this.node.bindings.length > 0 || slot_props.length > 0) {
if (!uses_spread && this.node.bindings.length === 0) {
component_opts.push(`props: ${attribute_object}`);
const initial_props: any = x`{}`;
if (slot_props.length > 0) {
initial_props.properties.push({
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: '$$slots' },
value: slot_props
}, {
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: '$$scope' },
value: x`{ ctx: #ctx }`
})
}
if (uses_spread) {
attribute_object = initial_props;
} else {
attribute_object = {
type: 'ObjectExpression',
properties: this.node.attributes.map(attr => {
return {
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: attr.name },
value: attr.get_value(block)
}
}).concat(initial_props.properties)
};
}
component_opts.properties.push({
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: 'props' },
value: attribute_object
});
} else {
component_opts.properties.push({
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: 'props' },
value: props
});
props = block.get_unique_name(`${name}_props`);
component_opts.push(`props: ${props}`);
}
}
@ -158,7 +191,12 @@ export default class InlineComponentWrapper extends Wrapper {
// will complain that options.target is missing. This would
// work better if components had separate public and private
// APIs
component_opts.push(`$$inline: true`);
component_opts.properties.push({
type: 'Property',
kind: 'init',
key: { type: 'Identifier', name: '$$inline' },
value: { type: 'Literal', value: true }
});
}
const fragment_dependencies = new Set(this.fragment ? ['$$scope'] : []);
@ -176,7 +214,7 @@ export default class InlineComponentWrapper extends Wrapper {
const dynamic_attributes = this.node.attributes.filter(a => a.get_dependencies().length > 0);
if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || non_let_dependencies.length > 0)) {
updates.push(`var ${name_changes} = {};`);
updates.push(`const ${name_changes} = {};`);
}
if (this.node.attributes.length) {
@ -217,13 +255,13 @@ export default class InlineComponentWrapper extends Wrapper {
});
block.chunks.init.push(b`
var ${levels} = [
const ${levels} = [
${initial_props.join(',\n')}
];
`);
statements.push(b`
for (var #i = 0; #i < ${levels}.length; #i += 1) {
for (let #i = 0; #i < ${levels}.length; #i += 1) {
${props} = @assign(${props}, ${levels}[#i]);
}
`);
@ -231,7 +269,7 @@ export default class InlineComponentWrapper extends Wrapper {
const conditions = Array.from(all_dependencies).map(dep => `changed.${dep}`).join(' || ');
updates.push(b`
var ${name_changes} = ${conditions ? `(${conditions}) ? @get_spread_update(${levels}, [
const ${name_changes} = ${conditions ? `(${conditions}) ? @get_spread_update(${levels}, [
${changes.join(',\n')}
]) : {}` : '{}'};
`);
@ -355,7 +393,7 @@ export default class InlineComponentWrapper extends Wrapper {
${(this.node.attributes.length || this.node.bindings.length) && b`
${props && `let ${props} = ${attribute_object};`}`}
${statements}
return ${stringify_props(component_opts)};
return ${component_opts};
}
if (${switch_value}) {
@ -433,7 +471,7 @@ export default class InlineComponentWrapper extends Wrapper {
b`if (${name}) @transition_out(${name}.$$.fragment, #local);`
);
block.chunks.destroy.push(b`if (${name}) @destroy_component(${name}${parent_node ? '' : ', detaching'});`);
block.chunks.destroy.push(b`if (${name}) @destroy_component(${name}, ${parent_node ? null : 'detaching'});`);
} else {
const expression = this.node.name === 'svelte:self'
? '__svelte:self__' // TODO conflict-proof this
@ -441,9 +479,9 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.init.push(b`
${(this.node.attributes.length || this.node.bindings.length) && b`
${props && `let ${props} = ${attribute_object};`}`}
${props && b`let ${props} = ${attribute_object};`}`}
${statements}
var ${name} = new ${expression}(${stringify_props(component_opts)});
const ${name} = new ${expression}(${component_opts});
${munged_bindings}
${munged_handlers}
@ -473,7 +511,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
block.chunks.destroy.push(b`
@destroy_component(${name}${parent_node ? '' : ', detaching'});
@destroy_component(${name}, ${parent_node ? null : 'detaching'});
`);
block.chunks.outro.push(

@ -71,7 +71,7 @@ export default class TitleWrapper extends Wrapper {
block.chunks.init.push(
b`@_document.title = ${init};`
);
const updater = `@_document.title = ${this.node.should_cache ? last : value};`;
const updater = b`@_document.title = ${this.node.should_cache ? last : value};`;
if (all_dependencies.size) {
const dependencies = Array.from(all_dependencies);

Loading…
Cancel
Save