Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows custom element to be defined without a tag #2749

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ export default class Component {
this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace;

if (compile_options.customElement) {
this.tag = this.component_options.tag || compile_options.tag;
if (!this.tag) {
throw new Error(`Cannot compile to a custom element without specifying a tag name via options.tag or <svelte:options>`);
if (this.component_options.tag === undefined && compile_options.tag === undefined) {
const svelteOptions = ast.html.children.find(child => child.name === '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 {
this.tag = this.name;
}
Expand Down Expand Up @@ -1265,9 +1269,9 @@ function process_component_options(component: Component, nodes) {
const message = `'tag' must be a string literal`;
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, {
code: `invalid-tag-property`,
message: `tag name must be two or more words joined by the '-' character`
Expand Down
8 changes: 6 additions & 2 deletions src/compile/render-dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,13 @@ export default function dom(

${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 {
const superclass = options.dev ? 'SvelteComponentDev' : 'SvelteComponent';

Expand Down
15 changes: 15 additions & 0 deletions test/custom-elements/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { rollup } from 'rollup';
import * as virtual from 'rollup-plugin-virtual';
import * as puppeteer from 'puppeteer';
import { addLineNumbers, loadConfig, loadSvelte } from "../helpers.js";
import { deepEqual } from 'assert';

const page = `
<body>
Expand Down Expand Up @@ -59,9 +60,11 @@ describe('custom-elements', function() {
const skip = /\.skip$/.test(dir);
const internal = path.resolve('internal.mjs');
const index = path.resolve('index.mjs');
const warnings = [];

(solo ? it.only : skip ? it.skip : it)(dir, async () => {
const config = loadConfig(`./custom-elements/samples/${dir}/_config.js`);
const expected_warnings = config.warnings || [];

const bundle = await rollup({
input: `test/custom-elements/samples/${dir}/test.js`,
Expand All @@ -84,6 +87,8 @@ describe('custom-elements', function() {
dev: config.dev
});

compiled.warnings.forEach(w => warnings.push(w));

return compiled.js;
}
}
Expand Down Expand Up @@ -112,6 +117,16 @@ describe('custom-elements', function() {
} catch (err) {
console.log(addLineNumbers(code));
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);
}
}
});
});
Expand Down
17 changes: 17 additions & 0 deletions test/custom-elements/samples/no-tag-warning/_config.js
Original file line number Diff line number Diff line change
@@ -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
}
}]
};
7 changes: 7 additions & 0 deletions test/custom-elements/samples/no-tag-warning/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<svelte:options />

<script>
export let name;
</script>

<h1>Hello {name}!</h1>
12 changes: 12 additions & 0 deletions test/custom-elements/samples/no-tag-warning/test.js
Original file line number Diff line number Diff line change
@@ -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!');
}
3 changes: 3 additions & 0 deletions test/custom-elements/samples/no-tag/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
warnings: []
};
7 changes: 7 additions & 0 deletions test/custom-elements/samples/no-tag/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<svelte:options tag={null} />

<script>
export let name;
</script>

<h1>Hello {name}!</h1>
12 changes: 12 additions & 0 deletions test/custom-elements/samples/no-tag/test.js
Original file line number Diff line number Diff line change
@@ -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!');
}