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

chore: custom elements validation #10720

Merged
merged 2 commits into from
Mar 7, 2024
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
5 changes: 5 additions & 0 deletions .changeset/kind-spoons-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

chore: custom elements validation
10 changes: 7 additions & 3 deletions packages/svelte/src/compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ export function compile(source, options) {
const validated = validate_component_options(options, '');
let parsed = _parse(source);

const combined_options = /** @type {import('#compiler').ValidatedCompileOptions} */ ({
const { customElement: customElementOptions, ...parsed_options } = parsed.options || {};

/** @type {import('#compiler').ValidatedCompileOptions} */
const combined_options = {
...validated,
...parsed.options
});
...parsed_options,
customElementOptions
};

if (parsed.metadata.ts) {
parsed = {
Expand Down
5 changes: 5 additions & 0 deletions packages/svelte/src/compiler/phases/1-parse/read/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export default function read_options(node) {
component_options.customElement = ce;
break;
} else if (value[0].expression.type !== 'ObjectExpression') {
// Before Svelte 4 it was necessary to explicitly set customElement to null or else you'd get a warning.
// This is no longer necessary, but for backwards compat just skip in this case now.
if (value[0].expression.type === 'Literal' && value[0].expression.value === null) {
break;
}
error(attribute, 'invalid-svelte-option-customElement');
}

Expand Down
8 changes: 6 additions & 2 deletions packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,8 @@ export function analyze_component(root, options) {
uses_rest_props: false,
uses_slots: false,
uses_component_bindings: false,
custom_element: options.customElement,
inject_styles: options.css === 'injected' || !!options.customElement,
custom_element: options.customElementOptions ?? options.customElement,
inject_styles: options.css === 'injected' || options.customElement,
accessors: options.customElement
? true
: !!options.accessors ||
Expand All @@ -399,6 +399,10 @@ export function analyze_component(root, options) {
}
};

if (!options.customElement && root.options?.customElement) {
warn(analysis.warnings, root.options, [], 'missing-custom-element-compile-option');
}

if (analysis.runes) {
const props_refs = module.scope.references.get('$$props');
if (props_refs) {
Expand Down
3 changes: 2 additions & 1 deletion packages/svelte/src/compiler/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { SourceMap } from 'magic-string';
import type { Context } from 'zimmerframe';
import type { Scope } from '../phases/scope.js';
import * as Css from './css.js';
import type { EachBlock, Namespace, SvelteNode } from './template.js';
import type { EachBlock, Namespace, SvelteNode, SvelteOptions } from './template.js';

/** The return value of `compile` from `svelte/compiler` */
export interface CompileResult {
Expand Down Expand Up @@ -224,6 +224,7 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions &
sourcemap: CompileOptions['sourcemap'];
legacy: Required<Required<CompileOptions>['legacy']>;
runes: CompileOptions['runes'];
customElementOptions: SvelteOptions['customElement'];
};

export type DeclarationKind =
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/compiler/types/template.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export interface Root extends BaseNode {
}

export interface SvelteOptions {
// start/end info (needed for Prettier, when someone wants to keep the options where they are)
// start/end info (needed for warnings and for our Prettier plugin)
start: number;
end: number;
// options
Expand Down
8 changes: 7 additions & 1 deletion packages/svelte/src/compiler/warnings.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ const block = {
'empty-block': () => 'Empty block'
};

const options = {
'missing-custom-element-compile-option': () =>
"The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?"
};

/** @satisfies {Warnings} */
const warnings = {
...css,
Expand All @@ -247,7 +252,8 @@ const warnings = {
...state,
...components,
...legacy,
...block
...block,
...options
};

/** @typedef {typeof warnings} AllWarnings */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { test } from '../../assert';
import { mount } from 'svelte';
const tick = () => Promise.resolve();

export default test({
skip: true, // TODO: decide if we want to keep the customElement={null} behavior (warning about not having set the tag when in ce mode, and disabling that this way)

async test({ assert, target, component: Component }) {
const component = new Component({ target, props: { name: 'slot' } });
async test({ assert, target, componentCtor: Component }) {
const component = mount(Component, { target, props: { name: 'slot' } });
await tick();
await tick();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- before Svelte 4 it was necessary to explicitly set customElement to null or else you'd get a warning. Keep this around for backwards compat -->
<svelte:options customElement={null} />

<script>
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[
{
"code": "missing-custom-element-compile-options",
"code": "missing-custom-element-compile-option",
"message": "The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?",
"start": {
"line": 1,
"column": 16
"column": 0
},
"end": {
"line": 1,
"column": 46
"column": 49
}
}
]
2 changes: 1 addition & 1 deletion packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ declare module 'svelte/compiler' {
}

interface SvelteOptions {
// start/end info (needed for Prettier, when someone wants to keep the options where they are)
// start/end info (needed for warnings and for our Prettier plugin)
start: number;
end: number;
// options
Expand Down
Loading