diff --git a/CHANGELOG.md b/CHANGELOG.md index f4f648916f..53175a203c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Allow multiple instances of the same action on an element ([#5516](https://github.com/sveltejs/svelte/pull/5516)) +* Support `foreign` namespace, which disables certain HTML5-specific behaviour and checks ([#5652](https://github.com/sveltejs/svelte/pull/5652)) * Support inline comment sourcemaps in code from preprocessors ([#5854](https://github.com/sveltejs/svelte/pull/5854)) ## 3.31.2 diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index 6f1a2da61e..7288b8e8dd 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1530,7 +1530,7 @@ The `` element provides a place to specify per-component compile * `immutable={false}` — the default. Svelte will be more conservative about whether or not mutable objects have changed * `accessors={true}` — adds getters and setters for the component's props * `accessors={false}` — the default -* `namespace="..."` — the namespace where this component will be used, most commonly "svg" +* `namespace="..."` — the namespace where this component will be used, most commonly "svg"; use the "foreign" namespace to opt out of case-insensitive attribute names and HTML-specific warnings * `tag="..."` — the name to use when compiling this component as a custom element ```sv diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/04-compile-time.md index 9fa0f92195..48f3ae2c4e 100644 --- a/site/content/docs/04-compile-time.md +++ b/site/content/docs/04-compile-time.md @@ -80,7 +80,7 @@ The following options can be passed to the compiler. None are required: | `outputFilename` | `null` | A `string` used for your JavaScript sourcemap. | `cssOutputFilename` | `null` | A `string` used for your CSS sourcemap. | `sveltePath` | `"svelte"` | The location of the `svelte` package. Any imports from `svelte` or `svelte/[module]` will be modified accordingly. - +| `namespace` | `"html"` | The namespace of the element; e.g., `"mathml"`, `"svg"`, `"foreign"`. --- diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 5fb81e7457..d2b22c65e1 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1349,7 +1349,8 @@ function process_component_options(component: Component, nodes) { 'accessors' in component.compile_options ? component.compile_options.accessors : !!component.compile_options.customElement, - preserveWhitespace: !!component.compile_options.preserveWhitespace + preserveWhitespace: !!component.compile_options.preserveWhitespace, + namespace: component.compile_options.namespace }; const node = nodes.find(node => node.name === 'svelte:options'); diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index 842539fcde..9f9b31917e 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -6,6 +6,7 @@ import { CompileOptions, Warning } from '../interfaces'; import Component from './Component'; import fuzzymatch from '../utils/fuzzymatch'; import get_name_from_filename from './utils/get_name_from_filename'; +import { valid_namespaces } from '../utils/namespaces'; const valid_options = [ 'format', @@ -22,6 +23,7 @@ const valid_options = [ 'hydratable', 'legacy', 'customElement', + 'namespace', 'tag', 'css', 'loopGuardTimeout', @@ -30,7 +32,7 @@ const valid_options = [ ]; function validate_options(options: CompileOptions, warnings: Warning[]) { - const { name, filename, loopGuardTimeout, dev } = options; + const { name, filename, loopGuardTimeout, dev, namespace } = options; Object.keys(options).forEach(key => { if (!valid_options.includes(key)) { @@ -65,6 +67,15 @@ function validate_options(options: CompileOptions, warnings: Warning[]) { toString: () => message }); } + + if (namespace && valid_namespaces.indexOf(namespace) === -1) { + const match = fuzzymatch(namespace, valid_namespaces); + if (match) { + throw new Error(`Invalid namespace '${namespace}' (did you mean '${match}'?)`); + } else { + throw new Error(`Invalid namespace '${namespace}'`); + } + } } export default function compile(source: string, options: CompileOptions = {}) { diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index b12e616f4d..8c5b4cb9f5 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -136,44 +136,45 @@ export default class Element extends Node { this.namespace = get_namespace(parent as Element, this, component.namespace); - if (this.name === 'textarea') { - if (info.children.length > 0) { - const value_attribute = info.attributes.find(node => node.name === 'value'); - if (value_attribute) { - component.error(value_attribute, { - code: 'textarea-duplicate-value', - message: 'A