diff --git a/CHANGELOG.md b/CHANGELOG.md index 24ae2f5d28..4e24c11d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,21 @@ # Svelte changelog -## Unreleased - - -* Support attached sourcemaps as magic comment inside code from preprocessors ([#5854](https://github.com/sveltejs/svelte/pull/5854)) +## 3.32.1 + +* Warn when using `module` variables reactively, and close weird reactivity loophole ([#5847](https://github.com/sveltejs/svelte/pull/5847)) +* Throw a parser error for `class:` directives with an empty class name ([#5858](https://github.com/sveltejs/svelte/issues/5858)) +* Fix extraneous store subscription in SSR mode ([#5883](https://github.com/sveltejs/svelte/issues/5883)) +* Don't emit update code for `class:` directives whose expression is not dynamic ([#5919](https://github.com/sveltejs/svelte/issues/5919)) +* Fix type inference for derived stores ([#5935](https://github.com/sveltejs/svelte/pull/5935)) +* Make parameters of built-in animations and transitions optional ([#5936](https://github.com/sveltejs/svelte/pull/5936)) +* Make `SvelteComponentDev` typings more forgiving ([#5937](https://github.com/sveltejs/svelte/pull/5937)) +* Fix `foreign` elements incorrectly disallowing `bind:this` ([#5942](https://github.com/sveltejs/svelte/pull/5942)) + +## 3.32.0 + +* Allow multiple instances of the same action on an element ([#5516](https://github.com/sveltejs/svelte/issues/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/package-lock.json b/package-lock.json index 186c738181..482acd470a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.31.2", + "version": "3.32.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4a5449c548..1c88873065 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.31.2", + "version": "3.32.1", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index 6f1a2da61e..2c665376dc 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1292,19 +1292,19 @@ Components can have child content, in the same way that elements can. The content is exposed in the child component using the `` element, which can contain fallback content that is rendered if no children are provided. ```sv - - - - -

this is some child content that will overwrite the default slot content

-
-
this fallback content will be rendered when no content is provided, like in the first example
+ + + + + +

this is some child content that will overwrite the default slot content

+
``` #### [``](slot_name) @@ -1314,18 +1314,18 @@ The content is exposed in the child component using the `` element, which Named slots allow consumers to target specific areas. They can also have fallback content. ```sv - - -

Hello

-

Copyright (c) 2019 Svelte Industries

-
-
No header was provided

Some content between header and footer

+ + + +

Hello

+

Copyright (c) 2019 Svelte Industries

+
``` #### [`$$slots`](slots_object) @@ -1337,20 +1337,21 @@ Named slots allow consumers to target specific areas. They can also have fallbac Note that explicitly passing in an empty named slot will add that slot's name to `$$slots`. For example, if a parent passes `
` to a child component, `$$slots.title` will be truthy within the child. ```sv - - -

Blog Post Title

-
-
{#if $$slots.description} - +
{/if}
+ + + +

Blog Post Title

+ +
``` #### [``](slot_let) @@ -1362,11 +1363,6 @@ Slots can be rendered zero or more times, and can pass values *back* to the pare The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}`, and `` is equivalent to ``. ```sv - - -
{thing.text}
-
-
    {#each items as item} @@ -1375,6 +1371,11 @@ The usual shorthand rules apply — `let:item` is equivalent to `let:item={item} {/each}
+ + + +
{thing.text}
+
``` --- @@ -1382,12 +1383,6 @@ The usual shorthand rules apply — `let:item` is equivalent to `let:item={item} Named slots can also expose values. The `let:` directive goes on the element with the `slot` attribute. ```sv - - -
{item.text}
-

Copyright (c) 2019 Svelte Industries

-
-
    {#each items as item} @@ -1398,6 +1393,12 @@ Named slots can also expose values. The `let:` directive goes on the element wit
+ + + +
{item.text}
+

Copyright (c) 2019 Svelte Industries

+
``` @@ -1530,7 +1531,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 b2c8820351..8aab2b4898 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -29,7 +29,7 @@ import add_to_set from './utils/add_to_set'; import check_graph_for_cycles from './utils/check_graph_for_cycles'; import { print, x, b } from 'code-red'; import { is_reserved_keyword } from './utils/reserved_keywords'; -import { apply_preprocessor_sourcemap } from '../utils/string_with_sourcemap'; +import { apply_preprocessor_sourcemap } from '../utils/mapped_code'; import Element from './nodes/Element'; import { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping/dist/types/types'; @@ -1175,15 +1175,20 @@ export default class Component { extract_reactive_declarations() { const component = this; - const unsorted_reactive_declarations = []; + const unsorted_reactive_declarations: Array<{ + assignees: Set; + dependencies: Set; + node: Node; + declaration: Node; + }> = []; this.ast.instance.content.body.forEach(node => { if (node.type === 'LabeledStatement' && node.label.name === '$') { this.reactive_declaration_nodes.add(node); - const assignees = new Set(); + const assignees = new Set(); const assignee_nodes = new Set(); - const dependencies = new Set(); + const dependencies = new Set(); let scope = this.instance_scope; const map = this.instance_scope_map; @@ -1214,10 +1219,22 @@ export default class Component { const { name } = identifier; const owner = scope.find_owner(name); const variable = component.var_lookup.get(name); - if (variable) variable.is_reactive_dependency = true; + let should_add_as_dependency = true; + + if (variable) { + variable.is_reactive_dependency = true; + if (variable.module) { + should_add_as_dependency = false; + component.warn(node as any, { + code: 'module-script-reactive-declaration', + message: `"${name}" is declared in a module script and will not be reactive` + }); + } + } const is_writable_or_mutated = variable && (variable.writable || variable.mutated); if ( + should_add_as_dependency && (!owner || owner === component.instance_scope) && (name[0] === '$' || is_writable_or_mutated) ) { @@ -1349,7 +1366,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..07f1739018 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