diff --git a/.eslintignore b/.eslintignore index b5cb03ae6e..d123c10530 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,7 +2,6 @@ **/expected.js _output test/*/samples/*/output.js -node_modules # automatically generated internal_exports.ts diff --git a/.eslintrc.js b/.eslintrc.js index 946a157e40..a093de610b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,65 +1,6 @@ module.exports = { root: true, - rules: { - indent: 'off', - 'no-unused-vars': 'off', - semi: [2, 'always'], - 'keyword-spacing': [2, { before: true, after: true }], - 'space-before-blocks': [2, 'always'], - 'no-mixed-spaces-and-tabs': [2, 'smart-tabs'], - 'no-cond-assign': 0, - 'object-shorthand': [2, 'always'], - 'no-const-assign': 2, - 'no-class-assign': 2, - 'no-this-before-super': 2, - 'no-var': 2, - 'no-unreachable': 2, - 'valid-typeof': 2, - 'quote-props': [2, 'as-needed'], - 'one-var': [2, 'never'], - 'prefer-arrow-callback': 2, - 'prefer-const': [2, { destructuring: 'all' }], - 'arrow-spacing': 2, - 'no-inner-declarations': 0, - 'require-atomic-updates': 'off', - '@typescript-eslint/indent': 'off', - '@typescript-eslint/camelcase': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/array-type': ['error', 'array-simple'], - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/explicit-member-accessibility': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_' - } - ], - '@typescript-eslint/no-object-literal-type-assertion': 'off', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/prefer-interface': 'off' - }, - globals: { - globalThis: false - }, - env: { - es6: true, - browser: true, - node: true, - mocha: true - }, - extends: [ - 'eslint:recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:@typescript-eslint/recommended' - ], - parserOptions: { - ecmaVersion: 9, - sourceType: 'module' - }, - plugins: ['svelte3'], + extends: '@sveltejs', settings: { 'import/core-modules': [ 'svelte', @@ -69,20 +10,5 @@ module.exports = { 'estree' ], 'svelte3/compiler': require('./compiler') - }, - overrides: [ - { - files: ['*.js'], - rules: { - '@typescript-eslint/no-var-requires': 'off' - } - }, - { - files: ['*.svelte'], - processor: 'svelte3/svelte3', - rules: { - '@typescript-eslint/indent': 'off' - } - } - ] + } }; diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1e77e0337a..7bd900ac33 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,8 @@ ### Before submitting the PR, please make sure you do the following -- [ ] It's really useful if your PR relates to an outstanding issue, so please reference it in your PR, or create an explanatory one for discussion. In many cases features are absent for a reason. +- [ ] It's really useful if your PR relates to an outstanding issue, so please reference it in your PR, or create an explanatory one for discussion. In many cases, features are absent for a reason. - [ ] This message body should clearly illustrate what problems it solves. If there are related issues, remember to reference them. - [ ] Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI. (Remember to `npm run lint`!) ### Tests -- [ ] Run the tests tests with `npm test` or `yarn test`) +- [ ] Run the tests with `npm test` or `yarn test`) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14824ecdfa..4e04386fcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,12 @@ on: [push, pull_request] jobs: Tests: runs-on: ${{ matrix.os }} + timeout-minutes: 10 strategy: matrix: - node-version: [8, 10, 12] + node-version: [8, 10, 12, 14] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - run: git config --global core.autocrlf false - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: @@ -19,12 +19,17 @@ jobs: CI: true Lint: runs-on: ubuntu-latest + timeout-minutes: 2 steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 - run: 'npm i && npm run lint' Unit: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 diff --git a/.gitignore b/.gitignore index 7a14244929..f7fac04eba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea .DS_Store .nyc_output +.vscode node_modules *.map /src/compiler/compile/internal_exports.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f1564848b5..61b4c36842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,114 @@ # Svelte changelog +## Unreleased + +* Fix reactivity when passing `$$props` to a `` ([#3364](https://github.com/sveltejs/svelte/issues/3364)) +* Fix unneeded invalidation of `$$props` and `$$restProps` ([#4993](https://github.com/sveltejs/svelte/issues/4993), [#5118](https://github.com/sveltejs/svelte/issues/5118)) + +## 3.24.0 + +* Support nullish coalescing (`??`) and optional chaining (`?.`) operators ([#1972](https://github.com/sveltejs/svelte/issues/1972)) +* Support `import.meta` ([#4379](https://github.com/sveltejs/svelte/issues/4379)) +* Fix only setting `` values when they're changed when there are spread attributes ([#4418](https://github.com/sveltejs/svelte/issues/4418)) +* Fix placement of `{@html}` when used at the root of a slot, at the root of a component, or in `` ([#5012](https://github.com/sveltejs/svelte/issues/5012), [#5071](https://github.com/sveltejs/svelte/pull/5071)) +* Fix certain handling of two-way bound `contenteditable` elements ([#5018](https://github.com/sveltejs/svelte/issues/5018)) +* Fix handling of `import`ed value that is used as a store and is also mutated ([#5019](https://github.com/sveltejs/svelte/issues/5019)) +* Do not display `a11y-missing-content` warning on elements with `contenteditable` bindings ([#5020](https://github.com/sveltejs/svelte/issues/5020)) +* Fix handling of `this` in inline function expressions in the template ([#5033](https://github.com/sveltejs/svelte/issues/5033)) +* Fix collapsing HTML with static content ([#5040](https://github.com/sveltejs/svelte/issues/5040)) +* Prevent use of `$store` at compile time when top-level `store` has been shadowed ([#5048](https://github.com/sveltejs/svelte/issues/5048)) +* Update ` ``` @@ -44,7 +44,7 @@ As in HTML, values may be unquoted. Attribute values can contain JavaScript expressions. -```html +```sv page {p} ``` @@ -52,7 +52,7 @@ Attribute values can contain JavaScript expressions. Or they can *be* JavaScript expressions. -```html +```sv ``` @@ -71,7 +71,7 @@ All other attributes are included unless their value is [nullish](https://develo An expression might include characters that would cause syntax highlighting to fail in regular HTML, so quoting the value is permitted. The quotes do not affect how the value is parsed: -```html +```sv ``` @@ -79,7 +79,7 @@ An expression might include characters that would cause syntax highlighting to f When the attribute name and value match (`name={name}`), they can be replaced with `{name}`. -```html +```sv @@ -91,7 +91,7 @@ By convention, values passed to components are referred to as *properties* or *p As with elements, `name={name}` can be replaced with the `{name}` shorthand. -```html +```sv ``` @@ -101,7 +101,7 @@ As with elements, `name={name}` can be replaced with the `{name}` shorthand. An element or component can have multiple spread attributes, interspersed with regular ones. -```html +```sv ``` @@ -109,10 +109,22 @@ An element or component can have multiple spread attributes, interspersed with r *`$$props`* references all props that are passed to a component – including ones that are not declared with `export`. It is useful in rare cases, but not generally recommended, as it is difficult for Svelte to optimise. -```html +```sv ``` +--- + +*`$$restProps`* contains only the props which are *not* declared with `export`. It can be used to pass down other unknown attributes to an element in a component. + +```html + +``` + + +> The `value` attribute of an `input` element or its children `option` elements must not be set with spread attributes when using `bind:group` or `bind:checked`. Svelte needs to be able to see the element's `value` directly in the markup in these cases so that it can link it to the bound variable. + +--- ### Text expressions @@ -124,7 +136,7 @@ An element or component can have multiple spread attributes, interspersed with r Text can also contain JavaScript expressions: -```html +```sv

Hello {name}!

{a} + {b} = {a + b}.

``` @@ -136,7 +148,7 @@ Text can also contain JavaScript expressions: You can use HTML comments inside components. -```html +```sv

Hello world

``` @@ -145,7 +157,7 @@ You can use HTML comments inside components. Comments beginning with `svelte-ignore` disable warnings for the next block of markup. Usually these are accessibility warnings; make sure that you're disabling them for a good reason. -```html +```sv ``` @@ -167,7 +179,7 @@ Comments beginning with `svelte-ignore` disable warnings for the next block of m Content that is conditionally rendered can be wrapped in an if block. -```html +```sv {#if answer === 42}

what was the question?

{/if} @@ -177,7 +189,7 @@ Content that is conditionally rendered can be wrapped in an if block. Additional conditions can be added with `{:else if expression}`, optionally ending in an `{:else}` clause. -```html +```sv {#if porridge.temperature > 100}

too hot!

{:else if 80 > porridge.temperature} @@ -197,6 +209,9 @@ Additional conditions can be added with `{:else if expression}`, optionally endi {#each expression as name, index}...{/each} ``` ```sv +{#each expression as name (key)}...{/each} +``` +```sv {#each expression as name, index (key)}...{/each} ``` ```sv @@ -207,7 +222,7 @@ Additional conditions can be added with `{:else if expression}`, optionally endi Iterating over lists of values can be done with an each block. -```html +```sv

Shopping list

    {#each items as item} @@ -222,7 +237,7 @@ You can use each blocks to iterate over any array or array-like value — that i An each block can also specify an *index*, equivalent to the second argument in an `array.map(...)` callback: -```html +```sv {#each items as item, i}
  • {i + 1}: {item.name} x {item.qty}
  • {/each} @@ -232,7 +247,12 @@ An each block can also specify an *index*, equivalent to the second argument in If a *key* expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change. -```html +```sv +{#each items as item (item.id)} +
  • {item.name} x {item.qty}
  • +{/each} + + {#each items as item, i (item.id)}
  • {i + 1}: {item.name} x {item.qty}
  • {/each} @@ -242,7 +262,7 @@ If a *key* expression is provided — which must uniquely identify each list ite You can freely use destructuring and rest patterns in each blocks. -```html +```sv {#each items as { id, name, qty }, i (id)}
  • {i + 1}: {name} x {qty}
  • {/each} @@ -260,7 +280,7 @@ You can freely use destructuring and rest patterns in each blocks. An each block can also have an `{:else}` clause, which is rendered if the list is empty. -```html +```sv {#each todos as todo}

    {todo.text}

    {:else} @@ -285,7 +305,7 @@ An each block can also have an `{:else}` clause, which is rendered if the list i Await blocks allow you to branch on the three possible states of a Promise — pending, fulfilled or rejected. -```html +```sv {#await promise}

    waiting for the promise to resolve...

    @@ -302,7 +322,7 @@ Await blocks allow you to branch on the three possible states of a Promise — p The `catch` block can be omitted if you don't need to render anything when the promise rejects (or no error is possible). -```html +```sv {#await promise}

    waiting for the promise to resolve...

    @@ -316,7 +336,7 @@ The `catch` block can be omitted if you don't need to render anything when the p If you don't care about the pending state, you can also omit the initial block. -```html +```sv {#await promise then value}

    The value is {value}

    {/await} @@ -337,7 +357,7 @@ The expression should be valid standalone HTML — `{@html "
    "}content{@html > Svelte does not sanitize expressions before injecting HTML. If the data comes from an untrusted source, you must sanitize it, or you are exposing your users to an XSS vulnerability. -```html +```sv

    {post.title}

    {@html post.content} @@ -360,7 +380,7 @@ The `{@debug ...}` tag offers an alternative to `console.log(...)`. It logs the It accepts a comma-separated list of variable names (not arbitrary expressions). -```html +```sv @@ -1331,7 +1355,7 @@ The `` element renders a component dynamically, using the comp If `this` is falsy, no component is rendered. -```html +```sv ``` @@ -1349,7 +1373,7 @@ If `this` is falsy, no component is rendered. The `` element allows you to add event listeners to the `window` object without worrying about removing them when the component is destroyed, or checking for the existence of `window` when server-side rendering. -```html +```sv ``` +> This behaviour will only work when the function passed to `onMount` *synchronously* returns a value. `async` functions always return a `Promise`, and as such cannot *synchronously* return a function. + #### `beforeUpdate` ```js @@ -62,7 +64,7 @@ Schedules a callback to run immediately before the component is updated after an > The first time the callback runs will be before the initial `onMount` -```html +```sv @@ -582,7 +586,7 @@ Animates a `blur` filter alongside an element's opacity. * `opacity` (`number`, default 0) - the opacity value to animate out to and in from * `amount` (`number`, default 5) - the size of the blur in pixels -```html +```sv @@ -621,7 +625,7 @@ Animates the x and y positions and the opacity of an element. `in` transitions a You can see the `fly` transition in action in the [transition tutorial](tutorial/adding-parameters-to-transitions). -```html +```sv - -

    Todos

    {#each todos as todo} -
    +
    {/each} diff --git a/site/content/examples/05-bindings/08-media-elements/App.svelte b/site/content/examples/05-bindings/08-media-elements/App.svelte index 469e9e12eb..7a071dbe49 100644 --- a/site/content/examples/05-bindings/08-media-elements/App.svelte +++ b/site/content/examples/05-bindings/08-media-elements/App.svelte @@ -109,8 +109,8 @@
    \ No newline at end of file +
    diff --git a/site/content/examples/06-lifecycle/03-tick/App.svelte b/site/content/examples/06-lifecycle/03-tick/App.svelte index c9cb6de420..477b5ffb79 100644 --- a/site/content/examples/06-lifecycle/03-tick/App.svelte +++ b/site/content/examples/06-lifecycle/03-tick/App.svelte @@ -4,7 +4,7 @@ let text = `Select some text and hit the tab key to toggle uppercase`; async function handleKeydown(event) { - if (event.which !== 9) return; + if (event.key !== 'Tab') return; event.preventDefault(); @@ -34,4 +34,4 @@ } - \ No newline at end of file + diff --git a/site/content/examples/09-transitions/04-custom-js-transitions/App.svelte b/site/content/examples/09-transitions/04-custom-js-transitions/App.svelte index 3b3fb94d1c..396c793639 100644 --- a/site/content/examples/09-transitions/04-custom-js-transitions/App.svelte +++ b/site/content/examples/09-transitions/04-custom-js-transitions/App.svelte @@ -4,7 +4,7 @@ function typewriter(node, { speed = 50 }) { const valid = ( node.childNodes.length === 1 && - node.childNodes[0].nodeType === 3 + node.childNodes[0].nodeType === Node.TEXT_NODE ); if (!valid) { @@ -33,4 +33,4 @@

    The quick brown fox jumps over the lazy dog

    -{/if} \ No newline at end of file +{/if} diff --git a/site/content/examples/10-animations/00-animate/App.svelte b/site/content/examples/10-animations/00-animate/App.svelte index 279821491b..a90d0ecfb5 100644 --- a/site/content/examples/10-animations/00-animate/App.svelte +++ b/site/content/examples/10-animations/00-animate/App.svelte @@ -112,7 +112,7 @@
    diff --git a/site/content/examples/21-miscellaneous/01-hacker-news/Item.svelte b/site/content/examples/21-miscellaneous/01-hacker-news/Item.svelte index e9cd617e96..608d04e497 100644 --- a/site/content/examples/21-miscellaneous/01-hacker-news/Item.svelte +++ b/site/content/examples/21-miscellaneous/01-hacker-news/Item.svelte @@ -3,6 +3,8 @@ export let item; export let returnTo; + + $: url = !item.domain ? `https://news.ycombinator.com/${item.url}` : item.url; - \ No newline at end of file + diff --git a/site/content/tutorial/07-lifecycle/04-tick/app-b/App.svelte b/site/content/tutorial/07-lifecycle/04-tick/app-b/App.svelte index c9cb6de420..477b5ffb79 100644 --- a/site/content/tutorial/07-lifecycle/04-tick/app-b/App.svelte +++ b/site/content/tutorial/07-lifecycle/04-tick/app-b/App.svelte @@ -4,7 +4,7 @@ let text = `Select some text and hit the tab key to toggle uppercase`; async function handleKeydown(event) { - if (event.which !== 9) return; + if (event.key !== 'Tab') return; event.preventDefault(); @@ -34,4 +34,4 @@ } - \ No newline at end of file + diff --git a/site/content/tutorial/07-lifecycle/04-tick/text.md b/site/content/tutorial/07-lifecycle/04-tick/text.md index b58b024ad6..8f5603a361 100644 --- a/site/content/tutorial/07-lifecycle/04-tick/text.md +++ b/site/content/tutorial/07-lifecycle/04-tick/text.md @@ -4,7 +4,7 @@ title: tick The `tick` function is unlike other lifecycle functions in that you can call it any time, not just when the component first initialises. It returns a promise that resolves as soon as any pending state changes have been applied to the DOM (or immediately, if there are no pending state changes). -When you invalidate component state in Svelte, it doesn't update the DOM immediately. Instead, it waits until the next *microtask* to see if there are any other changes that need to be applied, including in other components. Doing so avoids unnecessary work and allows the browser to batch things more effectively. +When you update component state in Svelte, it doesn't update the DOM immediately. Instead, it waits until the next *microtask* to see if there are any other changes that need to be applied, including in other components. Doing so avoids unnecessary work and allows the browser to batch things more effectively. You can see that behaviour in this example. Select a range of text and hit the tab key. Because the `