chore: custom elements validation (#10720)

- add "missing customElement option" warning
- add backwards compat support for customElement={null}
pull/10721/head
Simon H 2 years ago committed by GitHub
parent 6a01f48325
commit fff3320517
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
chore: custom elements validation

@ -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 = {

@ -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');
}

@ -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 ||
@ -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) {

@ -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 {
@ -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 =

@ -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

@ -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,
@ -247,7 +252,8 @@ const warnings = {
...state,
...components,
...legacy,
...block
...block,
...options
};
/** @typedef {typeof warnings} AllWarnings */

@ -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();

@ -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>

@ -1,3 +0,0 @@
import { test } from '../../test';
export default test({ skip: true });

@ -1,14 +0,0 @@
[
{
"code": "missing-custom-element-compile-options",
"end": {
"column": 46,
"line": 1
},
"message": "The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?",
"start": {
"column": 16,
"line": 1
}
}
]

@ -1,3 +0,0 @@
import { test } from '../../test';
export default test({ skip: true });

@ -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
}
}
]

@ -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

Loading…
Cancel
Save