start removing component.declarations

pull/2011/head
Richard Harris 7 years ago
parent 8c82b2c622
commit 8ed3bf7e9d

@ -99,7 +99,16 @@ export default class Stats {
return { return {
timings, timings,
warnings: this.warnings, warnings: this.warnings,
vars: component.vars vars: component.vars.map(variable => ({
name: variable.name,
kind: variable.kind,
import_name: variable.import_name || null,
export_name: variable.export_name || null,
source: variable.source || null,
module: variable.module || false,
mutated: variable.mutated || false,
referenced: variable.referenced || false
}))
}; };
} }

@ -15,7 +15,6 @@ import { Node, Ast, CompileOptions, Var } from '../interfaces';
import error from '../utils/error'; import error from '../utils/error';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
import flattenReference from '../utils/flattenReference'; import flattenReference from '../utils/flattenReference';
import addToSet from '../utils/addToSet';
import isReference from 'is-reference'; import isReference 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';
@ -65,7 +64,6 @@ export default class Component {
declarations: string[] = []; declarations: string[] = [];
imported_declarations: Set<string> = new Set(); imported_declarations: Set<string> = new Set();
hoistable_names: Set<string> = new Set();
hoistable_nodes: Set<Node> = new Set(); hoistable_nodes: Set<Node> = new Set();
node_for_declaration: Map<string, Node> = new Map(); node_for_declaration: Map<string, Node> = new Map();
partly_hoisted: string[] = []; partly_hoisted: string[] = [];
@ -148,20 +146,25 @@ export default class Component {
const props = [...this.template_references]; const props = [...this.template_references];
this.declarations.push(...props); this.declarations.push(...props);
props.forEach(name => { // props.forEach(name => {
this.add_var({ // this.add_var({
name, // name,
kind: 'injected', // kind: 'injected',
exported_as: name, // export_name: name,
mutated: true, // TODO kind of a misnomer... it's *mutable* but not necessarily *mutated*. is that a problem? // mutated: true, // TODO kind of a misnomer... it's *mutable* but not necessarily *mutated*. is that a problem?
referenced: true, // referenced: true,
writable: true // writable: true
}); // });
}); // });
} }
} }
add_var(variable: Var) { add_var(variable: Var) {
// TODO remove this
if (this.var_lookup.has(variable.name)) {
throw new Error(`dupe: ${variable.name}`);
}
this.vars.push(variable); this.vars.push(variable);
this.var_lookup.set(variable.name, variable); this.var_lookup.set(variable.name, variable);
} }
@ -171,13 +174,21 @@ export default class Component {
if (variable) { if (variable) {
variable.referenced = true; variable.referenced = true;
} else if (!this.ast.instance || name[0] === '$') { } else if (name[0] === '$') {
this.add_var({ this.add_var({
name, name,
kind: 'injected', kind: 'injected',
referenced: true,
mutated: true
});
} else if (!this.ast.instance) {
this.add_var({
name,
export_name: name,
kind: 'injected',
mutated: true, mutated: true,
referenced: true, referenced: true,
writable: name[0] !== '$' writable: true
}); });
} }
@ -241,9 +252,9 @@ export default class Component {
options.sveltePath, options.sveltePath,
importedHelpers, importedHelpers,
this.imports, this.imports,
this.vars.filter(variable => variable.module && variable.exported_as).map(variable => ({ this.vars.filter(variable => variable.module && variable.export_name).map(variable => ({
name: variable.name, name: variable.name,
as: variable.exported_as as: variable.export_name
})), })),
this.source this.source
); );
@ -414,25 +425,19 @@ export default class Component {
}); });
} }
const imported_as = specifier.imported const import_name = specifier.imported
? specifier.imported.name ? specifier.imported.name
: specifier.type === 'ImportDefaultSpecifier' : specifier.type === 'ImportDefaultSpecifier'
? 'default' ? 'default'
: '*'; : '*';
const import_type = specifier.type === 'ImportSpecifier'
? 'named'
: specifier.type === 'ImportDefaultSpecifier'
? 'default'
: 'namespace';
this.add_var({ this.add_var({
name: specifier.local.name, name: specifier.local.name,
kind: 'import', kind: 'import',
imported_as, import_name,
import_type,
source: node.source.value, source: node.source.value,
module: is_module, module: is_module,
hoistable: true
}); });
this.imported_declarations.add(specifier.local.name); this.imported_declarations.add(specifier.local.name);
@ -459,15 +464,9 @@ export default class Component {
if (node.declaration.type === 'VariableDeclaration') { if (node.declaration.type === 'VariableDeclaration') {
node.declaration.declarations.forEach(declarator => { node.declaration.declarations.forEach(declarator => {
extractNames(declarator.id).forEach(name => { extractNames(declarator.id).forEach(name => {
this.add_var({ const variable = this.var_lookup.get(name);
name, variable.export_name = name;
kind, if (kind !== 'const') variable.mutated = true;
exported_as: name,
module: is_module,
mutated: !is_module,
writable: kind === 'let' || kind === 'var',
initialised: !!declarator.init
});
}); });
}); });
} else { } else {
@ -482,14 +481,8 @@ export default class Component {
// sanity check // sanity check
if (!kind) throw new Error(`Unknown declaration type ${node.declaration.type}`); if (!kind) throw new Error(`Unknown declaration type ${node.declaration.type}`);
this.add_var({ const variable = this.var_lookup.get(name);
name, variable.export_name = name;
kind,
exported_as: name,
module: is_module,
mutated: !is_module,
initialised: true
});
} }
code.remove(node.start, node.declaration.start); code.remove(node.start, node.declaration.start);
@ -499,7 +492,7 @@ export default class Component {
const variable = this.var_lookup.get(specifier.local.name); const variable = this.var_lookup.get(specifier.local.name);
if (variable) { if (variable) {
variable.exported_as = specifier.exported.name; variable.export_name = specifier.exported.name;
} else { } else {
// TODO what happens with `export { Math }` or some other global? // TODO what happens with `export { Math }` or some other global?
} }
@ -577,7 +570,8 @@ export default class Component {
this.add_var({ this.add_var({
name, name,
kind kind,
module: true
}); });
this.declarations.push(name); this.declarations.push(name);
@ -623,7 +617,8 @@ export default class Component {
this.add_var({ this.add_var({
name, name,
kind, kind,
initialised: instance_scope.initialised_declarations.has(name) initialised: instance_scope.initialised_declarations.has(name),
writable: kind === 'var' || kind === 'let'
}); });
this.declarations.push(name); this.declarations.push(name);
@ -633,6 +628,8 @@ export default class Component {
}); });
globals.forEach(name => { globals.forEach(name => {
if (this.module_scope && this.module_scope.declarations.has(name)) return;
this.add_var({ this.add_var({
name, name,
kind: 'global' kind: 'global'
@ -756,7 +753,7 @@ export default class Component {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (name === meta.props_object) { if (name === meta.props_object) {
if (variable.exported_as) { if (variable.export_name) {
component.error(declarator, { component.error(declarator, {
code: 'exported-meta-props', code: 'exported-meta-props',
message: `Cannot export props binding` message: `Cannot export props binding`
@ -777,7 +774,7 @@ export default class Component {
} }
} }
if (variable.exported_as) { if (variable.export_name) {
has_exports = true; has_exports = true;
} else { } else {
has_only_exports = false; has_only_exports = false;
@ -861,7 +858,7 @@ export default class Component {
// reference instance variables other than other // reference instance variables other than other
// hoistable functions. TODO others? // hoistable functions. TODO others?
const { hoistable_names, hoistable_nodes, imported_declarations, var_lookup } = this; const { hoistable_nodes, imported_declarations, var_lookup } = this;
const top_level_function_declarations = new Map(); const top_level_function_declarations = new Map();
@ -871,7 +868,6 @@ export default class Component {
node.declarations.forEach(d => { node.declarations.forEach(d => {
const variable = this.var_lookup.get(d.id.name); const variable = this.var_lookup.get(d.id.name);
variable.hoistable = true; variable.hoistable = true;
hoistable_names.add(d.id.name);
}); });
hoistable_nodes.add(node); hoistable_nodes.add(node);
@ -924,7 +920,6 @@ export default class Component {
const variable = var_lookup.get(name); const variable = var_lookup.get(name);
if (variable.hoistable) return; if (variable.hoistable) return;
if (imported_declarations.has(name)) return;
if (top_level_function_declarations.has(name)) { if (top_level_function_declarations.has(name)) {
const other_declaration = top_level_function_declarations.get(name); const other_declaration = top_level_function_declarations.get(name);
@ -962,7 +957,6 @@ export default class Component {
if (!checked.has(node) && is_hoistable(node)) { if (!checked.has(node) && is_hoistable(node)) {
const variable = this.var_lookup.get(name); const variable = this.var_lookup.get(name);
variable.hoistable = true; variable.hoistable = true;
hoistable_names.add(name);
hoistable_nodes.add(node); hoistable_nodes.add(node);
remove_indentation(this.code, node); remove_indentation(this.code, node);
@ -1086,7 +1080,6 @@ export default class Component {
qualify(name) { qualify(name) {
const variable = this.var_lookup.get(name); const variable = this.var_lookup.get(name);
if (variable && variable.hoistable) return name; if (variable && variable.hoistable) return name;
if (this.imported_declarations.has(name)) return name;
if (this.declarations.indexOf(name) === -1) return name; if (this.declarations.indexOf(name) === -1) return name;
this.add_reference(name); // TODO we can probably remove most other occurrences of this this.add_reference(name); // TODO we can probably remove most other occurrences of this

@ -41,7 +41,12 @@ export default class EventHandler extends Node {
} }
} else { } else {
const name = component.getUniqueName(`${this.name}_handler`); const name = component.getUniqueName(`${this.name}_handler`);
component.declarations.push(name);
component.add_var({
name,
kind: 'injected',
referenced: true
});
component.partly_hoisted.push(deindent` component.partly_hoisted.push(deindent`
function ${name}(event) { function ${name}(event) {
@ -57,7 +62,7 @@ export default class EventHandler extends Node {
render(block: Block) { render(block: Block) {
if (this.expression) return this.expression.render(block); if (this.expression) return this.expression.render(block);
this.component.add_reference(this.handler_name); // this.component.add_reference(this.handler_name);
return `ctx.${this.handler_name}`; return `ctx.${this.handler_name}`;
} }
} }

@ -371,23 +371,38 @@ export default class Expression {
// 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, name);
component.add_var({
name,
kind: 'injected',
hoistable: true,
referenced: true
});
} }
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);
component.declarations.push(name);
component.add_reference(name);
code.overwrite(node.start, node.end, `ctx.${name}`); code.overwrite(node.start, node.end, `ctx.${name}`);
component.add_var({
name,
kind: 'injected',
referenced: true
});
} }
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);
component.declarations.push(name);
component.add_reference(name);
code.overwrite(node.start, node.end, name); code.overwrite(node.start, node.end, name);
component.add_var({
name,
kind: 'injected',
referenced: true
});
declarations.push(deindent` declarations.push(deindent`
function ${name}(${original_params ? '...args' : ''}) { function ${name}(${original_params ? '...args' : ''}) {
return ctx.${name}(ctx${original_params ? ', ...args' : ''}); return ctx.${name}(ctx${original_params ? ', ...args' : ''});
@ -461,8 +476,6 @@ function isContextual(component: Component, scope: TemplateScope, name: string)
// hoistables, module declarations, and imports are non-contextual // hoistables, module declarations, and imports are non-contextual
if (variable.hoistable) return false; if (variable.hoistable) return false;
if (variable.module) return false; // TODO make all module-level variables hoistable by default
if (variable.import_type) return false; // TODO ditto
// assume contextual // assume contextual
return true; return true;

@ -70,7 +70,7 @@ export default function dom(
options.css !== false options.css !== false
); );
const props = component.vars.filter(variable => !variable.module && variable.exported_as); const props = component.vars.filter(variable => !variable.module && variable.export_name);
const writable_props = props.filter(variable => variable.writable); const writable_props = props.filter(variable => variable.writable);
const set = (component.meta.props || writable_props.length > 0 || renderer.slots.size > 0) const set = (component.meta.props || writable_props.length > 0 || renderer.slots.size > 0)
@ -82,7 +82,7 @@ export default function dom(
$$invalidate('${component.meta.props_object}', ${component.meta.props_object}); $$invalidate('${component.meta.props_object}', ${component.meta.props_object});
`} `}
${writable_props.map(prop => ${writable_props.map(prop =>
`if ('${prop.exported_as}' in $$props) $$invalidate('${prop.name}', ${prop.name} = $$props.${prop.exported_as});`)} `if ('${prop.export_name}' in $$props) $$invalidate('${prop.name}', ${prop.name} = $$props.${prop.export_name});`)}
${renderer.slots.size > 0 && ${renderer.slots.size > 0 &&
`if ('$$scope' in $$props) $$invalidate('$$scope', $$scope = $$props.$$scope);`} `if ('$$scope' in $$props) $$invalidate('$$scope', $$scope = $$props.$$scope);`}
} }
@ -97,31 +97,31 @@ export default function dom(
props.forEach(x => { props.forEach(x => {
const variable = component.var_lookup.get(x.name); const variable = component.var_lookup.get(x.name);
if (component.imported_declarations.has(x.name) || variable.hoistable) { if (variable.hoistable) {
body.push(deindent` body.push(deindent`
get ${x.exported_as}() { get ${x.export_name}() {
return ${x.name}; return ${x.name};
} }
`); `);
} else { } else {
body.push(deindent` body.push(deindent`
get ${x.exported_as}() { get ${x.export_name}() {
return this.$$.ctx.${x.name}; return this.$$.ctx.${x.name};
} }
`); `);
} }
if (variable.writable && !renderer.readonly.has(x.exported_as)) { if (variable.writable && !renderer.readonly.has(x.export_name)) {
body.push(deindent` body.push(deindent`
set ${x.exported_as}(${x.name}) { set ${x.export_name}(${x.name}) {
this.$set({ ${x.name} }); this.$set({ ${x.name} });
@flush(); @flush();
} }
`); `);
} else if (component.options.dev) { } else if (component.options.dev) {
body.push(deindent` body.push(deindent`
set ${x.exported_as}(value) { set ${x.export_name}(value) {
throw new Error("<${component.tag}>: Cannot set read-only property '${x.exported_as}'"); throw new Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'");
} }
`); `);
} }
@ -137,8 +137,8 @@ export default function dom(
const { ctx } = this.$$; const { ctx } = this.$$;
const props = ${options.customElement ? `this.attributes` : `options.props || {}`}; const props = ${options.customElement ? `this.attributes` : `options.props || {}`};
${expected.map(prop => deindent` ${expected.map(prop => deindent`
if (ctx.${prop.name} === undefined && !('${prop.exported_as}' in props)) { if (ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) {
console.warn("<${component.tag}> was created without expected prop '${prop.exported_as}'"); console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'");
}`)} }`)}
`; `;
} }
@ -253,20 +253,14 @@ export default function dom(
${component.fully_hoisted.length > 0 && component.fully_hoisted.join('\n\n')} ${component.fully_hoisted.length > 0 && component.fully_hoisted.join('\n\n')}
`); `);
const filtered_declarations = component.declarations.filter(name => { const filtered_declarations = component.vars.filter(variable => {
const variable = component.var_lookup.get(name); return (variable.referenced || variable.export_name) && !variable.hoistable;
}).map(variable => variable.name);
if (variable && variable.hoistable) return false;
if (component.imported_declarations.has(name)) return false;
if (props.find(p => p.exported_as === name)) return true;
return component.template_references.has(name);
});
const filtered_props = props.filter(prop => { const filtered_props = props.filter(prop => {
const variable = component.var_lookup.get(prop.name); const variable = component.var_lookup.get(prop.name);
if (variable.hoistable) return false; if (variable.hoistable) return false;
if (component.imported_declarations.has(prop.name)) return false;
if (prop.name[0] === '$') return false; if (prop.name[0] === '$') return false;
return true; return true;
}); });
@ -368,7 +362,7 @@ export default function dom(
} }
static get observedAttributes() { static get observedAttributes() {
return ${JSON.stringify(props.map(x => x.exported_as))}; return ${JSON.stringify(props.map(x => x.export_name))};
} }
${body.length > 0 && body.join('\n\n')} ${body.length > 0 && body.join('\n\n')}

@ -405,8 +405,12 @@ export default class ElementWrapper extends Wrapper {
groups.forEach(group => { groups.forEach(group => {
const handler = renderer.component.getUniqueName(`${this.var}_${group.events.join('_')}_handler`); const handler = renderer.component.getUniqueName(`${this.var}_${group.events.join('_')}_handler`);
renderer.component.declarations.push(handler);
renderer.component.add_reference(handler); renderer.component.add_var({
name: handler,
kind: 'injected',
referenced: true
});
// TODO figure out how to handle locks // TODO figure out how to handle locks
const needsLock = group.bindings.some(binding => binding.needsLock); const needsLock = group.bindings.some(binding => binding.needsLock);
@ -506,8 +510,12 @@ export default class ElementWrapper extends Wrapper {
const this_binding = this.bindings.find(b => b.node.name === 'this'); const this_binding = this.bindings.find(b => b.node.name === 'this');
if (this_binding) { if (this_binding) {
const name = renderer.component.getUniqueName(`${this.var}_binding`); const name = renderer.component.getUniqueName(`${this.var}_binding`);
renderer.component.declarations.push(name);
renderer.component.add_reference(name); renderer.component.add_var({
name,
kind: 'injected',
referenced: true
});
const { handler, object } = this_binding; const { handler, object } = this_binding;

@ -238,8 +238,12 @@ export default class InlineComponentWrapper extends Wrapper {
if (binding.name === 'this') { if (binding.name === 'this') {
const fn = component.getUniqueName(`${this.var}_binding`); const fn = component.getUniqueName(`${this.var}_binding`);
component.declarations.push(fn);
component.add_reference(fn); component.add_var({
name: fn,
kind: 'injected',
referenced: true
});
let lhs; let lhs;
let object; let object;
@ -269,8 +273,12 @@ export default class InlineComponentWrapper extends Wrapper {
} }
const name = component.getUniqueName(`${this.var}_${binding.name}_binding`); const name = component.getUniqueName(`${this.var}_${binding.name}_binding`);
component.declarations.push(name);
component.add_reference(name); component.add_var({
name,
kind: 'injected',
referenced: true
});
const updating = block.getUniqueName(`updating_${binding.name}`); const updating = block.getUniqueName(`updating_${binding.name}`);
block.addVariable(updating); block.addVariable(updating);

@ -118,16 +118,18 @@ export default class WindowWrapper extends Wrapper {
`); `);
} }
component.declarations.push(handler_name); component.add_var({
component.add_reference(handler_name); name: handler_name,
kind: 'injected',
referenced: true
});
component.partly_hoisted.push(deindent` component.partly_hoisted.push(deindent`
function ${handler_name}() { function ${handler_name}() {
${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.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
@add_render_callback(ctx.${handler_name}); @add_render_callback(ctx.${handler_name});
`); `);

@ -25,13 +25,13 @@ export default function ssr(
let user_code; let user_code;
// TODO remove this, just use component.symbols everywhere // TODO remove this, just use component.symbols everywhere
const props = component.vars.filter(variable => !variable.module && variable.exported_as); const props = component.vars.filter(variable => !variable.module && variable.export_name);
if (component.javascript) { if (component.javascript) {
component.rewrite_props(); component.rewrite_props();
user_code = component.javascript; user_code = component.javascript;
} else if (!component.ast.instance && !component.ast.module && props.length > 0) { } else if (!component.ast.instance && !component.ast.module && props.length > 0) {
user_code = `let { ${props.map(prop => prop.exported_as).join(', ')} } = $$props;` user_code = `let { ${props.map(prop => prop.export_name).join(', ')} } = $$props;`
} }
const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$'); const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$');
@ -46,7 +46,7 @@ export default function ssr(
// TODO only do this for props with a default value // TODO only do this for props with a default value
const parent_bindings = component.javascript const parent_bindings = component.javascript
? props.map(prop => { ? props.map(prop => {
return `if ($$props.${prop.exported_as} === void 0 && $$bindings.${prop.exported_as} && ${prop.name} !== void 0) $$bindings.${prop.exported_as}(${prop.name});`; return `if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`;
}) })
: []; : [];

@ -82,9 +82,8 @@ export interface AppendTarget {
export interface Var { export interface Var {
name: string; name: string;
kind: 'let' | 'var' | 'const' | 'class' | 'function' | 'import' | 'injected' | 'global'; kind: 'let' | 'var' | 'const' | 'class' | 'function' | 'import' | 'injected' | 'global';
import_type?: 'default' | 'named' | 'namespace'; import_name?: '*' | 'default' | string; // the `foo` in `import { foo as bar }`
imported_as?: string; // the `foo` in `import { foo as bar }` export_name?: string; // the `bar` in `export { foo as bar }`
exported_as?: string; // the `bar` in `export { foo as bar }`
source?: string; source?: string;
module?: boolean; module?: boolean;
mutated?: boolean; mutated?: boolean;

@ -52,6 +52,10 @@ export function createScopes(expression: Node) {
}, },
}); });
scope.declarations.forEach((node, name) => {
globals.delete(name);
});
return { map, scope, globals }; return { map, scope, globals };
} }

Loading…
Cancel
Save