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

@ -371,23 +371,38 @@ export default class Expression {
// we can hoist this out of the component completely
component.fully_hoisted.push(fn);
code.overwrite(node.start, node.end, name);
component.add_var({
name,
kind: 'injected',
hoistable: true,
referenced: true
});
}
else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(fn);
component.declarations.push(name);
component.add_reference(name);
code.overwrite(node.start, node.end, `ctx.${name}`);
component.add_var({
name,
kind: 'injected',
referenced: true
});
}
else {
// we need a combo block/init recipe
component.partly_hoisted.push(fn);
component.declarations.push(name);
component.add_reference(name);
code.overwrite(node.start, node.end, name);
component.add_var({
name,
kind: 'injected',
referenced: true
});
declarations.push(deindent`
function ${name}(${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
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
return true;

@ -70,7 +70,7 @@ export default function dom(
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 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});
`}
${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 &&
`if ('$$scope' in $$props) $$invalidate('$$scope', $$scope = $$props.$$scope);`}
}
@ -97,31 +97,31 @@ export default function dom(
props.forEach(x => {
const variable = component.var_lookup.get(x.name);
if (component.imported_declarations.has(x.name) || variable.hoistable) {
if (variable.hoistable) {
body.push(deindent`
get ${x.exported_as}() {
get ${x.export_name}() {
return ${x.name};
}
`);
} else {
body.push(deindent`
get ${x.exported_as}() {
get ${x.export_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`
set ${x.exported_as}(${x.name}) {
set ${x.export_name}(${x.name}) {
this.$set({ ${x.name} });
@flush();
}
`);
} else if (component.options.dev) {
body.push(deindent`
set ${x.exported_as}(value) {
throw new Error("<${component.tag}>: Cannot set read-only property '${x.exported_as}'");
set ${x.export_name}(value) {
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 props = ${options.customElement ? `this.attributes` : `options.props || {}`};
${expected.map(prop => deindent`
if (ctx.${prop.name} === undefined && !('${prop.exported_as}' in props)) {
console.warn("<${component.tag}> was created without expected prop '${prop.exported_as}'");
if (ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) {
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')}
`);
const filtered_declarations = component.declarations.filter(name => {
const variable = component.var_lookup.get(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_declarations = component.vars.filter(variable => {
return (variable.referenced || variable.export_name) && !variable.hoistable;
}).map(variable => variable.name);
const filtered_props = props.filter(prop => {
const variable = component.var_lookup.get(prop.name);
if (variable.hoistable) return false;
if (component.imported_declarations.has(prop.name)) return false;
if (prop.name[0] === '$') return false;
return true;
});
@ -368,7 +362,7 @@ export default function dom(
}
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')}

@ -405,8 +405,12 @@ export default class ElementWrapper extends Wrapper {
groups.forEach(group => {
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
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');
if (this_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;

@ -238,8 +238,12 @@ export default class InlineComponentWrapper extends Wrapper {
if (binding.name === 'this') {
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 object;
@ -269,8 +273,12 @@ export default class InlineComponentWrapper extends Wrapper {
}
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}`);
block.addVariable(updating);

@ -118,16 +118,18 @@ export default class WindowWrapper extends Wrapper {
`);
}
component.declarations.push(handler_name);
component.add_reference(handler_name);
component.add_var({
name: handler_name,
kind: 'injected',
referenced: true
});
component.partly_hoisted.push(deindent`
function ${handler_name}() {
${props.map(prop => `${prop.name} = window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)}
}
`);
block.builders.init.addBlock(deindent`
@add_render_callback(ctx.${handler_name});
`);

@ -25,13 +25,13 @@ export default function ssr(
let user_code;
// 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) {
component.rewrite_props();
user_code = component.javascript;
} 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] === '$');
@ -46,7 +46,7 @@ export default function ssr(
// TODO only do this for props with a default value
const parent_bindings = component.javascript
? 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 {
name: string;
kind: 'let' | 'var' | 'const' | 'class' | 'function' | 'import' | 'injected' | 'global';
import_type?: 'default' | 'named' | 'namespace';
imported_as?: string; // the `foo` in `import { foo as bar }`
exported_as?: string; // the `bar` in `export { foo as bar }`
import_name?: '*' | 'default' | string; // the `foo` in `import { foo as bar }`
export_name?: string; // the `bar` in `export { foo as bar }`
source?: string;
module?: boolean;
mutated?: boolean;

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

Loading…
Cancel
Save