custom elements sort of working

pull/1864/head
Rich Harris 7 years ago
parent ae3a7dd369
commit 5030385b1b

@ -133,7 +133,7 @@ export default class Component {
this.walk_module_js();
this.walk_instance_js();
this.name = this.alias(name);
this.name = this.getUniqueName(name);
this.meta = process_meta(this, this.ast.html.children);
this.namespace = namespaces[this.meta.namespace] || this.meta.namespace;
@ -209,7 +209,7 @@ export default class Component {
});
const importedHelpers = Array.from(helpers)
.concat(options.dev ? '$$ComponentDev' : '$$Component')
.concat(options.dev ? 'SvelteComponentDev' : 'SvelteComponent')
.sort()
.map(name => {
const alias = this.alias(name);

@ -58,38 +58,9 @@ export default function dom(
const expectedProperties = Array.from(component.expectedProperties);
const globals = expectedProperties.filter(prop => globalWhitelist.has(prop));
if (component.customElement) {
// TODO use `export` to determine this
const props = Array.from(component.expectedProperties);
builder.addBlock(deindent`
class ${name} extends HTMLElement {
constructor(options = {}) {
super();
}
static get observedAttributes() {
return ${JSON.stringify(props)};
}
${renderer.slots.size && deindent`
connectedCallback() {
Object.keys(this.$$.slotted).forEach(key => {
this.appendChild(this.$$.slotted[key]);
});
}`}
attributeChangedCallback(attr, oldValue, newValue) {
this[attr] = newValue;
}
}
customElements.define("${component.customElement.tag}", ${name});
`);
} else {
const refs = Array.from(component.refs);
const superclass = component.alias(options.dev ? '$$ComponentDev' : '$$Component');
const superclass = component.alias(options.dev ? 'SvelteComponentDev' : 'SvelteComponent');
if (options.dev && !options.hydratable) {
block.builders.claim.addLine(
@ -135,25 +106,6 @@ export default function dom(
const not_equal = component.options.immutable ? `@not_equal` : `@safe_not_equal`;
let dev_props_check;
if (component.options.dev) {
// TODO check no uunexpected props were passed, as well as
// checking that expected ones were passed
const expected = component.exports
.map(x => x.name)
.filter(name => !component.initialised_declarations.has(name));
if (expected.length) {
dev_props_check = deindent`
const state = this.$$.get();
${expected.map(name => deindent`
if (state.${name} === undefined) {
console.warn("${debug_name} was created without expected data property '${name}'");
}`)}
`;
}
}
component.exports.forEach(x => {
body.push(deindent`
get ${x.as}() {
@ -177,6 +129,25 @@ export default function dom(
}
});
if (component.options.dev) {
// TODO check no uunexpected props were passed, as well as
// checking that expected ones were passed
const expected = component.exports
.map(x => x.name)
.filter(name => !component.initialised_declarations.has(name));
if (expected.length) {
dev_props_check = deindent`
const state = this.$$.get();
${expected.map(name => deindent`
if (state.${name} === undefined) {
console.warn("${debug_name} was created without expected data property '${name}'");
}`)}
`;
}
}
builder.addBlock(deindent`
function create_fragment(${component.alias('component')}, ctx) {
${block.getContents()}
@ -201,7 +172,29 @@ export default function dom(
${inject_refs && `$$self.$$.inject_refs = ${inject_refs};`}
}
`);
if (component.customElement) {
// TODO observedAttributes
builder.addBlock(deindent`
class ${name} extends @SvelteElement {
constructor() {
super();
@init(this, { target: this.shadowRoot }, define, create_fragment, ${not_equal});
}
static get observedAttributes() {
return [];
}
${body.join('\n\n')}
}
customElements.define("${component.customElement.tag}", ${name});
`);
} else {
builder.addBlock(deindent`
class ${name} extends ${superclass} {
constructor(options) {
super(${options.dev && `options`});

@ -110,7 +110,53 @@ export function init(component, options, define, create_fragment, not_equal) {
set_current_component(previous_component);
}
export class $$Component {
export let SvelteElement;
if (typeof HTMLElement !== 'undefined') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
for (let key in this.$$.slotted) {
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, oldValue, newValue) {
this[attr] = newValue;
}
$destroy() {
destroy(this, true);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1) callbacks.splice(index, 1);
};
}
$set(values) {
if (this.$$) {
const state = this.$$.get();
this.$$.set(values);
for (const key in values) {
if (this.$$.not_equal(state[key], values[key])) make_dirty(this, key);
}
}
}
}
}
export class SvelteComponent {
$destroy() {
destroy(this, true);
this.$destroy = noop;
@ -137,7 +183,7 @@ export class $$Component {
}
}
export class $$ComponentDev extends $$Component {
export class SvelteComponentDev extends SvelteComponent {
constructor(options) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`);

@ -15,7 +15,7 @@ const page = `
const assert = fs.readFileSync('test/custom-elements/assert.js', 'utf-8');
describe('custom-elements', function() {
describe.only('custom-elements', function() {
this.timeout(10000);
let svelte;
@ -104,12 +104,13 @@ describe('custom-elements', function() {
})
.then(result => {
if (result) console.log(result);
nightmare.end();
})
.catch(message => {
console.log(addLineNumbers(bundle));
nightmare.end();
throw new Error(message);
})
.then(() => nightmare.end());
});
});

@ -1,11 +1,11 @@
import * as assert from 'assert';
import './main.html';
export default function (target) {
target.innerHTML = '<custom-element name="world"></custom-element>';
export default async function (target) {
target.innerHTML = '<custom-element></custom-element>';
const el = target.querySelector('custom-element');
el.updateFoo(42);
await el.updateFoo(42);
const p = el.shadowRoot.querySelector('p');
assert.equal(p.textContent, '42');

@ -25,7 +25,7 @@ function getName(filename) {
return base[0].toUpperCase() + base.slice(1);
}
describe.only("runtime", () => {
describe("runtime", () => {
before(() => {
svelte = loadSvelte(false);
svelte$ = loadSvelte(true);

Loading…
Cancel
Save