pull/3539/head
Rich Harris 6 years ago
parent 1d8d17bc07
commit 50280232e8

@ -1,4 +1,3 @@
import MagicString from 'magic-string';
// @ts-ignore // @ts-ignore
import { walk, childKeys } from 'estree-walker'; import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character'; import { getLocator } from 'locate-character';
@ -23,14 +22,13 @@ import flatten_reference from './utils/flatten_reference';
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import TemplateScope from './nodes/shared/TemplateScope'; import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch'; import fuzzymatch from '../utils/fuzzymatch';
import { remove_indentation, add_indentation } from '../utils/indentation';
import get_object from './utils/get_object'; import get_object from './utils/get_object';
import unwrap_parens from './utils/unwrap_parens'; import unwrap_parens from './utils/unwrap_parens';
import Slot from './nodes/Slot'; import Slot from './nodes/Slot';
import { Node as ESTreeNode } from 'estree'; import { Node as ESTreeNode } from 'estree';
import add_to_set from './utils/add_to_set'; import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles'; import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print } from 'code-red'; import { print, x } from 'code-red';
interface ComponentOptions { interface ComponentOptions {
namespace?: string; namespace?: string;
@ -48,37 +46,13 @@ childKeys.Attribute = ['value'];
childKeys.ExportNamedDeclaration = ['declaration', 'specifiers']; childKeys.ExportNamedDeclaration = ['declaration', 'specifiers'];
function remove_node( function remove_node(
code: MagicString,
start: number,
end: number,
body: Node, body: Node,
node: Node node: Node
) { ) {
const i = body.indexOf(node); const i = body.indexOf(node);
if (i === -1) throw new Error('node not in list'); if (i === -1) throw new Error('node not in list');
let a; body.splice(i, 1);
let b;
if (body.length === 1) {
// remove everything, leave {}
a = start;
b = end;
} else if (i === 0) {
// remove everything before second node, including comments
a = start;
while (/\s/.test(code.original[a])) a += 1;
b = body[i].end;
while (/[\s,]/.test(code.original[b])) b += 1;
} else {
// remove the end of the previous node to the end of this one
a = body[i - 1].end;
b = node.end;
}
code.remove(a, b);
return;
} }
export default class Component { export default class Component {
@ -89,7 +63,6 @@ export default class Component {
ast: Ast; ast: Ast;
source: string; source: string;
code: MagicString;
name: Identifier; name: Identifier;
compile_options: CompileOptions; compile_options: CompileOptions;
fragment: Fragment; fragment: Fragment;
@ -173,8 +146,6 @@ export default class Component {
: compile_options.filename); : compile_options.filename);
this.locate = getLocator(this.source); this.locate = getLocator(this.source);
this.code = new MagicString(source);
// styles // styles
this.stylesheet = new Stylesheet( this.stylesheet = new Stylesheet(
source, source,
@ -261,15 +232,6 @@ export default class Component {
} }
} }
add_sourcemap_locations(node: Node) {
walk(node, {
enter: (node: Node) => {
this.code.addSourcemapLocation(node.start);
this.code.addSourcemapLocation(node.end);
},
});
}
alias(name: string) { alias(name: string) {
if (!this.aliases.has(name)) { if (!this.aliases.has(name)) {
this.aliases.set(name, this.get_unique_name(name)); this.aliases.set(name, this.get_unique_name(name));
@ -502,21 +464,20 @@ export default class Component {
} }
extract_imports(content) { extract_imports(content) {
const { code } = this;
content.body.forEach(node => { content.body.forEach(node => {
if (node.type === 'ImportDeclaration') { if (node.type === 'ImportDeclaration') {
// imports need to be hoisted out of the IIFE // imports need to be hoisted out of the IIFE
remove_node(code, content.start, content.end, content.body, node); remove_node(content.body, node);
this.imports.push(node); this.imports.push(node);
} }
}); });
} }
extract_exports(content) { extract_exports(content) {
const { code } = this; let i = content.body.length;
while (i--) {
const node = content.body[i];
content.body.forEach(node => {
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
this.error(node, { this.error(node, {
code: `default-export`, code: `default-export`,
@ -546,21 +507,20 @@ export default class Component {
variable.export_name = name; variable.export_name = name;
} }
code.remove(node.start, node.declaration.start); content.body[i] = node.declaration;
} else { } else {
remove_node(code, content.start, content.end, content.body, node);
node.specifiers.forEach(specifier => { node.specifiers.forEach(specifier => {
const variable = this.var_lookup.get(specifier.local.name); const variable = this.var_lookup.get(specifier.local.name);
if (variable) { if (variable) {
variable.export_name = specifier.exported.name; variable.export_name = specifier.exported.name;
} else {
// TODO what happens with `export { Math }` or some other global?
} }
}); });
content.body.splice(i, 1);
}
} }
} }
});
} }
extract_javascript(script) { extract_javascript(script) {
@ -573,35 +533,37 @@ export default class Component {
return true; return true;
}); });
return nodes_to_include;
if (nodes_to_include.length === 0) return null; if (nodes_to_include.length === 0) return null;
let a = script.content.start; let a = script.content.start;
while (/\s/.test(this.source[a])) a += 1; while (/\s/.test(this.source[a])) a += 1;
let b = a; // let b = a;
let result = ''; // let result = '';
script.content.body.forEach(node => { // script.content.body.forEach(node => {
if ( // if (
this.hoistable_nodes.has(node) || // this.hoistable_nodes.has(node) ||
this.reactive_declaration_nodes.has(node) // this.reactive_declaration_nodes.has(node)
) { // ) {
if (a !== b) result += `[✂${a}-${b}✂]`; // if (a !== b) result += `[✂${a}-${b}✂]`;
a = node.end; // a = node.end;
} // }
b = node.end; // b = node.end;
}); // });
// while (/\s/.test(this.source[a - 1])) a -= 1; // while (/\s/.test(this.source[a - 1])) a -= 1;
b = script.content.end; // b = script.content.end;
while (/\s/.test(this.source[b - 1])) b -= 1; // while (/\s/.test(this.source[b - 1])) b -= 1;
if (a < b) result += `[✂${a}-${b}✂]`; // if (a < b) result += `[✂${a}-${b}✂]`;
return result || null; // return result || null;
} }
walk_module_js() { walk_module_js() {
@ -620,8 +582,6 @@ export default class Component {
}, },
}); });
this.add_sourcemap_locations(script.content);
const { scope, globals } = create_scopes(script.content); const { scope, globals } = create_scopes(script.content);
this.module_scope = scope; this.module_scope = scope;
@ -657,7 +617,6 @@ export default class Component {
this.extract_imports(script.content); this.extract_imports(script.content);
this.extract_exports(script.content); this.extract_exports(script.content);
remove_indentation(this.code, script.content);
this.module_javascript = this.extract_javascript(script); this.module_javascript = this.extract_javascript(script);
} }
@ -665,8 +624,6 @@ export default class Component {
const script = this.ast.instance; const script = this.ast.instance;
if (!script) return; if (!script) return;
this.add_sourcemap_locations(script.content);
// inject vars for reactive declarations // inject vars for reactive declarations
script.content.body.forEach(node => { script.content.body.forEach(node => {
if (node.type !== 'LabeledStatement') return; if (node.type !== 'LabeledStatement') return;
@ -893,18 +850,16 @@ export default class Component {
.join(', '); .join(', ');
} }
rewrite_props(get_insert: (variable: Var) => string) { rewrite_props(_get_insert: (variable: Var) => string) {
// TODO
const component = this; const component = this;
const { code, instance_scope, instance_scope_map: map } = this; const { instance_scope, instance_scope_map: map } = this;
let scope = instance_scope; let scope = instance_scope;
const coalesced_declarations = [];
let current_group;
walk(this.ast.instance.content, { walk(this.ast.instance.content, {
enter(node, parent) { enter(node) {
if (/Function/.test(node.type)) { if (/Function/.test(node.type)) {
current_group = null;
return this.skip(); return this.skip();
} }
@ -914,16 +869,15 @@ export default class Component {
if (node.type === 'VariableDeclaration') { if (node.type === 'VariableDeclaration') {
if (node.kind === 'var' || scope === instance_scope) { if (node.kind === 'var' || scope === instance_scope) {
node.declarations.forEach((declarator, i) => { node.declarations.forEach(declarator => {
const next = node.declarations[i + 1]; // const next = node.declarations[i + 1];
if (declarator.id.type !== 'Identifier') { if (declarator.id.type !== 'Identifier') {
const inserts = [];
extract_names(declarator.id).forEach(name => { extract_names(declarator.id).forEach(name => {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (variable.export_name) { if (variable.export_name) {
// TODO is this still true post-#3539?
component.error(declarator, { component.error(declarator, {
code: 'destructured-prop', code: 'destructured-prop',
message: `Cannot declare props in destructured declaration`, message: `Cannot declare props in destructured declaration`,
@ -931,22 +885,10 @@ export default class Component {
} }
if (variable.subscribable) { if (variable.subscribable) {
inserts.push(get_insert(variable)); // TODO
} }
}); });
if (inserts.length > 0) {
if (next) {
code.overwrite(
declarator.end,
next.start,
`; ${inserts.join('; ')}; ${node.kind} `
);
} else {
code.appendLeft(declarator.end, `; ${inserts.join('; ')}`);
}
}
return; return;
} }
@ -954,112 +896,43 @@ export default class Component {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (variable.export_name) { if (variable.export_name) {
if (current_group && current_group.kind !== node.kind) { // const insert = variable.subscribable
current_group = null; // ? get_insert(variable)
} // : null;
const insert = variable.subscribable declarator.id = {
? get_insert(variable) type: 'ObjectPattern',
: null; properties: [{
type: 'Property',
if (!current_group || (current_group.insert && insert)) { method: false,
current_group = { shorthand: true,
kind: node.kind, computed: false,
declarators: [declarator], kind: 'init',
insert, key: declarator.id,
value: {
type: 'AssignmentPattern',
left: declarator.id,
right: declarator.init
}
}]
}; };
coalesced_declarations.push(current_group);
} else if (insert) {
current_group.insert = insert;
current_group.declarators.push(declarator);
} else {
current_group.declarators.push(declarator);
}
if (
variable.writable &&
variable.name !== variable.export_name
) {
code.prependRight(
declarator.id.start,
`${variable.export_name}: `
);
}
if (next) { declarator.init = x`$$props`;
const next_variable = component.var_lookup.get(next.id.name);
const new_declaration =
!next_variable.export_name ||
(current_group.insert && next_variable.subscribable);
if (new_declaration) {
code.overwrite(
declarator.end,
next.start,
` ${node.kind} `
);
}
}
} else {
current_group = null;
if (variable.subscribable) {
const insert = get_insert(variable);
if (next) {
code.overwrite(
declarator.end,
next.start,
`; ${insert}; ${node.kind} `
);
} else {
code.appendLeft(declarator.end, `; ${insert}`);
}
}
} }
}); });
} }
} else {
if (node.type !== 'ExportNamedDeclaration') {
if (!parent || parent.type === 'Program') current_group = null;
}
} }
}, },
leave(node) { leave(node, _parent, _key, index) {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
},
});
coalesced_declarations.forEach(group => {
const writable = group.kind === 'var' || group.kind === 'let';
let c = 0;
let combining = false;
group.declarators.forEach(declarator => {
const { id } = declarator;
if (combining) { if (node.type === 'ExportNamedDeclaration' && node.declaration) {
code.overwrite(c, id.start, ', '); _parent.body[index] = node.declaration;
} else {
if (writable) code.appendLeft(id.start, '{ ');
combining = true;
}
c = declarator.end;
});
if (combining) {
const insert = group.insert ? `; ${group.insert}` : '';
const suffix =
`${writable ? ` } = $$props` : ``}${insert}` +
(code.original[c] === ';' ? `` : `;`);
code.appendLeft(c, suffix);
} }
},
}); });
} }
@ -1213,8 +1086,6 @@ export default class Component {
variable.hoistable = true; variable.hoistable = true;
hoistable_nodes.add(node); hoistable_nodes.add(node);
remove_indentation(this.code, node);
const i = body.indexOf(node); const i = body.indexOf(node);
body.splice(i, 1); body.splice(i, 1);
this.fully_hoisted.push(node); this.fully_hoisted.push(node);
@ -1280,8 +1151,6 @@ export default class Component {
}, },
}); });
add_indentation(this.code, node.body, 2);
const expression = const expression =
node.body.expression && unwrap_parens(node.body.expression); node.body.expression && unwrap_parens(node.body.expression);
const declaration = expression && expression.left; const declaration = expression && expression.left;

@ -1,18 +1,18 @@
import Component from '../../Component'; import Component from '../../Component';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import { b } from 'code-red'; // import { b } from 'code-red';
import flatten_reference from '../../utils/flatten_reference'; import flatten_reference from '../../utils/flatten_reference';
import { create_scopes, Scope, extract_names } from '../../utils/scope'; import { create_scopes, Scope, extract_names } from '../../utils/scope';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { globals , sanitize } from '../../../utils/names'; import { globals } from '../../../utils/names';
import Wrapper from '../../render_dom/wrappers/shared/Wrapper'; import Wrapper from '../../render_dom/wrappers/shared/Wrapper';
import TemplateScope from './TemplateScope'; import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object'; import get_object from '../../utils/get_object';
import Block from '../../render_dom/Block'; import Block from '../../render_dom/Block';
import { INode } from '../interfaces'; import { INode } from '../interfaces';
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic'; import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
import { invalidate } from '../../utils/invalidate'; // import { invalidate } from '../../utils/invalidate';
const binary_operators: Record<string, number> = { const binary_operators: Record<string, number> = {
'**': 15, '**': 15,
@ -224,192 +224,189 @@ export default class Expression {
} }
// TODO move this into a render-dom wrapper? // TODO move this into a render-dom wrapper?
render(block?: Block) { render(_block?: Block) {
if (this.rendered) return this.rendered; // TODO
const { // if (this.rendered) return this.rendered;
component,
declarations, // const {
scope_map: map, // component,
template_scope, // declarations,
owner, // scope_map: map,
is_synthetic // template_scope,
} = this; // owner,
let scope = this.scope; // is_synthetic
// } = this;
const { code } = component; // let scope = this.scope;
let function_expression; // let function_expression;
let dependencies: Set<string>; // let dependencies: Set<string>;
let contextual_dependencies: Set<string>; // let contextual_dependencies: Set<string>;
// rewrite code as appropriate // // rewrite code as appropriate
walk(this.node, { // walk(this.node, {
enter(node: any, parent: any, key: string) { // enter(node: any, parent: any, key: string) {
// don't manipulate shorthand props twice // // don't manipulate shorthand props twice
if (key === 'value' && parent.shorthand) return; // if (key === 'value' && parent.shorthand) return;
code.addSourcemapLocation(node.start); // if (map.has(node)) {
code.addSourcemapLocation(node.end); // scope = map.get(node);
// }
if (map.has(node)) {
scope = map.get(node); // if (is_reference(node, parent)) {
} // const { name, nodes } = flatten_reference(node);
if (is_reference(node, parent)) { // if (scope.has(name)) return;
const { name, nodes } = flatten_reference(node); // if (globals.has(name) && !component.var_lookup.has(name)) return;
if (scope.has(name)) return; // if (function_expression) {
if (globals.has(name) && !component.var_lookup.has(name)) return; // if (template_scope.names.has(name)) {
// contextual_dependencies.add(name);
if (function_expression) {
if (template_scope.names.has(name)) { // template_scope.dependencies_for_name.get(name).forEach(dependency => {
contextual_dependencies.add(name); // dependencies.add(dependency);
// });
template_scope.dependencies_for_name.get(name).forEach(dependency => { // } else {
dependencies.add(dependency); // dependencies.add(name);
}); // component.add_reference(name); // TODO is this redundant/misplaced?
} else { // }
dependencies.add(name); // } else if (!is_synthetic && is_contextual(component, template_scope, name)) {
component.add_reference(name); // TODO is this redundant/misplaced? // code.prependRight(node.start, key === 'key' && parent.shorthand
} // ? `${name}: ctx.`
} else if (!is_synthetic && is_contextual(component, template_scope, name)) { // : 'ctx.');
code.prependRight(node.start, key === 'key' && parent.shorthand // }
? `${name}: ctx.`
: 'ctx.'); // if (node.type === 'MemberExpression') {
} // nodes.forEach(node => {
// code.addSourcemapLocation(node.start);
if (node.type === 'MemberExpression') { // code.addSourcemapLocation(node.end);
nodes.forEach(node => { // });
code.addSourcemapLocation(node.start); // }
code.addSourcemapLocation(node.end);
}); // this.skip();
} // }
this.skip(); // if (!function_expression) {
} // if (node.type === 'AssignmentExpression') {
// // TODO should this be a warning/error? `<p>{foo = 1}</p>`
if (!function_expression) { // }
if (node.type === 'AssignmentExpression') {
// TODO should this be a warning/error? `<p>{foo = 1}</p>` // if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
} // function_expression = node;
// dependencies = new Set();
if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { // contextual_dependencies = new Set();
function_expression = node; // }
dependencies = new Set(); // }
contextual_dependencies = new Set(); // },
}
} // leave(node: Node, parent: Node) {
}, // if (map.has(node)) scope = scope.parent;
leave(node: Node, parent: Node) { // if (node === function_expression) {
if (map.has(node)) scope = scope.parent; // const id = component.get_unique_name(
// sanitize(get_function_name(node, owner))
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(', ')} }`]
// : [];
const args = contextual_dependencies.size > 0
? [`{ ${Array.from(contextual_dependencies).join(', ')} }`] // let original_params;
: [];
// if (node.params.length > 0) {
let original_params; // original_params = code.slice(node.params[0].start, node.params[node.params.length - 1].end);
// args.push(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'
const body = code.slice(node.body.start, node.body.end).trim(); // ? b`${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${id}(${args.join(', ')}) ${body}`
// : b`const ${id} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`;
const fn = node.type === 'FunctionExpression'
? b`${node.async ? 'async ' : ''}function${node.generator ? '*' : ''} ${id}(${args.join(', ')}) ${body}` // if (dependencies.size === 0 && contextual_dependencies.size === 0) {
: b`const ${id} = ${node.async ? 'async ' : ''}(${args.join(', ')}) => ${body};`; // // we can hoist this out of the component completely
// component.fully_hoisted.push(fn);
if (dependencies.size === 0 && contextual_dependencies.size === 0) { // code.overwrite(node.start, node.end, id.name);
// we can hoist this out of the component completely
component.fully_hoisted.push(fn); // component.add_var({
code.overwrite(node.start, node.end, id.name); // name: id.name,
// internal: true,
component.add_var({ // hoistable: true,
name: id.name, // referenced: true
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);
else if (contextual_dependencies.size === 0) { // code.overwrite(node.start, node.end, `ctx.${id}`);
// function can be hoisted inside the component init
component.partly_hoisted.push(fn); // component.add_var({
code.overwrite(node.start, node.end, `ctx.${id}`); // name: id.name,
// internal: true,
component.add_var({ // referenced: true
name: id.name, // });
internal: true, // }
referenced: true
}); // else {
} // // we need a combo block/init recipe
// component.partly_hoisted.push(fn);
else { // code.overwrite(node.start, node.end, id.name);
// we need a combo block/init recipe
component.partly_hoisted.push(fn); // component.add_var({
code.overwrite(node.start, node.end, id.name); // name: id.name,
// internal: true,
component.add_var({ // referenced: true
name: id.name, // });
internal: true,
referenced: true // declarations.push(b`
}); // function ${id}(${original_params ? '...args' : ''}) {
// return ctx.${id}(ctx${original_params ? ', ...args' : ''});
declarations.push(b` // }
function ${id}(${original_params ? '...args' : ''}) { // `);
return ctx.${id}(ctx${original_params ? ', ...args' : ''}); // }
}
`); // if (parent && parent.method) {
} // code.prependRight(node.start, ': ');
// }
if (parent && parent.method) {
code.prependRight(node.start, ': '); // function_expression = null;
} // dependencies = null;
// contextual_dependencies = null;
function_expression = null; // }
dependencies = null;
contextual_dependencies = null; // if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
} // const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { // // normally (`a = 1`, `b.c = 2`), there'll be a single name
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; // // (a or b). In destructuring cases (`[d, e] = [e, d]`) there
// // may be more, in which case we need to tack the extra ones
// normally (`a = 1`, `b.c = 2`), there'll be a single name // // onto the initial function call
// (a or b). In destructuring cases (`[d, e] = [e, d]`) there // const names = new Set(extract_names(assignee));
// may be more, in which case we need to tack the extra ones
// onto the initial function call // const traced: Set<string> = new Set();
const names = new Set(extract_names(assignee)); // names.forEach(name => {
// const dependencies = template_scope.dependencies_for_name.get(name);
const traced: Set<string> = new Set(); // if (dependencies) {
names.forEach(name => { // dependencies.forEach(name => traced.add(name));
const dependencies = template_scope.dependencies_for_name.get(name); // } else {
if (dependencies) { // traced.add(name);
dependencies.forEach(name => traced.add(name)); // }
} else { // });
traced.add(name);
} // invalidate(component, scope, code, node, traced);
}); // }
// }
invalidate(component, scope, code, node, traced); // });
}
} // if (declarations.length > 0) {
}); // block.maintain_context = true;
// declarations.forEach(declaration => {
if (declarations.length > 0) { // block.chunks.init.push(declaration);
block.maintain_context = true; // });
declarations.forEach(declaration => { // }
block.chunks.init.push(declaration);
});
}
throw new Error(`bad`); throw new Error(`bad`);
@ -417,29 +414,29 @@ export default class Expression {
} }
} }
function get_function_name(_node, parent) { // function get_function_name(_node, parent) {
if (parent.type === 'EventHandler') { // if (parent.type === 'EventHandler') {
return `${parent.name}_handler`; // return `${parent.name}_handler`;
} // }
if (parent.type === 'Action') { // if (parent.type === 'Action') {
return `${parent.name}_function`; // return `${parent.name}_function`;
} // }
return 'func'; // return 'func';
} // }
function is_contextual(component: Component, scope: TemplateScope, name: string) { // function is_contextual(component: Component, scope: TemplateScope, name: string) {
if (name === '$$props') return true; // if (name === '$$props') return true;
// if it's a name below root scope, it's contextual // // if it's a name below root scope, it's contextual
if (!scope.is_top_level(name)) return true; // if (!scope.is_top_level(name)) return true;
const variable = component.var_lookup.get(name); // const variable = component.var_lookup.get(name);
// hoistables, module declarations, and imports are non-contextual // // hoistables, module declarations, and imports are non-contextual
if (!variable || variable.hoistable) return false; // if (!variable || variable.hoistable) return false;
// assume contextual // // assume contextual
return true; // return true;
} // }

@ -14,7 +14,7 @@ export default function dom(
component: Component, component: Component,
options: CompileOptions options: CompileOptions
) { ) {
const { name, code } = component; const { name } = component;
const renderer = new Renderer(component, options); const renderer = new Renderer(component, options);
const { block } = renderer; const { block } = renderer;
@ -198,7 +198,7 @@ export default function dom(
// onto the initial function call // onto the initial function call
const names = new Set(extract_names(assignee)); const names = new Set(extract_names(assignee));
invalidate(component, scope, code, node, names); invalidate(component, scope, node, names);
} }
} }
}); });

@ -2,7 +2,7 @@ import Renderer from '../Renderer';
import Wrapper from './shared/Wrapper'; import Wrapper from './shared/Wrapper';
import Block from '../Block'; import Block from '../Block';
import DebugTag from '../../nodes/DebugTag'; import DebugTag from '../../nodes/DebugTag';
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 } from 'code-red';
export default class DebugTagWrapper extends Wrapper { export default class DebugTagWrapper extends Wrapper {
@ -21,11 +21,11 @@ export default class DebugTagWrapper extends Wrapper {
render(block: Block, _parent_node: string, _parent_nodes: string) { render(block: Block, _parent_node: string, _parent_nodes: string) {
const { renderer } = this; const { renderer } = this;
const { component } = renderer; // const { component } = renderer;
if (!renderer.options.dev) return; if (!renderer.options.dev) return;
const { var_lookup } = component; // const { var_lookup } = component;
if (this.node.expressions.length === 0) { if (this.node.expressions.length === 0) {
// Debug all // Debug all
@ -37,43 +37,45 @@ export default class DebugTagWrapper extends Wrapper {
block.chunks.create.push(b`debugger`); block.chunks.create.push(b`debugger`);
block.chunks.update.push(b`debugger`); block.chunks.update.push(b`debugger`);
} else { } else {
const { code } = component; // TODO
code.overwrite(this.node.start + 1, this.node.start + 7, 'log', {
storeName: true
});
const log = `[✂${this.node.start + 1}-${this.node.start + 7}✂]`;
const dependencies = new Set(); // const { code } = component;
this.node.expressions.forEach(expression => { // code.overwrite(this.node.start + 1, this.node.start + 7, 'log', {
add_to_set(dependencies, expression.dependencies); // storeName: true
}); // });
// const log = `[✂${this.node.start + 1}-${this.node.start + 7}✂]`;
// const dependencies = new Set();
// this.node.expressions.forEach(expression => {
// add_to_set(dependencies, expression.dependencies);
// });
const condition = Array.from(dependencies).map(d => `changed.${d}`).join(' || '); // const condition = Array.from(dependencies).map(d => `changed.${d}`).join(' || ');
const ctx_identifiers = this.node.expressions // const ctx_identifiers = this.node.expressions
.filter(e => { // .filter(e => {
const looked_up_var = var_lookup.get(e.node.name); // const looked_up_var = var_lookup.get(e.node.name);
return !(looked_up_var && looked_up_var.hoistable); // return !(looked_up_var && looked_up_var.hoistable);
}) // })
.map(e => e.node.name) // .map(e => e.node.name)
.join(', '); // .join(', ');
const logged_identifiers = this.node.expressions.map(e => e.node.name).join(', '); // const logged_identifiers = this.node.expressions.map(e => e.node.name).join(', ');
block.chunks.update.push(b` // block.chunks.update.push(b`
if (${condition}) { // if (${condition}) {
const { ${ctx_identifiers} } = ctx; // const { ${ctx_identifiers} } = ctx;
@_console.${log}({ ${logged_identifiers} }); // @_console.${log}({ ${logged_identifiers} });
debugger; // debugger;
} // }
`); // `);
block.chunks.create.push(b` // block.chunks.create.push(b`
{ // {
const { ${ctx_identifiers} } = ctx; // const { ${ctx_identifiers} } = ctx;
@_console.${log}({ ${logged_identifiers} }); // @_console.${log}({ ${logged_identifiers} });
debugger; // debugger;
} // }
`); // `);
} }
} }
} }

@ -104,11 +104,12 @@ export default class EachBlockWrapper extends Wrapper {
? node.expression.node.elements.length ? node.expression.node.elements.length
: null; : null;
// TODO
// hack the sourcemap, so that if data is missing the bug // hack the sourcemap, so that if data is missing the bug
// is easy to find // is easy to find
let c = this.node.start + 2; let c = this.node.start + 2;
while (renderer.component.source[c] !== 'e') c += 1; while (renderer.component.source[c] !== 'e') c += 1;
renderer.component.code.overwrite(c, c + 4, 'length'); // renderer.component.code.overwrite(c, c + 4, 'length');
const each_block_value = renderer.component.get_unique_name(`${this.var}_value`); const each_block_value = renderer.component.get_unique_name(`${this.var}_value`);
const iterations = block.get_unique_name(`${this.var}_blocks`); const iterations = block.get_unique_name(`${this.var}_blocks`);

@ -18,7 +18,7 @@ export default class MustacheTagWrapper extends Tag {
render(block: Block, parent_node: string, parent_nodes: string) { render(block: Block, parent_node: string, parent_nodes: string) {
const { init } = this.rename_this_method( const { init } = this.rename_this_method(
block, block,
value => `@set_data(${this.var}, ${value});` value => x`@set_data(${this.var}, ${value});`
); );
block.add_element( block.add_element(

@ -46,7 +46,7 @@ export default class RawMustacheTagWrapper extends Tag {
const { init } = this.rename_this_method( const { init } = this.rename_this_method(
block, block,
content => `${html_tag}.p(${content});` content => x`${html_tag}.p(${content});`
); );
const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null'; const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null';

@ -4,6 +4,7 @@ import Renderer from '../../Renderer';
import Block from '../../Block'; import Block from '../../Block';
import MustacheTag from '../../../nodes/MustacheTag'; import MustacheTag from '../../../nodes/MustacheTag';
import RawMustacheTag from '../../../nodes/RawMustacheTag'; import RawMustacheTag from '../../../nodes/RawMustacheTag';
import { Node } from '../../../../interfaces';
export default class Tag extends Wrapper { export default class Tag extends Wrapper {
node: MustacheTag | RawMustacheTag; node: MustacheTag | RawMustacheTag;
@ -17,7 +18,7 @@ export default class Tag extends Wrapper {
rename_this_method( rename_this_method(
block: Block, block: Block,
update: ((value: string) => string) update: ((value: Node) => Node)
) { ) {
const dependencies = this.node.expression.dynamic_dependencies(); const dependencies = this.node.expression.dynamic_dependencies();
const snippet = this.node.expression.node; const snippet = this.node.expression.node;

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

@ -1,57 +0,0 @@
import MagicString from 'magic-string';
import { Node } from '../interfaces';
import { walk } from 'estree-walker';
import repeat from './repeat';
export function remove_indentation(code: MagicString, node: Node) {
const indent = code.getIndentString();
const pattern = new RegExp(`^${indent}`, 'gm');
const excluded = [];
walk(node, {
enter(node) {
if (node.type === 'TemplateElement') {
excluded.push(node);
}
}
});
const str = code.original.slice(node.start, node.end);
let match;
while (match = pattern.exec(str)) {
const index = node.start + match.index;
while (excluded[0] && excluded[0].end < index) excluded.shift();
if (excluded[0] && excluded[0].start < index) continue;
code.remove(index, index + indent.length);
}
}
export function add_indentation(code: MagicString, node: Node, levels = 1) {
const base_indent = code.getIndentString();
const indent = repeat(base_indent, levels);
const pattern = /\n/gm;
const excluded = [];
walk(node, {
enter(node) {
if (node.type === 'TemplateElement') {
excluded.push(node);
}
}
});
const str = code.original.slice(node.start, node.end);
let match;
while (match = pattern.exec(str)) {
const index = node.start + match.index;
while (excluded[0] && excluded[0].end < index) excluded.shift();
if (excluded[0] && excluded[0].start < index) continue;
code.appendLeft(index + 1, indent);
}
}

@ -1,5 +1,5 @@
<script> <script>
let name = 'world'; export let name = 'world';
</script> </script>
<h1>Hello {name}!</h1> <h1>Hello {name}!</h1>
Loading…
Cancel
Save