diff --git a/.eslintignore b/.eslintignore index 04940e2f7d..a0ca1e55c8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,7 @@ **/_actual.js **/expected.js test/*/samples/*/output.js +node_modules # output files animate/*.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..2c7f2ed1b4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,99 @@ +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': [ + 'error', + 'tab', + { + SwitchCase: 1, + ignoredNodes: ['TemplateLiteral'] + } + ], + '@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': [ + 'error', + { + allowAsParameter: true + } + ], + '@typescript-eslint/no-unused-vars': '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'], + settings: { + 'import/core-modules': [ + 'svelte', + 'svelte/internal', + 'svelte/store', + 'svelte/easing', + '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/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 26b87eab68..0000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "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, - "@typescript-eslint/indent": ["error", "tab", { - "SwitchCase": 1, - "ignoredNodes": ["TemplateLiteral"] - }], - "@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": ["error", { - "allowAsParameter": true - }], - "@typescript-eslint/no-unused-vars": "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" - }, - "settings": { - "import/core-modules": [ - "svelte", - "svelte/internal", - "svelte/store", - "svelte/easing", - "estree" - ] - }, - "overrides": [ - { - "files": ["*.js"], - "rules": { - "@typescript-eslint/no-var-requires": "off" - } - } - ] -} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e7bd60d3f4..6871094f5c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,21 +1,26 @@ - +Thanks for being part of Svelte! +------- diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..f5369e9991 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,52 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'Bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Logs** +Please include browser console and server logs around the time this bug occurred. + +**To Reproduce** +To help us help you, if you've found a bug please consider the following: + +* If you can demonstrate the bug using https://svelte.dev/repl, please do. +* If that's not possible, we recommend creating a small repo that illustrates the problem. +* Reproductions should be small, self-contained, correct examples – http://sscce.org. + +Occasionally, this won't be possible, and that's fine – we still appreciate you raising the issue. But please understand that Svelte is run by unpaid volunteers in their free time, and issues that follow these instructions will get fixed faster. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Stacktraces** +If you have a stack trace to include, we recommend putting inside a `
` block for the sake of the thread's readability: + +
+ Stack trace + + Stack trace goes here... +
+ +**Information about your Svelte project:** +- Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, IE 10) + +- Your operating system: (e.x. OS X 10, Ubuntu Linux 19.10, Windows XP, etc) + +- Svelte version (Please check you can reproduce the issue with the latest release!) + +- Whether your project uses Webpack or Rollup + +**Severity** +How severe an issue is this bug to you? Is this annoying, blocking some users, blocking an upgrade or blocking your usage of Svelte entirely? + +Note: the more honest and specific you are here the more we will take you seriously. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..e533c21c96 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'New Feature' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. For example: I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**How important is this feature to you?** +Note: the more honest and specific you are here the more we will take you seriously. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/questions-and-help.md b/.github/ISSUE_TEMPLATE/questions-and-help.md new file mode 100644 index 0000000000..a6e0dc6e19 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/questions-and-help.md @@ -0,0 +1,12 @@ +--- +name: Questions and help +about: If you think you need help with something related to Svelte +title: '' +labels: 'Question' +assignees: '' + +--- + +This issue tracker is intended to collect bug reports and feature requests. + +For help with installation, information on how features work, or questions about specific features of Svelte, please come and join us in the [Svelte Discord](https://svelte.dev/chat), or ask your question on [Stack Overflow](https://stackoverflow.com/questions/tagged/svelte). Any issues open for help requests will be closed to keep from clogging up the issue tracker. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d2882acf4a..1e77e0337a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +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. +- [ ] 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`) diff --git a/.gitignore b/.gitignore index 590bd1d88e..1de9283c03 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ node_modules /animate /scratch/ /coverage/ -/coverage.lcov/ +/coverage.lcov /test/sourcemaps/samples/*/output.js /test/sourcemaps/samples/*/output.js.map /test/sourcemaps/samples/*/output.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eeaf4acab..6d576f9124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,132 @@ # Svelte changelog +## 3.11.0 + +* `$capture_state` and `$inject_state` HMR hooks in dev mode ([#3148](https://github.com/sveltejs/svelte/pull/3148)) +* Allow unclosed tags inside if/each/etc blocks ([#2807](https://github.com/sveltejs/svelte/issues/2807)) +* Invalidate unreferenced store values inside ` ``` diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index 981224fc14..b878f1453e 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -94,6 +94,14 @@ 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 + +``` + ### Text expressions @@ -111,6 +119,27 @@ Text can also contain JavaScript expressions: ``` +### Comments + +--- + +You can use HTML comments inside components. + +```html + +

Hello world

+``` + +--- + +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 + + +``` + + ### {#if ...} ```sv @@ -289,7 +318,9 @@ If you don't care about the pending state, you can also omit the initial block. --- -In a text expression, characters like `<` and `>` are escaped. With HTML expressions, they're not. +In a text expression, characters like `<` and `>` are escaped; however, with HTML expressions, they're not. + +The expression should be valid standalone HTML — `{@html "
"}content{@html "
"}` will *not* work, because `` is not valid 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. @@ -354,7 +385,7 @@ The `{@debug}` tag without any arguments will insert a `debugger` statement that As well as attributes, elements can have *directives*, which control the element's behaviour in some way. -#### [on:*eventname*](on_component_event) +#### [on:*eventname*](on_element_event) ```sv on:eventname={handler} @@ -947,7 +978,7 @@ Unlike with `transition:`, transitions applied with `in:` and `out:` are not bid -#### animate: +#### animate:*fn* ```sv animate:name @@ -982,7 +1013,7 @@ DOMRect { --- -An animation is triggered when the contents of a [keyed each block](docs#Each_blocks) are re-ordered. Animations do not run when an element is removed, only when the each block's data is reordered. Animate directives must be on an element that is an *immediate* child of a keyed each block. +An animation is triggered when the contents of a [keyed each block](docs#each) are re-ordered. Animations do not run when an element is removed, only when the each block's data is reordered. Animate directives must be on an element that is an *immediate* child of a keyed each block. Animations can be used with Svelte's [built-in animation functions](docs#svelte_animate) or [custom animation functions](docs#Custom_animation_functions). diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 3552552767..976e78cea5 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -156,6 +156,8 @@ Like lifecycle functions, this must be called during component initialisation. ``` +> Context is not inherently reactive. If you need reactive values in context then you can pass a store into context, which *will* be reactive. + #### `getContext` ```js @@ -212,39 +214,7 @@ Events dispatched from child components can be listened to in their parent. Any ### `svelte/store` -The `svelte/store` module exports functions for creating [stores](tutorial/writable-stores). - ---- - -To be considered a store, an object must have a `subscribe` method that returns an `unsubscribe` function. - -```js -const unsubscribe = store.subscribe(value => { - console.log(value); -}); // logs `value` - -// later... -unsubscribe(); -``` - ---- - -Stores have special significance inside Svelte components. Their values can be read by prefixing the store's name with the `$` character, which causes Svelte to set up subscriptions and unsubscriptions automatically during the component's lifecycle. - -```html - - - -``` +The `svelte/store` module exports functions for creating [stores](docs#4_Prefix_stores_with_$_to_access_their_values). #### `writable` @@ -257,7 +227,11 @@ store = writable(value: any, (set: (value: any) => void) => () => void) --- -Creates a store with additional `set` and `update` methods. +Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional `set` and `update` methods. + +`set` is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it. + +`update` is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store. ```js import { writable } from 'svelte/store'; @@ -558,6 +532,42 @@ You can see the `fade` transition in action in the [transition tutorial](tutoria {/if} ``` +#### `blur` + +```sv +transition:blur={params} +``` +```sv +in:blur={params} +``` +```sv +out:blur={params} +``` + +--- + +Animates a `blur` filter alongside an element's opacity. + +`blur` accepts the following parameters: + +* `delay` (`number`, default 0) — milliseconds before starting +* `duration` (`number`, default 400) — milliseconds the transition lasts +* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#svelte_easing) +* `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 + + +{#if condition} +
+ fades in and out +
+{/if} +``` + #### `fly` ```sv @@ -693,7 +703,7 @@ Animates the stroke of an SVG element, like a snake in a tube. `in` transitions * `duration` (`number` | `function`, default 800) — milliseconds the transition lasts * `easing` (`function`, default `cubicInOut`) — an [easing function](docs#svelte_easing) -The `speed` parameter is a means of setting the duration of the transition relative to the path's length. It is modifier that is applied to the length of the path: `duration = length / speed`. A path that is 1000 pixels with a speed of 1 will have a duration of `1000ms`, setting the speed to `0.5` will halve that duration and setting it to `2` will double it. +The `speed` parameter is a means of setting the duration of the transition relative to the path's length. It is modifier that is applied to the length of the path: `duration = length / speed`. A path that is 1000 pixels with a speed of 1 will have a duration of `1000ms`, setting the speed to `0.5` will double that duration and setting it to `2` will halve it. ```html + +

Hello {name}!

+ +``` + +--- + +Alternatively, use `tag={null}` to indicate that the consumer of the custom element should name it. + +```js +import MyElement from './MyElement.svelte'; + +customElements.define('my-element', MyElement); +``` + +--- + +Once a custom element has been defined, it can be used as a regular DOM element: + +```js +document.body.innerHTML = ` + +

This is some slotted content

+
+`; +``` + +--- + +By default, custom elements are compiled with `accessors: true`, which means that any [props](docs#Attributes_and_props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible). + +To prevent this, add `accessors={false}` to ``. + +```js +const el = document.querySelector('my-element'); + +// get the current value of the 'name' prop +console.log(el.name); + +// set a new value, updating the shadow DOM +el.name = 'everybody'; +``` + +Custom elements can be a useful way to package components for consumption in a non-Svelte app, as they will work with vanilla HTML and JavaScript as well as [most frameworks](https://custom-elements-everywhere.com/). There are, however, some important differences to be aware of: + +* Styles are *encapsulated*, rather than merely *scoped*. This means that any non-component styles (such as you might have in a `global.css` file) will not apply to the custom element, including styles with the `:global(...)` modifier +* Instead of being extracted out as a separate .css file, styles are inlined into the component as a JavaScript string +* Custom elements are not generally suitable for server-side rendering, as the shadow DOM is invisible until JavaScript loads +* In Svelte, slotted content renders *lazily*. In the DOM, it renders *eagerly*. In other words, it will always be created even if the component's `` element is inside an `{#if ...}` block. Similarly, including a `` in an `{#each ...}` block will not cause the slotted content to be rendered multiple times +* The `let:` directive has no effect +* Polyfills are required to support older browsers + ### Server-side component API diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/04-compile-time.md index c219bae497..f47fe564af 100644 --- a/site/content/docs/04-compile-time.md +++ b/site/content/docs/04-compile-time.md @@ -186,15 +186,15 @@ result: { } = svelte.preprocess( source: string, preprocessors: Array<{ - markup?: (input: { source: string, filename: string }) => Promise<{ + markup?: (input: { content: string, filename: string }) => Promise<{ code: string, dependencies?: Array }>, - script?: (input: { source: string, attributes: Record, filename: string }) => Promise<{ + script?: (input: { content: string, attributes: Record, filename: string }) => Promise<{ code: string, dependencies?: Array }>, - style?: (input: { source: string, attributes: Record, filename: string }) => Promise<{ + style?: (input: { content: string, attributes: Record, filename: string }) => Promise<{ code: string, dependencies?: Array }> diff --git a/site/content/examples/03-logic/04-keyed-each-blocks/App.svelte b/site/content/examples/03-logic/04-keyed-each-blocks/App.svelte index 5cd279d7a7..cd12eed954 100644 --- a/site/content/examples/03-logic/04-keyed-each-blocks/App.svelte +++ b/site/content/examples/03-logic/04-keyed-each-blocks/App.svelte @@ -2,11 +2,11 @@ import Thing from './Thing.svelte'; let things = [ - { id: 1, value: 'a' }, - { id: 2, value: 'b' }, - { id: 3, value: 'c' }, - { id: 4, value: 'd' }, - { id: 5, value: 'e' } + { id: 1, color: '#0d0887' }, + { id: 2, color: '#6a00a8' }, + { id: 3, color: '#b12a90' }, + { id: 4, color: '#e16462' }, + { id: 5, color: '#fca636' } ]; function handleClick() { @@ -22,14 +22,14 @@

Keyed

{#each things as thing (thing.id)} - + {/each}

Unkeyed

{#each things as thing} - + {/each}
\ No newline at end of file diff --git a/site/content/examples/03-logic/04-keyed-each-blocks/Thing.svelte b/site/content/examples/03-logic/04-keyed-each-blocks/Thing.svelte index 4e86d32109..28c3c65d05 100644 --- a/site/content/examples/03-logic/04-keyed-each-blocks/Thing.svelte +++ b/site/content/examples/03-logic/04-keyed-each-blocks/Thing.svelte @@ -1,9 +1,24 @@ -

{valueAtStart} / {value}

\ No newline at end of file +

+ initial + current +

+ + \ No newline at end of file diff --git a/site/content/examples/05-bindings/04-textarea-inputs/App.svelte b/site/content/examples/05-bindings/04-textarea-inputs/App.svelte index be338bf47f..5a9e18f79d 100644 --- a/site/content/examples/05-bindings/04-textarea-inputs/App.svelte +++ b/site/content/examples/05-bindings/04-textarea-inputs/App.svelte @@ -1,12 +1,12 @@ - + -{@html marked(value)} \ No newline at end of file +{@html marked(text)} \ No newline at end of file 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 2cf819fca0..fd4f843ca1 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 @@