inject props at declaration site

pull/1839/head
Rich Harris 7 years ago
parent 7407bccb79
commit b026f04fbb

@ -506,29 +506,134 @@ export default class Component {
this.addSourcemapLocations(script.content);
let { scope, map, globals } = createScopes(script.content);
this.instance_scope = scope;
let { scope: instance_scope, map, globals } = createScopes(script.content);
this.instance_scope = instance_scope;
this.instance_scope_map = map;
scope.declarations.forEach((node, name) => {
instance_scope.declarations.forEach((node, name) => {
this.userVars.add(name);
this.declarations.push(name);
this.node_for_declaration.set(name, node);
});
this.writable_declarations = scope.writable_declarations;
this.initialised_declarations = scope.initialised_declarations;
this.writable_declarations = instance_scope.writable_declarations;
this.initialised_declarations = instance_scope.initialised_declarations;
globals.forEach(name => {
this.userVars.add(name);
});
this.extract_imports_and_exports(script.content, this.imports, this.props);
this.rewrite_props();
this.javascript = this.extract_javascript(script);
}
rewrite_props() {
const { instance_scope, instance_scope_map: map } = this;
let scope = instance_scope;
// TODO we will probably end up wanting to use this elsewhere
const exported = new Set();
this.props.forEach(prop => {
exported.add(prop.name);
});
const coalesced_declarations = [];
let current_group;
walk(this.instance_script.content, {
enter(node) {
if (/Function/.test(node.type)) {
current_group = null;
return this.skip();
}
if (map.has(node)) {
scope = map.get(node);
}
if (node.type === 'VariableDeclaration') {
if (node.kind === 'var' || scope === instance_scope) {
let has_exports = false;
let has_only_exports = true;
node.declarations.forEach(declarator => {
extractNames(declarator.id).forEach(name => {
if (exported.has(name)) {
has_exports = true;
} else {
has_only_exports = false;
}
});
});
if (has_only_exports) {
if (current_group && current_group[current_group.length - 1].kind !== node.kind) {
current_group = null;
}
// rewrite as a group, later
if (!current_group) {
current_group = [];
coalesced_declarations.push(current_group);
}
current_group.push(node);
} else {
if (has_exports) {
// rewrite in place
throw new Error('TODO rewrite prop declaration in place');
}
current_group = null;
}
}
} else {
current_group = null;
}
},
leave(node) {
if (map.has(node)) {
scope = scope.parent;
}
}
});
coalesced_declarations.forEach(group => {
const kind = group[0].kind;
let replacement = '';
let combining = false;
group.forEach(node => {
node.declarations.forEach(({ id, init }) => {
if (id.type === 'Identifier') {
const value = init
? this.code.slice(id.start, init.end)
: this.code.slice(id.start, id.end);
if (combining) {
replacement += `, ${value}`;
} else {
replacement += `${kind} { ${value}`;
combining = true;
}
} else {
throw new Error('TODO destructured declarations');
}
});
});
if (combining) {
replacement += ' } = $$props;';
}
this.code.overwrite(group[0].start, group[group.length - 1].end, replacement);
});
}
warn_if_undefined(node, template_scope: TemplateScope) {
const { name } = node;
if (this.module_scope && this.module_scope.declarations.has(name)) return;

@ -177,15 +177,14 @@ export default function dom(
${component.fully_hoisted.length > 0 && component.fully_hoisted.join('\n\n')}
function define($$self, $$make_dirty) {
${should_add_css &&
`if (!document.getElementById("${component.stylesheet.id}-style")) @add_css();`}
${component.javascript || component.props.map(x => `let ${x.name};`)}
function ${component.alias('define')}($$self, $$props, $$make_dirty) {
${component.javascript || (
component.props.length > 0 &&
`let { ${component.props.map(x => x.name === x.as ? x.as : `${x.as}: ${x.name}`).join(', ')} } = $$props;`
)}
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
// TODO only what's needed by the template
$$self.$$.get = () => ({ ${component.declarations.join(', ')} });
${set && `$$self.$$.set = ${set};`}
@ -202,7 +201,7 @@ export default function dom(
${css.code && `this.shadowRoot.innerHTML = \`<style>${escape(css.code, { onlyEscapeAtSymbol: true }).replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot }, define, create_fragment, ${not_equal});
@init(this, { target: this.shadowRoot }, ${component.alias('define')}, create_fragment, ${not_equal});
${dev_props_check}
@ -232,6 +231,7 @@ export default function dom(
class ${name} extends ${superclass} {
constructor(options) {
super(${options.dev && `options`});
${should_add_css && `if (!document.getElementById("${component.stylesheet.id}-style")) @add_css();`}
@init(this, options, define, create_fragment, ${not_equal});
${dev_props_check}

@ -75,6 +75,7 @@ function esm(
${importBlock}
${code}
export default ${name};
${module_exports.length > 0 && `export { ${module_exports.map(e => e.name === e.as ? e.name : `${e.name} as ${e.as}`).join(', ')} };`}`;
}

@ -3,7 +3,7 @@ import * as fs from "fs";
import * as path from "path";
import { loadConfig, svelte } from "../helpers.js";
describe("js", () => {
describe.only("js", () => {
fs.readdirSync("test/js/samples").forEach(dir => {
if (dir[0] === ".") return;

@ -47,8 +47,6 @@ function create_fragment(component, ctx) {
}
function define($$self, $$props, $$make_dirty) {
if (!document.getElementById("svelte-1a7i8ec-style")) add_css();
let { foo = 42 } = $$props;
$$self.$$.get = () => ({ foo });
@ -61,6 +59,7 @@ function define($$self, $$props, $$make_dirty) {
class SvelteComponent extends SvelteComponent_1 {
constructor(options) {
super();
if (!document.getElementById("svelte-1a7i8ec-style")) add_css();
init(this, options, define, create_fragment, safe_not_equal);
}
@ -73,4 +72,5 @@ class SvelteComponent extends SvelteComponent_1 {
flush();
}
}
export default SvelteComponent;

@ -54,7 +54,7 @@ function create_fragment(component, ctx) {
};
}
function define($$self, $$make_dirty) {
function define($$self, $$props, $$make_dirty) {
let { y } = $$props;
function onwindowscroll() {
@ -64,7 +64,6 @@ function define($$self, $$make_dirty) {
window_updating = false;
}
// TODO only what's needed by the template
$$self.$$.get = () => ({ y, onwindowscroll });
$$self.$$.set = $$props => {

Loading…
Cancel
Save