pull/3539/head
Richard Harris 6 years ago
parent 9d947292a1
commit 12e7b5ecf5

28
package-lock.json generated

@ -520,6 +520,12 @@
"source-map": "^0.7.3"
},
"dependencies": {
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
@ -1208,9 +1214,9 @@
"dev": true
},
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.7.0.tgz",
"integrity": "sha512-6BzTIhsjm2jnqLO+O23aaiDGr9jsrgwT1ObAIEebwvbJthCoF5T1MtybbCx540r2U5fsn/8IIv7ZWqMqTHuu6Q==",
"dev": true
},
"esutils": {
@ -3103,6 +3109,14 @@
"magic-string": "^0.25.2",
"resolve": "^1.11.0",
"rollup-pluginutils": "^2.8.1"
},
"dependencies": {
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
}
}
},
"rollup-plugin-json": {
@ -3170,6 +3184,14 @@
"dev": true,
"requires": {
"estree-walker": "^0.6.1"
},
"dependencies": {
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
}
}
},
"run-async": {

@ -69,7 +69,7 @@
"eslint": "^6.3.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-svelte3": "^2.7.3",
"estree-walker": "^0.6.1",
"estree-walker": "^0.7.0",
"is-reference": "^1.1.3",
"jsdom": "^15.1.1",
"kleur": "^3.0.3",

@ -810,11 +810,11 @@ export default class Component {
const variable = this.var_lookup.get(name);
if (variable && (variable.subscribable && variable.reassigned)) {
return `$$subscribe_${name}($$invalidate('${name}', ${value || name}))`;
return x`$$subscribe_${name}($$invalidate('${name}', ${value || name}))`;
}
if (name[0] === '$' && name[1] !== '$') {
return `${name.slice(1)}.set(${name})`;
return x`${name.slice(1)}.set(${name})`;
}
if (
@ -845,9 +845,7 @@ export default class Component {
});
});
return Array.from(deps)
.map(n => `$$invalidate('${n}', ${n})`)
.join(', ');
return Array.from(deps).map(n => x`$$invalidate('${n}', ${n})`);
}
rewrite_props(_get_insert: (variable: Var) => string) {

@ -4,14 +4,14 @@ import is_reference from 'is-reference';
import flatten_reference from '../../utils/flatten_reference';
import { create_scopes, Scope, extract_names } from '../../utils/scope';
import { Node } from '../../../interfaces';
import { globals } from '../../../utils/names';
import { globals, sanitize } from '../../../utils/names';
import Wrapper from '../../render_dom/wrappers/shared/Wrapper';
import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object';
import Block from '../../render_dom/Block';
import { INode } from '../interfaces';
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
import { x } from 'code-red';
import { x, b } from 'code-red';
import { invalidate } from '../../utils/invalidate';
const binary_operators: Record<string, number> = {
@ -228,6 +228,7 @@ export default class Expression {
declarations,
scope_map: map,
template_scope,
owner,
is_synthetic
} = this;
let scope = this.scope;
@ -237,11 +238,8 @@ export default class Expression {
let dependencies: Set<string>;
let contextual_dependencies: Set<string>;
// TODO this feels a lil messy
let return_value = this.node;
walk(this.node, {
enter(node: any, parent: any, key: string, index: number) {
const node = walk(this.node, {
enter(node: any, parent: any, key: string) {
// don't manipulate shorthand props twice
if (key === 'value' && parent.shorthand) return;
@ -267,18 +265,7 @@ export default class Expression {
component.add_reference(name); // TODO is this redundant/misplaced?
}
} else if (!is_synthetic && is_contextual(component, template_scope, name)) {
const replaced = x`ctx.${node}`;
if (parent) {
if (index !== null) {
// TODO can this happen?
parent[key][index] = replaced;
} else {
parent[key] = replaced;
}
} else {
return_value = replaced;
}
this.replace(x`#ctx.${node}`);
}
this.skip();
@ -297,78 +284,72 @@ export default class Expression {
}
},
leave(node: Node, _parent: Node) {
leave(node: Node) {
if (map.has(node)) scope = scope.parent;
if (node === function_expression) {
// const id = component.get_unique_name(
// sanitize(get_function_name(node, owner))
// );
// const args = contextual_dependencies.size > 0
// ? [`{ ${Array.from(contextual_dependencies).join(', ')} }`]
// : [];
// let original_params;
// if (node.params.length > 0) {
// original_params = code.slice(node.params[0].start, node.params[node.params.length - 1].end);
// args.push(original_params);
// }
// const body = code.slice(node.body.start, node.body.end).trim();
// const fn = node.type === 'FunctionExpression'
// ? b`${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${id}(${args.join(', ')}) ${body}`
// : b`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, id.name);
// component.add_var({
// name: id.name,
// internal: true,
// hoistable: true,
// referenced: true
// });
// }
// 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.${id}`);
// component.add_var({
// name: id.name,
// internal: true,
// referenced: true
// });
// }
// else {
// // we need a combo block/init recipe
// component.partly_hoisted.push(fn);
// code.overwrite(node.start, node.end, id.name);
// component.add_var({
// name: id.name,
// internal: true,
// referenced: true
// });
// declarations.push(b`
// function ${id}(${original_params ? '...args' : ''}) {
// return ctx.${id}(ctx${original_params ? ', ...args' : ''});
// }
// `);
// }
// if (parent && parent.method) {
// code.prependRight(node.start, ': ');
// }
const id = component.get_unique_name(
sanitize(get_function_name(node, owner))
);
const declaration = b`const ${id} = ${node}`;
if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(declaration);
node.name = id;
this.replace(id as any);
component.add_var({
name: id.name,
internal: true,
hoistable: true,
referenced: true
});
}
else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);
node.name = id;
this.replace(x`#ctx.${id}` as any);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
}
else {
// we need a combo block/init recipe
component.partly_hoisted.push(declaration);
node.name = id;
this.replace(id as any);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
if (node.params.length > 0) {
declarations.push(b`
function ${id}(...args) {
return #ctx.${id}(#ctx, ...args);
}
`);
} else {
declarations.push(b`
function ${id}() {
return #ctx.${id}(#ctx);
}
`);
}
}
function_expression = null;
dependencies = null;
@ -394,7 +375,7 @@ export default class Expression {
}
});
invalidate(component, scope, node, traced);
this.replace(invalidate(component, scope, node, traced));
}
}
});
@ -406,21 +387,21 @@ export default class Expression {
});
}
return return_value;
return node;
}
}
// function get_function_name(_node, parent) {
// if (parent.type === 'EventHandler') {
// return `${parent.name}_handler`;
// }
function get_function_name(_node, parent) {
if (parent.type === 'EventHandler') {
return `${parent.name}_handler`;
}
// if (parent.type === 'Action') {
// return `${parent.name}_function`;
// }
if (parent.type === 'Action') {
return `${parent.name}_function`;
}
// return 'func';
// }
return 'func';
}
function is_contextual(component: Component, scope: TemplateScope, name: string) {
if (name === '$$props') return true;

@ -114,7 +114,7 @@ export default class Block {
this.get_unique_name = this.renderer.component.get_unique_name_maker();
this.aliases = new Map().set('ctx', this.get_unique_name('ctx'));
this.aliases = new Map();
if (this.key) this.aliases.set('key', this.get_unique_name('key'));
}
@ -304,9 +304,9 @@ export default class Block {
if (this.chunks.update.length === 0 && !this.maintain_context) {
properties.update = noop;
} else {
const ctx = this.maintain_context ? x`new_ctx` : x`ctx`;
const ctx = this.maintain_context ? x`#new_ctx` : x`#ctx`;
properties.update = x`function update(#changed, ${ctx}) {
${this.maintain_context && b`ctx = ${ctx};`}
${this.maintain_context && b`#ctx = ${ctx};`}
${this.chunks.update}
}`;
}
@ -385,7 +385,7 @@ export default class Block {
${dev
? b`
const block = ${return_value};
@dispatch_dev("SvelteRegisterBlock", { block, id: ${this.name || 'create_fragment'}.name, type: "${this.type}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", ctx });
@dispatch_dev("SvelteRegisterBlock", { block, id: ${this.name || 'create_fragment'}.name, type: "${this.type}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", ctx: #ctx });
return block;`
: b`
return ${return_value};`
@ -398,7 +398,7 @@ export default class Block {
const key = this.key && { type: 'Identifier', name: this.get_unique_name('key') };
const id = { type: 'Identifier', name: this.name };
const args: any[] = [x`ctx`];
const args: any[] = [x`#ctx`];
if (key) args.unshift(key);

@ -368,7 +368,7 @@ export default function dom(
${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`}
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
${component.partly_hoisted}
${set && b`$$self.$set = ${set};`}

@ -7,7 +7,7 @@ import FragmentWrapper from './Fragment';
import { b, x } from 'code-red';
import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
import { Identifier } from '../../../interfaces';
import { Identifier, Node } from '../../../interfaces';
export class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
@ -315,7 +315,7 @@ export default class EachBlockWrapper extends Wrapper {
block: Block;
parent_node: string;
parent_nodes: string;
snippet: string;
snippet: Node;
initial_anchor_node: Identifier;
initial_mount_node: Identifier;
update_anchor_node: Identifier;
@ -424,7 +424,7 @@ export default class EachBlockWrapper extends Wrapper {
}: {
block: Block;
parent_nodes: string;
snippet: string;
snippet: Node;
initial_anchor_node: Identifier;
initial_mount_node: Identifier;
update_anchor_node: Identifier;

@ -7,7 +7,7 @@ import Node from '../../../nodes/shared/Node';
import Renderer from '../../Renderer';
import flatten_reference from '../../../utils/flatten_reference';
import EachBlock from '../../../nodes/EachBlock';
import { Node as INode } from '../../../../interfaces';
import { Node as INode, Identifier } from '../../../../interfaces';
function get_tail(node: INode) {
const end = node.end;
@ -26,7 +26,7 @@ export default class BindingWrapper {
contextual_dependencies: Set<string>;
snippet?: string;
};
snippet: string;
snippet: INode;
is_readonly: boolean;
needs_lock: boolean;
@ -90,7 +90,7 @@ export default class BindingWrapper {
return this.node.is_readonly_media_attribute();
}
render(block: Block, lock: string) {
render(block: Block, lock: Identifier) {
if (this.is_readonly) return;
const { parent } = this;
@ -167,14 +167,22 @@ export default class BindingWrapper {
if (update_dom) {
block.chunks.update.push(
update_conditions.length ? b`if (${update_conditions.join(' && ')}) ${update_dom}` : update_dom
update_conditions.length
? b`if (${update_conditions.join(' && ')}) {
${update_dom}
}`
: 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, ' '));
}
}
}

@ -453,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.name);
binding.render(block, lock);
});
// media bindings — awkward special case. The native timeupdate events

@ -205,7 +205,7 @@ export default class InlineComponentWrapper extends Wrapper {
let value_object = value;
if (attr.expression.node.type !== 'ObjectExpression') {
value_object = `@get_spread_object(${value})`;
value_object = x`@get_spread_object(${value})`;
}
changes.push(condition ? `${condition} && ${value_object}` : value_object);
} else {

@ -40,7 +40,7 @@ export default class Tag extends Wrapper {
? `(${changed_check}) && ${update_cached_value}`
: changed_check;
block.chunks.update.push(b`if (${condition}) ${update(content)}`);
block.chunks.update.push(b`if (${condition}) ${update(content as Node)}`);
}
return { init: content };

@ -2,6 +2,7 @@ import { b } from 'code-red';
import Block from '../../Block';
import Action from '../../../nodes/Action';
import Component from '../../../Component';
import { x } from 'code-red';
export default function add_actions(
component: Component,
@ -28,16 +29,24 @@ export default function add_actions(
const fn = component.qualify(action.name);
block.chunks.mount.push(
b`${id} = ${fn}.call(null, ${target}${snippet ? `, ${snippet}` : ''}) || {};`
b`${id} = ${fn}.call(null, ${target}, ${snippet}) || {};`
);
if (dependencies && dependencies.length > 0) {
let conditional = `typeof ${id}.update === 'function' && `;
const deps = dependencies.map(dependency => `changed.${dependency}`).join(' || ');
conditional += dependencies.length > 1 ? `(${deps})` : deps;
let condition = x`typeof ${id}.update === 'function'`;
// TODO can this case be handled more elegantly?
if (dependencies.length > 0) {
let changed = x`#changed.${dependencies[0]}`;
for (let i = 1; i < dependencies.length; i += 1) {
changed = x`${changed} || #changed.${dependencies[i]}`;
}
condition = x`${condition} && ${changed}`;
}
block.chunks.update.push(
b`if (${conditional}) ${id}.update.call(null, ${snippet});`
b`if (${condition}) ${id}.update.call(null, ${snippet});`
);
}

@ -1,67 +1,66 @@
import Component from '../Component';
import { Node } from '../../interfaces';
// import { nodes_match } from '../../utils/nodes_match';
import { nodes_match } from '../../utils/nodes_match';
import { Scope } from './scope';
import { x } from 'code-red';
export function invalidate(_component: Component, _scope: Scope, _node: Node, _names: Set<string>) {
// const [head, ...tail] = Array.from(names).filter(name => {
// const owner = scope.find_owner(name);
// if (owner && owner !== component.instance_scope) return false;
export function invalidate(component: Component, scope: Scope, node: Node, names: Set<string>) {
const [head, ...tail] = Array.from(names).filter(name => {
const owner = scope.find_owner(name);
if (owner && owner !== component.instance_scope) return false;
// const variable = component.var_lookup.get(name);
const variable = component.var_lookup.get(name);
// return variable && (
// !variable.hoistable &&
// !variable.global &&
// !variable.module &&
// (
// variable.referenced ||
// variable.is_reactive_dependency ||
// variable.export_name ||
// variable.name[0] === '$'
// )
// );
// });
return variable && (
!variable.hoistable &&
!variable.global &&
!variable.module &&
(
variable.referenced ||
variable.is_reactive_dependency ||
variable.export_name ||
variable.name[0] === '$'
)
);
});
// TODO
if (head) {
component.has_reactive_assignments = true;
// if (head) {
// component.has_reactive_assignments = true;
if (node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return component.invalidate(head);
} else {
// if (node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
// code.overwrite(node.start, node.end, component.invalidate(head));
// } else {
// let suffix = ')';
// TODO stores
// if (head[0] === '$') {
// code.prependRight(node.start, `${component.helper('set_store_value')}(${head.slice(1)}, `);
// } else {
// let prefix = `$$invalidate`;
// if (head[0] === '$') {
// code.prependRight(node.start, `${component.helper('set_store_value')}(${head.slice(1)}, `);
// } else {
// let prefix = `$$invalidate`;
// const variable = component.var_lookup.get(head);
// if (variable.subscribable && variable.reassigned) {
// prefix = `$$subscribe_${head}($$invalidate`;
// suffix += `)`;
// }
// const variable = component.var_lookup.get(head);
// if (variable.subscribable && variable.reassigned) {
// prefix = `$$subscribe_${head}($$invalidate`;
// suffix += `)`;
// }
// code.prependRight(node.start, `${prefix}('${head}', `);
// }
// code.prependRight(node.start, `${prefix}('${head}', `);
// }
const extra_args = tail.map(name => component.invalidate(name));
// const extra_args = tail.map(name => component.invalidate(name));
const pass_value = (
extra_args.length > 0 ||
(node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
(node.type === 'UpdateExpression' && !node.prefix)
);
// const pass_value = (
// extra_args.length > 0 ||
// (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
// (node.type === 'UpdateExpression' && !node.prefix)
// );
if (pass_value) {
extra_args.unshift(head);
}
// if (pass_value) {
// extra_args.unshift(head);
// }
return x`$$invalidate("${head}", ${node}, ${extra_args})`;
}
}
// suffix = `${extra_args.map(arg => `, ${arg}`).join('')}${suffix}`;
// code.appendLeft(node.end, suffix);
// }
// }
return node;
}

@ -5,12 +5,10 @@ const Parser = acorn.Parser;
export const parse = (source: string) => Parser.parse(source, {
sourceType: 'module',
// @ts-ignore TODO pending release of fixed types
ecmaVersion: 11,
preserveParens: true
ecmaVersion: 11
});
export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
// @ts-ignore TODO pending release of fixed types
ecmaVersion: 11,
preserveParens: true
ecmaVersion: 11
});

@ -26,7 +26,7 @@ process.on('unhandledRejection', err => {
unhandled_rejection = err;
});
describe("runtime", () => {
describe.only("runtime", () => {
before(() => {
svelte = loadSvelte(false);
svelte$ = loadSvelte(true);

@ -1,3 +1,3 @@
export default {
html: '<h1>1 + 2 = 3</h1>'
html: '<p>1 + 2 = 3</p>'
};

@ -3,7 +3,7 @@ export default {
<button>action</button>
`,
async test({ assert, component, target, window }) {
async test({ assert, target, window }) {
const button = target.querySelector('button');
const eventEnter = new window.MouseEvent('mouseenter');
const eventLeave = new window.MouseEvent('mouseleave');

Loading…
Cancel
Save