From 1e982c1e6d33e03d3f0939cf66c79db7adb1df08 Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Wed, 5 Apr 2023 15:51:47 +0530 Subject: [PATCH 01/15] chore: Run prettier --- sites/svelte.dev/src/routes/docs/+page.svelte | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sites/svelte.dev/src/routes/docs/+page.svelte b/sites/svelte.dev/src/routes/docs/+page.svelte index ec6a29bfa8..f6ef383623 100644 --- a/sites/svelte.dev/src/routes/docs/+page.svelte +++ b/sites/svelte.dev/src/routes/docs/+page.svelte @@ -130,7 +130,7 @@ 'accessibility-warnings-a11y-role-supports-aria-props', 'accessibility-warnings-a11y-structure', 'accessibility-warnings-a11y-unknown-aria-attribute', - 'accessibility-warnings-a11y-unknown-role', + 'accessibility-warnings-a11y-unknown-role' ]; /** @type {Map}*/ @@ -154,7 +154,7 @@ [/template-syntax-(const|debug|html)$/i, 'special-tags#$1'], [ /template-syntax-(tags|attributes-and-props|text-expressions|comments)$/i, - 'dot-svelte-files#$1', + 'dot-svelte-files#$1' ], // !!!! This one should stay at the bottom of `template-syntax`, or it may end up hijacking logic blocks and special tags [/template-syntax-(.+)/i, 'special-elements#$1'], @@ -164,7 +164,7 @@ [/run-time-(client-side-component-api)-?(.*)/i, '$1#$2'], [ /run-time-(svelte-easing|server-side-component-api|custom-element-api|svelte-register)$/i, - '$1', + '$1' ], // Catch all, should be at the end or will include store, motion, transition and other modules starting with svelte [/run-time-(svelte)(?:-(.+))?/i, '$1#$2'], @@ -173,7 +173,7 @@ [/compile-time-?(.*)/i, 'svelte-compiler#$1'], // Accessibility warnings - [/(accessibility-warnings)-?(.+)/i, '$1#$2'], + [/(accessibility-warnings)-?(.+)/i, '$1#$2'] ]); function get_old_new_ids_map() { @@ -199,7 +199,6 @@ } function getURlToRedirectTo() { - console.log(get_old_new_ids_map()); const hash = $page.url.hash.replace(/^#/i, ''); if (!hash) return '/docs/introduction'; @@ -212,5 +211,8 @@ return `/docs/${old_new_map.get(hash)}`; } - onMount(() => goto(getURlToRedirectTo(), { replaceState: true })); + onMount(() => { + console.log(get_old_new_ids_map()); + goto(getURlToRedirectTo(), { replaceState: true }); + }); From 99979959c04616142cbc104a22af0edf53eb1635 Mon Sep 17 00:00:00 2001 From: Puru Vijay <47742487+PuruVJ@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:36:59 +0530 Subject: [PATCH 02/15] Update readme (#8453) --- sites/svelte.dev/README.md | 154 +++++++++++++++++- sites/svelte.dev/src/routes/blog/+page.svelte | 1 - 2 files changed, 148 insertions(+), 7 deletions(-) diff --git a/sites/svelte.dev/README.md b/sites/svelte.dev/README.md index 47cc33dd74..085ad9d53e 100644 --- a/sites/svelte.dev/README.md +++ b/sites/svelte.dev/README.md @@ -9,16 +9,16 @@ If you do want to use a database, set it up on [Supabase](https://supabase.com) Run the site sub-project: ```bash -pnpm install -pnpm dev +npm install +npm run dev ``` and navigate to [localhost:5173](http://localhost:5173). -The first time you run the site locally, it will update the list of Contributors and REPL dependencies. After this it won't run again unless you force it by running: +The first time you run the site locally, it will update the list of Contributors, REPL dependencies and examples data that is used on the [examples page](https://svelte-dev-2.vercel.app/examples). After this it won't run again unless you force it by running: ```bash -pnpm update +npm run update ``` ## Running using the local copy of Svelte @@ -56,11 +56,153 @@ The GitHub app requires a specific callback URL, and so cannot be used with the ## Building the site -To build the website, run `pnpm build`. The output can be found in `build`. +To build the website, run `npm run build`. The output can be found in `.vercel`. ## Testing -Tests can be run using `pnpm test`. +Tests can be run using `npm run test`. + +## Docs & other content + +All the docs, examples, tutorials, FAQ live in the [site/content](../../site/content) directory, outside the site sub-project. If you modify these, and your app server is running, you will need to reload the page to see the changes. + +Following are the file structures of the different kind of documentations + +### Docs structure + +```txt +- site/content/docs + - 01-getting-started <- Category + - meta.json <- Metadata + - 01-introduction.md <- Page + - 02-template-syntax <- Category + - meta.json <- Metadata + - 01-logic-blocks.md <- Page + - 02-special-tags.md <- Page + - 03-element-directives.md <- Page +``` + +If you are creating a new page, it must be within a category. That is, you can't have a .md file in the `docs` directory's root level. You may have a category without any pages in it, but you can't have a page without a category. You can add the new page in an existing category, or create your own, for example: + +```txt +- site/content/docs + ++ - 07-my-new-category <- Category ++ - 01-my-new-page.md <- Page +``` + +The numbers in front of category folders and page files are just for ordering the content. They may not be consecutive. Their only purpose exists for the docs author to decide how the content is arranged. + +> Because of hardcoded regex in docs processing code, the numbers prefixed to pages are REQUIRED and _must be two digits_. + +The name of the file is what determines the URL of the page. For example, the URL of `01-introduction.md` is `https://svelte.dev/docs/introduction`. The URL of `02-special-tags.md` is `https://svelte.dev/docs/special-tags`. Even though these are in categories, the URL does not contain the category name. Keep this in mind when creating new pages, as two pages with same slug in different categories will clash. + +**meta.json** files contain data about the current category. At the time of writing it only has one field: `title` + +```json +{ + "title": "Getting Started" +} +``` + +This `title` field is used as category text in the sidebar on docs page. + +Every single .md file in the docs must have frontmatter with `title` in it. For example, this is how the frontmatter of `02-logic-blocks.md` looks like: + +```md +--- +title: .svelte files +--- + +Components are the building blocks of Svelte applications. They are written into `.svelte` files, using a superset of HTML. + +All three sections — script, styles and markup — are optional. + + +``` + +You need not specify a h1 tag(or in markdown, a `#`). The `title` field in the frontmatter will be used as the h1 tag. + +The headings in the document must start from h2(`##`). That is, you can't have an h1 tag in the document. h2(`##`), h3(`###`), h4(`####`) and h5(`#####`) are all valid. + +#### Processing + +Docs are processed in the [`src/lib/server/docs`](./src/lib/server/docs) folder. `get-docs-data.js` is responsible for reading the docs from filesystem and accumulating the metadata in forms of arrays and objects. `docs/index.js` has the code responsible for _rendering_ the markdown files into HTML. These functions are then imported into [src/routes/docs/+layout.server.js](./src/routes/docs/+layout.server.js) and used to generate docs list, and similarly in [src/routes/docs/%5Bslug%5D/+page.server.js](./src/routes/docs/%5Bslug%5D/%2Bpage.server.js) and are rendered there. + +### Tutorial structure + +```txt +- site/content/tutorial + - 01-introduction <- Category + - meta.json <- Metadata + - 01-basics <- Page's content folder + - text.md <- Text content of tutorial + - app-a <- The initial app folder + - App.svelte + - store.js + - app-b <- The final app folder. Not always present + - App.svelte + - store.js +``` + +Similar to how docs are structured, only difference is that the pages are in a folders, and their content is in a `text.md` file. Alongside, are two folders, _app-a_ and _app-b_. These are the initial and final apps respectively. The initial app is the one that the tutorial shows, and the final app is the one that the tutorial switches to after user clicks on the **Show me** button. + +> app-b is not always there. This means that the _Show me_ button is not present for that page. + +The naming scheme of docs is followed here as well. The numbers in front of the folders are just for ordering the content. They may not be consecutive. Their only purpose exists for the tutorial author to decide how the content is arranged. _And they are compulsary_. + +#### Processing + +Tutorials are processed in the [`src/lib/server/tutorial`](./src/lib/server/tutorial) folder. `get-tutorial-data.js` is responsible for reading the tutorials from filesystem and accumulating the metadata in forms of arrays and objects. `tutorial/index.js` has the code responsible for _rendering_ the markdown files into HTML. These functions are then imported into [src/routes/tutorial/+layout.server.js](./src/routes/tutorial/%2Blayout.server.js) and used to generate tutorial list, and similarly in [src/routes/tutorial/%5Bslug%5D/+page.server.js](./src/routes/tutorial/%5Bslug%5D/%2Bpage.server.js) and are rendered there. + +### Examples structure + +```txt +- site/content/examples + - 00-introduction <- Category + - meta.json <- Metadata + - 00-hello-world <- Page's content folder + - meta.json <- Metadata + - App.svelte <- code files + - 01-reactivity <- Category + - meta.json <- Metadata + - 00-reactive-assignments <- Page's content folder + - meta.json <- Metadata + - App.svelte <- code files +``` + +Similar to the tutorial, only difference: There is no `text.md`, and the code files are kept right in the folder, not in `app-` folder. + +Same naming scheme as docs and tutorial is followed. + +#### Processing + +Examples are processed in the [`src/lib/server/examples`](./src/lib/server/examples) folder. `get-examples-data.js` is responsible for reading the examples from filesystem and accumulating the metadata in forms of arrays and objects. `examples/index.js` has the code responsible for _rendering_ the markdown files into HTML. These functions are then imported into [src/routes/examples/%5Bslug%5D/+page.server.js](./src/routes/examples/%5Bslug%5D/%2Bpage.server.js) and are rendered there. + +### Blog structure + +```txt +- site/content/blog + - 2019-01-01-my-first-post.md + - 2019-01-02-my-second-post.md +``` + +Compared to the rest of the content, blog posts are not in a folder. They are placed at the root of `site/content/blog` folder. The name of the file is the date of the post, followed by the slug of the post. The slug is the URL where the blog post is available. For example, the slug of `2019-01-01-my-first-post.md` is `my-first-post`. + +All the metadata about the blog post is mentioned in the frontematter of a post. For example, this is how the frontmatter of [2023-03-09-zero-config-type-safety.md](../../site/content/blog/2023-03-09-zero-config-type-safety.md) looks like: + +```md +--- +title: Zero-effort type safety +description: More convenience and correctness, less boilerplate +author: Simon Holthausen +authorURL: https://twitter.com/dummdidumm_ +--- +``` + +#### Processing + +Blog posts are processed in the [`src/lib/server/blog`](./src/lib/server/blog) folder. `get-blog-data.js` is responsible for reading the blog posts from filesystem and accumulating the metadata in forms of arrays and objects. `blog/index.js` has the code responsible for _rendering_ the markdown files into HTML. These functions are then imported into [src/routes/blog/+page.svelte](./src/routes/blog/%2Bpage.server.js), where they show the list of blogs. The rendering function is imported in [src/routes/blog/%5Bslug%5D/+page.server.js](./src/routes/blog/%5Bslug%5D/%2Bpage.server.js) and renders the individual blog post there. ## Translating the API docs diff --git a/sites/svelte.dev/src/routes/blog/+page.svelte b/sites/svelte.dev/src/routes/blog/+page.svelte index 119cbd2e93..ddb748bea3 100644 --- a/sites/svelte.dev/src/routes/blog/+page.svelte +++ b/sites/svelte.dev/src/routes/blog/+page.svelte @@ -1,5 +1,4 @@ From def1890f4ff0cccb9573bfc79984fbe1258ed318 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 11 Apr 2023 02:25:53 -0700 Subject: [PATCH 03/15] chore: bump @jridgewell/sourcemap-codec (#8458) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c894b1605..3f32f9633f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "devDependencies": { "@ampproject/remapping": "^0.3.0", - "@jridgewell/sourcemap-codec": "^1.4.14", + "@jridgewell/sourcemap-codec": "^1.4.15", "@rollup/plugin-commonjs": "^11.0.0", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^11.2.1", @@ -184,9 +184,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -5499,9 +5499,9 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@nodelib/fs.scandir": { diff --git a/package.json b/package.json index dc44115e39..25aafb817a 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "homepage": "https://svelte.dev", "devDependencies": { "@ampproject/remapping": "^0.3.0", - "@jridgewell/sourcemap-codec": "^1.4.14", + "@jridgewell/sourcemap-codec": "^1.4.15", "@rollup/plugin-commonjs": "^11.0.0", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^11.2.1", From 3a7685fef554f0ae2480146eb7c2d0ec944b28ac Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:44:19 +0200 Subject: [PATCH 04/15] fix: special-case width/height attribute during spread (#8412) fixes #6752 --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Tan Li Hau --- src/runtime/internal/dom.ts | 11 ++++++++++- .../samples/spread-width-height-attributes/_config.js | 4 ++++ .../spread-width-height-attributes/main.svelte | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/spread-width-height-attributes/_config.js create mode 100644 test/runtime/samples/spread-width-height-attributes/main.svelte diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 34049b3580..8a78accb50 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -306,6 +306,15 @@ export function attr(node: Element, attribute: string, value?: string) { else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } +/** + * List of attributes that should always be set through the attr method, + * because updating them through the property setter doesn't work reliably. + * In the example of `width`/`height`, the problem is that the setter only + * accepts numeric values, but the attribute can also be set to a string like `50%`. + * If this list becomes too big, rethink this approach. + */ +const always_set_through_set_attribute = ['width', 'height']; + export function set_attributes(node: Element & ElementCSSInlineStyle, attributes: { [x: string]: string }) { // @ts-ignore const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); @@ -316,7 +325,7 @@ export function set_attributes(node: Element & ElementCSSInlineStyle, attributes node.style.cssText = attributes[key]; } else if (key === '__value') { (node as any).value = node[key] = attributes[key]; - } else if (descriptors[key] && descriptors[key].set) { + } else if (descriptors[key] && descriptors[key].set && always_set_through_set_attribute.indexOf(key) === -1) { node[key] = attributes[key]; } else { attr(node, key, attributes[key]); diff --git a/test/runtime/samples/spread-width-height-attributes/_config.js b/test/runtime/samples/spread-width-height-attributes/_config.js new file mode 100644 index 0000000000..cf2dc7efde --- /dev/null +++ b/test/runtime/samples/spread-width-height-attributes/_config.js @@ -0,0 +1,4 @@ +export default { + // https://github.com/sveltejs/svelte/issues/6752 + html: '' +}; diff --git a/test/runtime/samples/spread-width-height-attributes/main.svelte b/test/runtime/samples/spread-width-height-attributes/main.svelte new file mode 100644 index 0000000000..b91b008457 --- /dev/null +++ b/test/runtime/samples/spread-width-height-attributes/main.svelte @@ -0,0 +1 @@ + From 0adc09da9714bb0fcc7fafdbee569ea7cad4fae5 Mon Sep 17 00:00:00 2001 From: Cymaera <69355340+TheCymaera@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:17:58 +0800 Subject: [PATCH 05/15] feat: add support for resize observer bindings (#8022) Implements ResizeObserver bindings: #5524 (comment) Continuation of: #5963 Related to #7583 --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- elements/index.d.ts | 5 ++ src/compiler/compile/nodes/Binding.ts | 3 +- src/compiler/compile/nodes/Element.ts | 7 +- .../render_dom/wrappers/Element/Binding.ts | 8 ++- .../render_dom/wrappers/Element/index.ts | 32 +++++++-- src/compiler/utils/patterns.ts | 6 ++ .../internal/ResizeObserverSingleton.ts | 67 +++++++++++++++++++ src/runtime/internal/dom.ts | 8 ++- 8 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 src/runtime/internal/ResizeObserverSingleton.ts diff --git a/elements/index.d.ts b/elements/index.d.ts index 7595d767bf..ac32ae94c3 100644 --- a/elements/index.d.ts +++ b/elements/index.d.ts @@ -546,6 +546,11 @@ export interface HTMLAttributes extends AriaAttributes, D */ 'bind:innerText'?: string | undefined | null; + readonly 'bind:contentRect'?: DOMRectReadOnly | undefined | null; + readonly 'bind:contentBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4 + readonly 'bind:borderBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4 + readonly 'bind:devicePixelContentBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4 + // SvelteKit 'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null; 'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null; diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 0c29f7ec67..303506222f 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -3,7 +3,7 @@ import get_object from '../utils/get_object'; import Expression from './shared/Expression'; import Component from '../Component'; import TemplateScope from './shared/TemplateScope'; -import { regex_dimensions } from '../../utils/patterns'; +import { regex_dimensions, regex_box_size } from '../../utils/patterns'; import { Node as ESTreeNode } from 'estree'; import { TemplateNode } from '../../interfaces'; import Element from './Element'; @@ -92,6 +92,7 @@ export default class Binding extends Node { this.is_readonly = regex_dimensions.test(this.name) || + regex_box_size.test(this.name) || (isElement(parent) && ((parent.is_media_node() && read_only_media_attributes.has(this.name)) || (parent.name === 'input' && type === 'file')) /* TODO others? */); diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 1678ea1caa..2410904d63 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -12,7 +12,7 @@ import Text from './Text'; import { namespaces } from '../../utils/namespaces'; import map_children from './shared/map_children'; import { is_name_contenteditable, get_contenteditable_attr } from '../utils/contenteditable'; -import { regex_dimensions, regex_starts_with_newline, regex_non_whitespace_character } from '../../utils/patterns'; +import { regex_dimensions, regex_starts_with_newline, regex_non_whitespace_character, regex_box_size } from '../../utils/patterns'; import fuzzymatch from '../../utils/fuzzymatch'; import list from '../../utils/list'; import Let from './Let'; @@ -1090,7 +1090,10 @@ export default class Element extends Node { } else if (contenteditable && !contenteditable.is_static) { return component.error(contenteditable, compiler_errors.dynamic_contenteditable_attribute); } - } else if (name !== 'this') { + } else if ( + name !== 'this' && + !regex_box_size.test(name) + ) { return component.error(binding, compiler_errors.invalid_binding(binding.name)); } }); diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index 642e4694b1..01da1f0e12 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -11,6 +11,7 @@ import { Node, Identifier } from 'estree'; import add_to_set from '../../../utils/add_to_set'; import mark_each_block_bindings from '../shared/mark_each_block_bindings'; import handle_select_value_binding from './handle_select_value_binding'; +import { regex_box_size } from '../../../../utils/patterns'; export default class BindingWrapper { node: Binding; @@ -455,7 +456,12 @@ function get_value_from_dom( return x`$$value`; } - // node.name === 'input' && node.get_static_attribute_value('type') === 'range' }, + // resize events { event_names: ['elementresize'], filter: (_node: Element, name: string) => regex_dimensions.test(name) }, + { + event_names: ['elementresizecontentbox'], + filter: (_node: Element, name: string) => + regex_content_rect.test(name) ?? regex_content_box_size.test(name) + }, + + { + event_names: ['elementresizeborderbox'], + filter: (_node: Element, name: string) => + regex_border_box_size.test(name) + }, + + { + event_names: ['elementresizedevicepixelcontentbox'], + filter: (_node: Element, name: string) => + regex_device_pixel_content_box_size.test(name) + }, // media events { event_names: ['timeupdate'], @@ -747,13 +765,19 @@ export default class ElementWrapper extends Wrapper { `); binding_group.events.forEach(name => { - if (name === 'elementresize') { - // special case + const resizeListenerFunctions = { + elementresize: 'add_iframe_resize_listener', + elementresizecontentbox: 'resize_observer_content_box.observe', + elementresizeborderbox: 'resize_observer_border_box.observe', + elementresizedevicepixelcontentbox: 'resize_observer_device_pixel_content_box.observe' + }; + + if (name in resizeListenerFunctions) { const resize_listener = block.get_unique_name(`${this.var.name}_resize_listener`); block.add_variable(resize_listener); block.chunks.mount.push( - b`${resize_listener} = @add_resize_listener(${this.var}, ${callee}.bind(${this.var}));` + b`${resize_listener} = @${resizeListenerFunctions[name]}(${this.var}, ${callee}.bind(${this.var}));` ); block.chunks.destroy.push( diff --git a/src/compiler/utils/patterns.ts b/src/compiler/utils/patterns.ts index 9429d47227..f0fb08c7c9 100644 --- a/src/compiler/utils/patterns.ts +++ b/src/compiler/utils/patterns.ts @@ -22,3 +22,9 @@ export const regex_ends_with_underscore = /_$/; export const regex_invalid_variable_identifier_characters = /[^a-zA-Z0-9_$]/g; export const regex_dimensions = /^(?:offset|client)(?:Width|Height)$/; + +export const regex_content_rect = /^(?:contentRect)$/; +export const regex_content_box_size = /^(?:contentBoxSize)$/; +export const regex_border_box_size = /^(?:borderBoxSize)$/; +export const regex_device_pixel_content_box_size = /^(?:devicePixelContentBoxSize)$/; +export const regex_box_size = /^(?:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize)$/; diff --git a/src/runtime/internal/ResizeObserverSingleton.ts b/src/runtime/internal/ResizeObserverSingleton.ts new file mode 100644 index 0000000000..6d1e5b567b --- /dev/null +++ b/src/runtime/internal/ResizeObserverSingleton.ts @@ -0,0 +1,67 @@ +/** + * Resize observer singleton. + * One listener per element only! + * https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ + */ +export class ResizeObserverSingleton { + constructor(readonly options?: ResizeObserverOptions) {} + + observe(element: Element, listener: Listener) { + this._listeners.set(element, listener); + this._getObserver().observe(element, this.options); + return () => { + this._listeners.delete(element); + this._observer.unobserve(element); // this line can probably be removed + }; + } + + static readonly entries: WeakMap = 'WeakMap' in globalThis ? new WeakMap() : undefined; + + private readonly _listeners: WeakMap = 'WeakMap' in globalThis ? new WeakMap() : undefined; + private _observer?: ResizeObserver; + private _getObserver() { + return this._observer ?? (this._observer = new ResizeObserver((entries) => { + for (const entry of entries) { + ResizeObserverSingleton.entries.set(entry.target, entry); + this._listeners.get(entry.target)?.(entry); + } + })); + } +} + +type Listener = (entry: ResizeObserverEntry)=>any; + +// TODO: Remove this +interface ResizeObserverSize { + readonly blockSize: number; + readonly inlineSize: number; +} + +interface ResizeObserverEntry { + readonly borderBoxSize: readonly ResizeObserverSize[]; + readonly contentBoxSize: readonly ResizeObserverSize[]; + readonly contentRect: DOMRectReadOnly; + readonly devicePixelContentBoxSize: readonly ResizeObserverSize[]; + readonly target: Element; +} + +type ResizeObserverBoxOptions = 'border-box' | 'content-box' | 'device-pixel-content-box'; + +interface ResizeObserverOptions { + box?: ResizeObserverBoxOptions; +} + +interface ResizeObserver { + disconnect(): void; + observe(target: Element, options?: ResizeObserverOptions): void; + unobserve(target: Element): void; +} + +interface ResizeObserverCallback { + (entries: ResizeObserverEntry[], observer: ResizeObserver): void; +} + +declare let ResizeObserver: { + prototype: ResizeObserver; + new(callback: ResizeObserverCallback): ResizeObserver; +}; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 8a78accb50..4ffa9e4742 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -1,3 +1,4 @@ +import { ResizeObserverSingleton } from './ResizeObserverSingleton'; import { contenteditable_truthy_values, has_prop } from './utils'; // Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM @@ -698,7 +699,7 @@ export function is_crossorigin() { return crossorigin; } -export function add_resize_listener(node: HTMLElement, fn: () => void) { +export function add_iframe_resize_listener(node: HTMLElement, fn: () => void) { const computed_style = getComputedStyle(node); if (computed_style.position === 'static') { @@ -746,6 +747,11 @@ export function add_resize_listener(node: HTMLElement, fn: () => void) { }; } +export const resize_observer_content_box = new ResizeObserverSingleton({ box: 'content-box' }); +export const resize_observer_border_box = new ResizeObserverSingleton({ box: 'border-box' }); +export const resize_observer_device_pixel_content_box = new ResizeObserverSingleton({ box: 'device-pixel-content-box' }); +export { ResizeObserverSingleton }; + export function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } From 56351a3fabbc8ebd44723aa724b8050ab19a6dcd Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:19:30 +0200 Subject: [PATCH 06/15] chore: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5568dce50..ea9545aa65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Svelte changelog +## Unreleased + +* Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752)) +* Add support for resize observer bindings (`
`) ([#8022](https://github.com/sveltejs/svelte/pull/8022)) + ## 3.58.0 * Add `bind:innerText` for `contenteditable` elements ([#3311](https://github.com/sveltejs/svelte/issues/3311)) From cd690e025bb2ad2e50cfc417d4b58408c779f080 Mon Sep 17 00:00:00 2001 From: James Scott-Brown Date: Tue, 11 Apr 2023 13:05:22 +0100 Subject: [PATCH 07/15] docs: clarify meaning of "this" in a comment (#8478) --- site/content/docs/02-component-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/02-component-format.md b/site/content/docs/02-component-format.md index d9f7a35662..f1563a62c6 100644 --- a/site/content/docs/02-component-format.md +++ b/site/content/docs/02-component-format.md @@ -286,7 +286,7 @@ You cannot `export default`, since the default export is the component itself. - -
- -
-``` - -## Attributes and props - -By default, attributes work exactly like their HTML counterparts. - -```svelte -
- -
-``` - -As in HTML, values may be unquoted. - -```svelte - -``` - -Attribute values can contain JavaScript expressions. - -```svelte -page {p} -``` - -Or they can _be_ JavaScript expressions. - -```svelte - -``` - -Boolean attributes are included on the element if their value is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and excluded if it's [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy). - -All other attributes are included unless their value is [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` or `undefined`). - -```svelte - -
This div has no title attribute
-``` - -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: - -```svelte - -``` - -When the attribute name and value match (`name={name}`), they can be replaced with `{name}`. - -```svelte - - - -``` - -By convention, values passed to components are referred to as _properties_ or _props_ rather than _attributes_, which are a feature of the DOM. - -As with elements, `name={name}` can be replaced with the `{name}` shorthand. - -```svelte - -``` - -_Spread attributes_ allow many attributes or properties to be passed to an element or component at once. - -An element or component can have multiple spread attributes, interspersed with regular ones. - -```svelte - -``` - -_`$$props`_ references all props that are passed to a component, including ones that are not declared with `export`. It is not generally recommended, as it is difficult for Svelte to optimise. But it can be useful in rare cases – for example, when you don't know at compile time what props might be passed to a component. - -```svelte - -``` - -_`$$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. It shares the same optimisation problems as _`$$props`_, and is likewise not recommended. - -```svelte - -``` - -> 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. - -> Sometimes, the attribute order matters as Svelte sets attributes sequentially in JavaScript. For example, ``, Svelte will attempt to set the value to `1` (rounding up from 0.5 as the step by default is 1), and then set the step to `0.1`. To fix this, change it to ``. - -> Another example is ``. Svelte will set the img `src` before making the img element `loading="lazy"`, which is probably too late. Change this to `` to make the image lazily loaded. - -## Text expressions - -```svelte -{expression} -``` - -Text can also contain JavaScript expressions: - -> If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses. - -```svelte -

Hello {name}!

-

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

- -
{/^[A-Za-z ]+$/.test(value) ? x : y}
-``` - -## Comments - -You can use HTML comments inside components. - -```svelte -

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. - -```svelte - - -``` diff --git a/site/content/docs/02-template-syntax/02-basic-markup.md b/site/content/docs/02-template-syntax/02-basic-markup.md new file mode 100644 index 0000000000..83f11861e5 --- /dev/null +++ b/site/content/docs/02-template-syntax/02-basic-markup.md @@ -0,0 +1,134 @@ +--- +title: Basic markup +--- + +## Tags + +A lowercase tag, like `
`, denotes a regular HTML element. A capitalised tag, such as `` or ``, indicates a _component_. + +```svelte + + +
+ +
+``` + +## Attributes and props + +By default, attributes work exactly like their HTML counterparts. + +```svelte +
+ +
+``` + +As in HTML, values may be unquoted. + +```svelte + +``` + +Attribute values can contain JavaScript expressions. + +```svelte +page {p} +``` + +Or they can _be_ JavaScript expressions. + +```svelte + +``` + +Boolean attributes are included on the element if their value is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and excluded if it's [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy). + +All other attributes are included unless their value is [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` or `undefined`). + +```svelte + +
This div has no title attribute
+``` + +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: + +```svelte + +``` + +When the attribute name and value match (`name={name}`), they can be replaced with `{name}`. + +```svelte + + + +``` + +By convention, values passed to components are referred to as _properties_ or _props_ rather than _attributes_, which are a feature of the DOM. + +As with elements, `name={name}` can be replaced with the `{name}` shorthand. + +```svelte + +``` + +_Spread attributes_ allow many attributes or properties to be passed to an element or component at once. + +An element or component can have multiple spread attributes, interspersed with regular ones. + +```svelte + +``` + +_`$$props`_ references all props that are passed to a component, including ones that are not declared with `export`. It is not generally recommended, as it is difficult for Svelte to optimise. But it can be useful in rare cases – for example, when you don't know at compile time what props might be passed to a component. + +```svelte + +``` + +_`$$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. It shares the same optimisation problems as _`$$props`_, and is likewise not recommended. + +```svelte + +``` + +> 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. + +> Sometimes, the attribute order matters as Svelte sets attributes sequentially in JavaScript. For example, ``, Svelte will attempt to set the value to `1` (rounding up from 0.5 as the step by default is 1), and then set the step to `0.1`. To fix this, change it to ``. + +> Another example is ``. Svelte will set the img `src` before making the img element `loading="lazy"`, which is probably too late. Change this to `` to make the image lazily loaded. + +## Text expressions + +```svelte +{expression} +``` + +Text can also contain JavaScript expressions: + +> If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses. + +```svelte +

Hello {name}!

+

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

+ +
{/^[A-Za-z ]+$/.test(value) ? x : y}
+``` + +## Comments + +You can use HTML comments inside components. + +```svelte +

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. + +```svelte + + +``` diff --git a/site/content/docs/02-template-syntax/02-logic-blocks.md b/site/content/docs/02-template-syntax/03-logic-blocks.md similarity index 100% rename from site/content/docs/02-template-syntax/02-logic-blocks.md rename to site/content/docs/02-template-syntax/03-logic-blocks.md diff --git a/site/content/docs/02-template-syntax/03-special-tags.md b/site/content/docs/02-template-syntax/04-special-tags.md similarity index 100% rename from site/content/docs/02-template-syntax/03-special-tags.md rename to site/content/docs/02-template-syntax/04-special-tags.md diff --git a/site/content/docs/02-template-syntax/04-element-directives.md b/site/content/docs/02-template-syntax/05-element-directives.md similarity index 100% rename from site/content/docs/02-template-syntax/04-element-directives.md rename to site/content/docs/02-template-syntax/05-element-directives.md diff --git a/site/content/docs/02-template-syntax/05-component-directives.md b/site/content/docs/02-template-syntax/06-component-directives.md similarity index 100% rename from site/content/docs/02-template-syntax/05-component-directives.md rename to site/content/docs/02-template-syntax/06-component-directives.md diff --git a/site/content/docs/02-template-syntax/06-special-elements.md b/site/content/docs/02-template-syntax/07-special-elements.md similarity index 100% rename from site/content/docs/02-template-syntax/06-special-elements.md rename to site/content/docs/02-template-syntax/07-special-elements.md diff --git a/sites/svelte.dev/package-lock.json b/sites/svelte.dev/package-lock.json index 3ea482a9b5..2ac8620774 100644 --- a/sites/svelte.dev/package-lock.json +++ b/sites/svelte.dev/package-lock.json @@ -20,7 +20,7 @@ "devDependencies": { "@resvg/resvg-js": "^2.4.1", "@sveltejs/adapter-vercel": "^2.4.1", - "@sveltejs/kit": "^1.15.1", + "@sveltejs/kit": "^1.15.4", "@sveltejs/site-kit": "^3.4.0", "@sveltejs/vite-plugin-svelte": "^2.0.4", "@types/marked": "^4.0.8", @@ -1248,9 +1248,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.1.tgz", - "integrity": "sha512-Wexy3N+COoClTuRawVJRbLoH5HFxNrXG3uoHt/Yd5IGx8WAcJM9Nj/CcBLw/tjCR9uDDYMnx27HxuPy3YIYQUA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.4.tgz", + "integrity": "sha512-m+Tid9nbtFawmiu85lDlal0AQ7UeuV48UsuKMe06QLr3ntMQSUzIPqyswNRZqFrar6NhVTUXQ0aO61M3U4MWpQ==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json index 6a3104aa3a..543264f913 100644 --- a/sites/svelte.dev/package.json +++ b/sites/svelte.dev/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@resvg/resvg-js": "^2.4.1", "@sveltejs/adapter-vercel": "^2.4.1", - "@sveltejs/kit": "^1.15.1", + "@sveltejs/kit": "^1.15.4", "@sveltejs/site-kit": "^3.4.0", "@sveltejs/vite-plugin-svelte": "^2.0.4", "@types/marked": "^4.0.8", From ec733593e6165fa51c4a2d8a4c9cb973549cef0c Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Tue, 11 Apr 2023 23:18:26 +0530 Subject: [PATCH 09/15] fix: Old site redirects --- sites/svelte.dev/src/routes/docs/+page.svelte | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sites/svelte.dev/src/routes/docs/+page.svelte b/sites/svelte.dev/src/routes/docs/+page.svelte index f6ef383623..da1672c297 100644 --- a/sites/svelte.dev/src/routes/docs/+page.svelte +++ b/sites/svelte.dev/src/routes/docs/+page.svelte @@ -137,14 +137,15 @@ const pages_regex_map = new Map([ // Basic ones [/(before-we-begin|getting-started)$/i, 'introduction'], - [/(component-format|template-syntax)$/i, 'dot-svelte-files'], + [/template-syntax$/i, 'basic-markup'], + [/component-format$/i, 'svelte-components'], [/run-time$/i, 'svelte'], [/compile-time$/i, 'svelte-compiler'], [/(accessibility-warnings)$/i, '$1'], // component-format- - [/component-format-(script|style|script-context-module)$/i, 'dot-svelte-files#$1'], - [/component-format-(?:script)(?:-?(.*))$/i, 'dot-svelte-files#$1'], + [/component-format-(script|style|script-context-module)$/i, 'svelte-components#$1'], + [/component-format-(?:script)(?:-?(.*))$/i, 'svelte-components#$1'], // template-syntax [/template-syntax-((?:element|component)-directives)-?(.*)/i, '$1#$2'], @@ -152,10 +153,7 @@ [/template-syntax-(?:slot)-?(.*)/i, 'special-elements#$1'], [/template-syntax-(if|each|await|key)$/i, 'logic-blocks#$1'], [/template-syntax-(const|debug|html)$/i, 'special-tags#$1'], - [ - /template-syntax-(tags|attributes-and-props|text-expressions|comments)$/i, - 'dot-svelte-files#$1' - ], + [/template-syntax-(tags|attributes-and-props|text-expressions|comments)$/i, 'basic-markup#$1'], // !!!! This one should stay at the bottom of `template-syntax`, or it may end up hijacking logic blocks and special tags [/template-syntax-(.+)/i, 'special-elements#$1'], From 71d173a4c0c48205f98b71015ebc8ee0cfadcb7a Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Tue, 11 Apr 2023 23:25:20 +0530 Subject: [PATCH 10/15] feat: Add home slot --- sites/svelte.dev/src/routes/+layout.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sites/svelte.dev/src/routes/+layout.svelte b/sites/svelte.dev/src/routes/+layout.svelte index 6d48972e86..8c96bed230 100644 --- a/sites/svelte.dev/src/routes/+layout.svelte +++ b/sites/svelte.dev/src/routes/+layout.svelte @@ -16,7 +16,11 @@
-