FIX #2417: allows custom element to be defined without a tag

* warning given on compile if tag is absent
* no warning if tag is set to `null`
pull/2749/head
Colin Casey 6 years ago
parent 438acdc09a
commit 690f163e88

@ -152,10 +152,14 @@ export default class Component {
this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace; this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace;
if (compile_options.customElement) { if (compile_options.customElement) {
this.tag = this.component_options.tag || compile_options.tag; if (this.component_options.tag === undefined && compile_options.tag === undefined) {
if (!this.tag) { const svelteOptions = ast.html.children.find(child => child.name === 'svelte:options');
throw new Error(`Cannot compile to a custom element without specifying a tag name via options.tag or <svelte:options>`); this.warn(svelteOptions, {
code: 'custom-element-no-tag',
message: `No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/>`
});
} }
this.tag = this.component_options.tag || compile_options.tag;
} else { } else {
this.tag = this.name; this.tag = this.name;
} }
@ -1265,9 +1269,9 @@ function process_component_options(component: Component, nodes) {
const message = `'tag' must be a string literal`; const message = `'tag' must be a string literal`;
const tag = get_value(attribute, code, message); const tag = get_value(attribute, code, message);
if (typeof tag !== 'string') component.error(attribute, { code, message }); if (typeof tag !== 'string' && tag !== null) component.error(attribute, { code, message });
if (!/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
component.error(attribute, { component.error(attribute, {
code: `invalid-tag-property`, code: `invalid-tag-property`,
message: `tag name must be two or more words joined by the '-' character` message: `tag name must be two or more words joined by the '-' character`

@ -462,9 +462,13 @@ export default function dom(
${body.length > 0 && body.join('\n\n')} ${body.length > 0 && body.join('\n\n')}
} }
customElements.define("${component.tag}", ${name});
`); `);
if (component.tag != null) {
builder.add_block(deindent`
customElements.define("${component.tag}", ${name});
`);
}
} else { } else {
const superclass = options.dev ? 'SvelteComponentDev' : 'SvelteComponent'; const superclass = options.dev ? 'SvelteComponentDev' : 'SvelteComponent';

@ -5,6 +5,7 @@ import { rollup } from 'rollup';
import * as virtual from 'rollup-plugin-virtual'; import * as virtual from 'rollup-plugin-virtual';
import * as puppeteer from 'puppeteer'; import * as puppeteer from 'puppeteer';
import { addLineNumbers, loadConfig, loadSvelte } from "../helpers.js"; import { addLineNumbers, loadConfig, loadSvelte } from "../helpers.js";
import { deepEqual } from 'assert';
const page = ` const page = `
<body> <body>
@ -59,9 +60,11 @@ describe('custom-elements', function() {
const skip = /\.skip$/.test(dir); const skip = /\.skip$/.test(dir);
const internal = path.resolve('internal.mjs'); const internal = path.resolve('internal.mjs');
const index = path.resolve('index.mjs'); const index = path.resolve('index.mjs');
const warnings = [];
(solo ? it.only : skip ? it.skip : it)(dir, async () => { (solo ? it.only : skip ? it.skip : it)(dir, async () => {
const config = loadConfig(`./custom-elements/samples/${dir}/_config.js`); const config = loadConfig(`./custom-elements/samples/${dir}/_config.js`);
const expected_warnings = config.warnings || [];
const bundle = await rollup({ const bundle = await rollup({
input: `test/custom-elements/samples/${dir}/test.js`, input: `test/custom-elements/samples/${dir}/test.js`,
@ -84,6 +87,8 @@ describe('custom-elements', function() {
dev: config.dev dev: config.dev
}); });
compiled.warnings.forEach(w => warnings.push(w));
return compiled.js; return compiled.js;
} }
} }
@ -112,6 +117,16 @@ describe('custom-elements', function() {
} catch (err) { } catch (err) {
console.log(addLineNumbers(code)); console.log(addLineNumbers(code));
throw err; throw err;
} finally {
if (expected_warnings) {
deepEqual(warnings.map(w => ({
code: w.code,
message: w.message,
pos: w.pos,
start: w.start,
end: w.end
})), expected_warnings);
}
} }
}); });
}); });

@ -0,0 +1,17 @@
export default {
warnings: [{
code: "custom-element-no-tag",
message: "No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag=\"my-thing\"/>. To hide this warning, use <svelte:options tag={null}/>",
pos: 0,
start: {
character: 0,
column: 0,
line: 1
},
end: {
character: 18,
column: 18,
line: 1
}
}]
};

@ -0,0 +1,7 @@
<svelte:options />
<script>
export let name;
</script>
<h1>Hello {name}!</h1>

@ -0,0 +1,12 @@
import * as assert from 'assert';
import CustomElement from './main.svelte';
export default function (target) {
customElements.define('no-tag', CustomElement);
target.innerHTML = `<no-tag name="world"></no-tag>`;
const el = target.querySelector('no-tag');
const h1 = el.shadowRoot.querySelector('h1');
assert.equal(h1.textContent, 'Hello world!');
}

@ -0,0 +1,3 @@
export default {
warnings: []
};

@ -0,0 +1,7 @@
<svelte:options tag={null} />
<script>
export let name;
</script>
<h1>Hello {name}!</h1>

@ -0,0 +1,12 @@
import * as assert from 'assert';
import CustomElement from './main.svelte';
export default function (target) {
customElements.define('no-tag', CustomElement);
target.innerHTML = `<no-tag name="world"></no-tag>`;
const el = target.querySelector('no-tag');
const h1 = el.shadowRoot.querySelector('h1');
assert.equal(h1.textContent, 'Hello world!');
}
Loading…
Cancel
Save