From b20fb114a657abcd63a79d25789428258f952b3a Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 21 Sep 2022 21:48:01 +0800 Subject: [PATCH 01/46] allow nullish values for component event handlers (#7863) --- src/runtime/internal/Component.ts | 6 ++++++ .../samples/component-events-nullish/Widget.svelte | 14 ++++++++++++++ .../samples/component-events-nullish/_config.js | 9 +++++++++ .../samples/component-events-nullish/main.svelte | 6 ++++++ 4 files changed, 35 insertions(+) create mode 100644 test/runtime/samples/component-events-nullish/Widget.svelte create mode 100644 test/runtime/samples/component-events-nullish/_config.js create mode 100644 test/runtime/samples/component-events-nullish/main.svelte diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 3e48010547..0e9e4f0610 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -212,6 +212,9 @@ if (typeof HTMLElement === 'function') { $on(type, callback) { // TODO should this delegate to addEventListener? + if (!is_function(callback)) { + return noop; + } const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); callbacks.push(callback); @@ -244,6 +247,9 @@ export class SvelteComponent { } $on(type, callback) { + if (!is_function(callback)) { + return noop; + } const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); callbacks.push(callback); diff --git a/test/runtime/samples/component-events-nullish/Widget.svelte b/test/runtime/samples/component-events-nullish/Widget.svelte new file mode 100644 index 0000000000..07ae574403 --- /dev/null +++ b/test/runtime/samples/component-events-nullish/Widget.svelte @@ -0,0 +1,14 @@ + + ``` +--- +It is important to note that the reactive blocks are ordered via simple static analysis at compile time, and all the compiler looks at are the variables that are assigned to and used within the block itself, not in any functions called by them. This means that `yDependent` will not be updated when `x` is updated in the following example: + +```sv + +``` + +Moving the line `$: yDependent = y` bellow `$: setY(x)` will cause `yDependent` to be updated when `x` is updated. + --- If a statement consists entirely of an assignment to an undeclared variable, Svelte will inject a `let` declaration on your behalf. From 158ec43d99376ccb70c374e306552151b1dbf63b Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 6 Oct 2022 22:29:48 +0800 Subject: [PATCH 16/46] [fix] do not warn about missing props for bindings (#6583) --- src/compiler/compile/render_dom/index.ts | 21 +++++----- .../samples/capture-inject-state/expected.js | 22 +++++------ test/js/samples/debug-empty/expected.js | 14 +++---- .../debug-foo-bar-baz-things/expected.js | 38 +++++++++---------- test/js/samples/debug-foo/expected.js | 22 +++++------ .../expected.js | 14 +++---- .../Foo.svelte | 8 ++++ .../_config.js | 9 +++++ .../main.svelte | 8 ++++ 9 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/_config.js create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 5fdac9bce4..173d93d5dd 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -121,7 +121,7 @@ export default function dom( const accessors = []; const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`; - let dev_props_check: Node[] | Node; + let missing_props_check: Node[] | Node; let inject_state: Expression; let capture_state: Expression; let props_inject: Node[] | Node; @@ -227,13 +227,13 @@ export default function dom( const expected = props.filter(prop => prop.writable && !prop.initialised); if (expected.length) { - dev_props_check = b` - const { ctx: #ctx } = this.$$; - const props = ${options.customElement ? x`this.attributes` : x`options.props || {}`}; - ${expected.map(prop => b` - if (${renderer.reference(prop.name)} === undefined && !('${prop.export_name}' in props)) { - @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); - }`)} + missing_props_check = b` + $$self.$$.on_mount.push(function () { + ${expected.map(prop => b` + if (${prop.name} === undefined && !(('${prop.export_name}' in $$props) || $$self.$$.bound[$$self.$$.props['${prop.export_name}']])) { + @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); + }`)} + }); `; } @@ -476,6 +476,7 @@ export default function dom( ${instance_javascript} + ${missing_props_check} ${unknown_props_check} ${renderer.binding_groups.size > 0 && b`const $$binding_groups = [${[...renderer.binding_groups.keys()].map(_ => x`[]`)}];`} @@ -533,8 +534,6 @@ export default function dom( @init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, null, ${dirty}); - ${dev_props_check} - if (options) { if (options.target) { @insert(options.target, this, options.anchor); @@ -594,8 +593,6 @@ export default function dom( super(${options.dev && 'options'}); @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${optional_parameters}); ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} - - ${dev_props_check} } } `[0] as ClassDeclaration; diff --git a/test/js/samples/capture-inject-state/expected.js b/test/js/samples/capture-inject-state/expected.js index 58e1879553..882b3e9cf6 100644 --- a/test/js/samples/capture-inject-state/expected.js +++ b/test/js/samples/capture-inject-state/expected.js @@ -113,6 +113,17 @@ function instance($$self, $$props, $$invalidate) { let { alias: realName } = $$props; let local; let shadowedByModule; + + $$self.$$.on_mount.push(function () { + if (prop === undefined && !('prop' in $$props || $$self.$$.bound[$$self.$$.props['prop']])) { + console.warn(" was created without expected prop 'prop'"); + } + + if (realName === undefined && !('alias' in $$props || $$self.$$.bound[$$self.$$.props['alias']])) { + console.warn(" was created without expected prop 'alias'"); + } + }); + const writable_props = ['prop', 'alias']; Object.keys($$props).forEach(key => { @@ -166,17 +177,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*prop*/ ctx[0] === undefined && !('prop' in props)) { - console.warn(" was created without expected prop 'prop'"); - } - - if (/*realName*/ ctx[1] === undefined && !('alias' in props)) { - console.warn(" was created without expected prop 'alias'"); - } } get prop() { diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 55303b41b7..ee304591cb 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -72,6 +72,13 @@ function instance($$self, $$props, $$invalidate) { let { $$slots: slots = {}, $$scope } = $$props; validate_slots('Component', slots, []); let { name } = $$props; + + $$self.$$.on_mount.push(function () { + if (name === undefined && !('name' in $$props || $$self.$$.bound[$$self.$$.props['name']])) { + console.warn(" was created without expected prop 'name'"); + } + }); + const writable_props = ['name']; Object.keys($$props).forEach(key => { @@ -106,13 +113,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*name*/ ctx[0] === undefined && !('name' in props)) { - console.warn(" was created without expected prop 'name'"); - } } get name() { diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index 3377d35018..02be04c26e 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -176,6 +176,25 @@ function instance($$self, $$props, $$invalidate) { let { foo } = $$props; let { bar } = $$props; let { baz } = $$props; + + $$self.$$.on_mount.push(function () { + if (things === undefined && !('things' in $$props || $$self.$$.bound[$$self.$$.props['things']])) { + console.warn(" was created without expected prop 'things'"); + } + + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + + if (bar === undefined && !('bar' in $$props || $$self.$$.bound[$$self.$$.props['bar']])) { + console.warn(" was created without expected prop 'bar'"); + } + + if (baz === undefined && !('baz' in $$props || $$self.$$.bound[$$self.$$.props['baz']])) { + console.warn(" was created without expected prop 'baz'"); + } + }); + const writable_props = ['things', 'foo', 'bar', 'baz']; Object.keys($$props).forEach(key => { @@ -216,25 +235,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*things*/ ctx[0] === undefined && !('things' in props)) { - console.warn(" was created without expected prop 'things'"); - } - - if (/*foo*/ ctx[1] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } - - if (/*bar*/ ctx[2] === undefined && !('bar' in props)) { - console.warn(" was created without expected prop 'bar'"); - } - - if (/*baz*/ ctx[3] === undefined && !('baz' in props)) { - console.warn(" was created without expected prop 'baz'"); - } } get things() { diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index 518897c237..84dedb0a91 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -168,6 +168,17 @@ function instance($$self, $$props, $$invalidate) { validate_slots('Component', slots, []); let { things } = $$props; let { foo } = $$props; + + $$self.$$.on_mount.push(function () { + if (things === undefined && !('things' in $$props || $$self.$$.bound[$$self.$$.props['things']])) { + console.warn(" was created without expected prop 'things'"); + } + + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + }); + const writable_props = ['things', 'foo']; Object.keys($$props).forEach(key => { @@ -204,17 +215,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*things*/ ctx[0] === undefined && !('things' in props)) { - console.warn(" was created without expected prop 'things'"); - } - - if (/*foo*/ ctx[1] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } } get things() { diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index 5e10ec55f8..a94cf7b5f2 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -69,6 +69,13 @@ function instance($$self, $$props, $$invalidate) { validate_slots('Component', slots, []); let { foo } = $$props; let bar; + + $$self.$$.on_mount.push(function () { + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + }); + const writable_props = ['foo']; Object.keys($$props).forEach(key => { @@ -110,13 +117,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*foo*/ ctx[0] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } } get foo() { diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte b/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte new file mode 100644 index 0000000000..60278c4845 --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte @@ -0,0 +1,8 @@ + + +
{w} {x} {y}
\ No newline at end of file diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js b/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js new file mode 100644 index 0000000000..ececa00a8c --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js @@ -0,0 +1,9 @@ +export default { + compileOptions: { + dev: true + }, + + warnings: [ + " was created without expected prop 'y'" + ] +}; diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte b/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte new file mode 100644 index 0000000000..eab3c8336f --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte @@ -0,0 +1,8 @@ + + + \ No newline at end of file From d04b1cca24fdfb9f170d0a5f747d94eabe7ee22b Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Thu, 6 Oct 2022 23:30:42 +0900 Subject: [PATCH 17/46] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ba64d0bb..d12212be87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Supports nullish values for component event handlers ([#7568](https://github.com/sveltejs/svelte/issues/7568)) * Better error message when specifying an invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) * Fix to call component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) +* Fix false positive warnings about props binding in dev mode ([#4457](https://github.com/sveltejs/svelte/issues/4457)) ## 3.50.1 From 8de7931c64b46d22fc303adce14eb3f680c8ca66 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 7 Oct 2022 08:23:12 +0800 Subject: [PATCH 18/46] [feat] better error message for invalid logic block placement (#7862) * better error message for invalid logic block placement * include checking for {@html} tags in invalid location --- src/compiler/parse/errors.ts | 8 ++++++++ src/compiler/parse/state/tag.ts | 19 ++++++++++++++++--- .../html-block-in-attribute/errors.json | 9 +++++++++ .../html-block-in-attribute/input.svelte | 1 + .../html-block-in-textarea/errors.json | 9 +++++++++ .../html-block-in-textarea/input.svelte | 3 +++ .../logic-block-in-attribute/errors.json | 9 +++++++++ .../logic-block-in-attribute/input.svelte | 1 + .../logic-block-in-textarea/errors.json | 9 +++++++++ .../logic-block-in-textarea/input.svelte | 5 +++++ 10 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 test/validator/samples/html-block-in-attribute/errors.json create mode 100644 test/validator/samples/html-block-in-attribute/input.svelte create mode 100644 test/validator/samples/html-block-in-textarea/errors.json create mode 100644 test/validator/samples/html-block-in-textarea/input.svelte create mode 100644 test/validator/samples/logic-block-in-attribute/errors.json create mode 100644 test/validator/samples/logic-block-in-attribute/input.svelte create mode 100644 test/validator/samples/logic-block-in-textarea/errors.json create mode 100644 test/validator/samples/logic-block-in-textarea/input.svelte diff --git a/src/compiler/parse/errors.ts b/src/compiler/parse/errors.ts index 63bd5b0919..26f03c0cb1 100644 --- a/src/compiler/parse/errors.ts +++ b/src/compiler/parse/errors.ts @@ -107,6 +107,14 @@ export default { code: `invalid-${slug}-placement`, message: `<${name}> tags cannot be inside elements or blocks` }), + invalid_logic_block_placement: (location: string, name: string) => ({ + code: 'invalid-logic-block-placement', + message: `{#${name}} logic block cannot be ${location}` + }), + invalid_tag_placement: (location: string, name: string) => ({ + code: 'invalid-tag-placement', + message: `{@${name}} tag cannot be ${location}` + }), invalid_ref_directive: (name: string) => ({ code: 'invalid-ref-directive', message: `The ref directive is no longer supported — use \`bind:this={${name}}\` instead` diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index efce375b7e..4be47f25b0 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -219,7 +219,8 @@ export default function tag(parser: Parser) { element.children = read_sequence( parser, () => - /^<\/textarea(\s[^>]*)?>/i.test(parser.template.slice(parser.index)) + /^<\/textarea(\s[^>]*)?>/i.test(parser.template.slice(parser.index)), + 'inside \ No newline at end of file diff --git a/test/validator/samples/logic-block-in-attribute/errors.json b/test/validator/samples/logic-block-in-attribute/errors.json new file mode 100644 index 0000000000..ebf68938e7 --- /dev/null +++ b/test/validator/samples/logic-block-in-attribute/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-logic-block-placement", + "message": "{#if} logic block cannot be in attribute value", + "start": { "line": 1, "column": 12, "character": 12 }, + "end": { "line": 1, "column": 12, "character": 12 }, + "pos": 12 + } +] diff --git a/test/validator/samples/logic-block-in-attribute/input.svelte b/test/validator/samples/logic-block-in-attribute/input.svelte new file mode 100644 index 0000000000..241529527c --- /dev/null +++ b/test/validator/samples/logic-block-in-attribute/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/validator/samples/logic-block-in-textarea/errors.json b/test/validator/samples/logic-block-in-textarea/errors.json new file mode 100644 index 0000000000..b1d73d4903 --- /dev/null +++ b/test/validator/samples/logic-block-in-textarea/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-logic-block-placement", + "message": "{#each} logic block cannot be inside \ No newline at end of file From 2b7393885724ed158dd3ce1b9e53b9737a5f3c3e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 7 Oct 2022 08:25:04 +0800 Subject: [PATCH 19/46] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d12212be87..6ca984af7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Better error message when specifying an invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) * Fix to call component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) * Fix false positive warnings about props binding in dev mode ([#4457](https://github.com/sveltejs/svelte/issues/4457)) +* Better error message when using logic blocks or tags in invalid place ([#7552](https://github.com/sveltejs/svelte/issues/7552)) ## 3.50.1 From 220325cd9feea12f9a9bc5eb8df5120f856b1bc9 Mon Sep 17 00:00:00 2001 From: Samuel Stroschein <35429197+samuelstroschein@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:11:46 +0200 Subject: [PATCH 20/46] [docs] add inline documentation to svelte runtime functions (#7846) * add documentation * add links do docs --- src/runtime/internal/lifecycle.ts | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index fbbeca9a67..e75bbdc501 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -11,18 +11,47 @@ export function get_current_component() { return current_component; } +/** + * Schedules a callback to run immediately before the component is updated after any state change. + * + * The first time the callback runs will be before the initial `onMount` + * + * https://svelte.dev/docs#run-time-svelte-beforeupdate + */ export function beforeUpdate(fn: () => any) { get_current_component().$$.before_update.push(fn); } +/** + * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. + * It must be called during the component's initialisation (but doesn't need to live *inside* the component; + * it can be called from an external module). + * + * `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). + * + * https://svelte.dev/docs#run-time-svelte-onmount + */ export function onMount(fn: () => any) { get_current_component().$$.on_mount.push(fn); } +/** + * Schedules a callback to run immediately after the component has been updated. + * + * The first time the callback runs will be after the initial `onMount` + */ export function afterUpdate(fn: () => any) { get_current_component().$$.after_update.push(fn); } +/** + * Schedules a callback to run immediately before the component is unmounted. + * + * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the + * only one that runs inside a server-side component. + * + * https://svelte.dev/docs#run-time-svelte-ondestroy + */ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } @@ -31,6 +60,18 @@ export interface DispatchOptions { cancelable?: boolean; } +/** + * Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). + * Event dispatchers are functions that can take two arguments: `name` and `detail`. + * + * Component events created with `createEventDispatcher` create a + * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). + * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). + * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) + * property and can contain any type of data. + * + * https://svelte.dev/docs#run-time-svelte-createeventdispatcher + */ export function createEventDispatcher(): < EventKey extends Extract >( @@ -57,19 +98,47 @@ export function createEventDispatcher(): < }; } +/** + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component + * (including slotted content) with `getContext`. + * + * Like lifecycle functions, this must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-setcontext + */ export function setContext(key, context: T): T { get_current_component().$$.context.set(key, context); return context; } +/** + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-getcontext + */ export function getContext(key): T { return get_current_component().$$.context.get(key); } +/** + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you + * programmatically create a component and want to pass the existing context to it. + * + * https://svelte.dev/docs#run-time-svelte-getallcontexts + */ export function getAllContexts = Map>(): T { return get_current_component().$$.context; } +/** + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-hascontext + */ export function hasContext(key): boolean { return get_current_component().$$.context.has(key); } From ff2759e1437559669bd032abaff6920c2fc46570 Mon Sep 17 00:00:00 2001 From: Geoff Rich <4992896+geoffrich@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:25:19 -0700 Subject: [PATCH 21/46] [docs] fix typo in getting started (#7918) --- site/content/docs/01-getting-started.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md index 8ae5b1fb9c..e8aae80f23 100644 --- a/site/content/docs/01-getting-started.md +++ b/site/content/docs/01-getting-started.md @@ -7,6 +7,7 @@ title: Getting started To try Svelte in an interactive online environment you can try [the REPL](https://svelte.dev/repl) or [StackBlitz](https://node.new/svelte). To create a project locally, run: + ``` npm create vite@latest myapp -- --template svelte cd myapp @@ -16,6 +17,6 @@ npm run dev Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in beta). -See the SvelteSociety website run by the Svelte community for a list of integrations with various [tooling and editots](https://sveltesociety.dev/tools). +The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and the Svelte community has created a list of integrations with various other [tooling and editors](https://sveltesociety.dev/tools). If you're having trouble, get help on [Discord](https://svelte.dev/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte). From 1c659193a0b1fdf6e472be182fb600a430784d20 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:34:31 -0700 Subject: [PATCH 22/46] [docs] update SvelteKit status (#7919) --- site/content/docs/01-getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md index e8aae80f23..48cdbcc231 100644 --- a/site/content/docs/01-getting-started.md +++ b/site/content/docs/01-getting-started.md @@ -15,7 +15,7 @@ npm install npm run dev ``` -Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in beta). +Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in release candidate status). The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and the Svelte community has created a list of integrations with various other [tooling and editors](https://sveltesociety.dev/tools). From 6ac7038e47c38221f16f0f73af93c9024ff2a18c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 10 Oct 2022 13:15:45 -0400 Subject: [PATCH 23/46] -> v3.51.0 --- CHANGELOG.md | 34 +++++++++++++++++----------------- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca984af7d..cbf22201f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,26 +1,26 @@ # Svelte changelog -## Unreleased +## 3.51.0 -* Fix hydration issue with using `{@html}` and components in `svelte:head` ([#4533](https://github.com/sveltejs/svelte/issues/4533), [#6463](https://github.com/sveltejs/svelte/issues/6463), [#7444](https://github.com/sveltejs/svelte/issues/7444)) -* Warn instead of throwing error if `` is void tag ([#7566](https://github.com/sveltejs/svelte/issues/7566)) -* Treat `inert` as boolean attribute ([#7785](https://github.com/sveltejs/svelte/pull/7785)) -* Supporting scoped style for `` ([#7443](https://github.com/sveltejs/svelte/issues/7443)) -* Supports SVG elements with ``([#7613](https://github.com/sveltejs/svelte/issues/7613)) -* Warn user when binding on a `{...rest}` object in `{#each}` block ([#6860](https://github.com/sveltejs/svelte/issues/6860)) -* Supports `--style-props` for `` ([#7461](https://github.com/sveltejs/svelte/issues/7461)) -* Supports `--style-props` for SVG components ([#7808](https://github.com/sveltejs/svelte/issues/7808)) * Add a11y warnings: - * `a11y-no-noninteractive-tabindex`: check for tabindex on non-interactive elements ([#6693](https://github.com/sveltejs/svelte/pull/6693)) * `a11y-click-events-have-key-events`: check if click event is accompanied by key events ([#5073](https://github.com/sveltejs/svelte/pull/5073)) -* `a11y-role-has-required-aria-props` do not warn when elements matched their semantic role ([#7838](https://github.com/sveltejs/svelte/pull/7838)) -* Supports custom element in `` ([#7766](https://github.com/sveltejs/svelte/pull/7766)) -* Improve performance of custom element data setting in `` ([#7869](https://github.com/sveltejs/svelte/pull/7869)) + * `a11y-no-noninteractive-tabindex`: check for tabindex on non-interactive elements ([#6693](https://github.com/sveltejs/svelte/pull/6693)) +* Warn when two-way binding to `{...rest}` object in `{#each}` block ([#6860](https://github.com/sveltejs/svelte/issues/6860)) +* Support `--style-props` on `` ([#7461](https://github.com/sveltejs/svelte/issues/7461)) * Supports nullish values for component event handlers ([#7568](https://github.com/sveltejs/svelte/issues/7568)) -* Better error message when specifying an invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) -* Fix to call component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) -* Fix false positive warnings about props binding in dev mode ([#4457](https://github.com/sveltejs/svelte/issues/4457)) -* Better error message when using logic blocks or tags in invalid place ([#7552](https://github.com/sveltejs/svelte/issues/7552)) +* Supports SVG elements with ``([#7613](https://github.com/sveltejs/svelte/issues/7613)) +* Treat `inert` as boolean attribute ([#7785](https://github.com/sveltejs/svelte/pull/7785)) +* Support `--style-props` for SVG components ([#7808](https://github.com/sveltejs/svelte/issues/7808)) +* Fix false positive dev warnings about unset props when they are bound ([#4457](https://github.com/sveltejs/svelte/issues/4457)) +* Fix hydration with `{@html}` and components in `` ([#4533](https://github.com/sveltejs/svelte/issues/4533), [#6463](https://github.com/sveltejs/svelte/issues/6463), [#7444](https://github.com/sveltejs/svelte/issues/7444)) +* Support scoped style for `` ([#7443](https://github.com/sveltejs/svelte/issues/7443)) +* Improve error message for invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) +* Improve error message when using logic blocks or tags at invalid location ([#7552](https://github.com/sveltejs/svelte/issues/7552)) +* Warn instead of throwing error if `` is a void tag ([#7566](https://github.com/sveltejs/svelte/issues/7566)) +* Supports custom elements in `` ([#7733](https://github.com/sveltejs/svelte/issues/7733)) +* Fix calling component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) +* Do not generate `a11y-role-has-required-aria-props` warning when elements match their semantic role ([#7837](https://github.com/sveltejs/svelte/issues/7837)) +* Improve performance of custom element data setting in `` ([#7869](https://github.com/sveltejs/svelte/pull/7869)) ## 3.50.1 diff --git a/package-lock.json b/package-lock.json index 23ccc6b66f..dec2c8ef0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.50.1", + "version": "3.51.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 836fa33267..bf6b9e7754 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.50.1", + "version": "3.51.0", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 26a428972b7865d3871e05368df5e7b47bbf7e5b Mon Sep 17 00:00:00 2001 From: metonym Date: Thu, 13 Oct 2022 05:49:00 -0700 Subject: [PATCH 24/46] [fix] "not interactive" -> "noninteractive" (#7930) --- site/content/docs/06-accessibility-warnings.md | 2 +- src/compiler/compile/compiler_warnings.ts | 2 +- .../samples/a11y-no-nointeractive-tabindex/warnings.json | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/content/docs/06-accessibility-warnings.md b/site/content/docs/06-accessibility-warnings.md index c82ef21fcb..8be8eb888c 100644 --- a/site/content/docs/06-accessibility-warnings.md +++ b/site/content/docs/06-accessibility-warnings.md @@ -268,7 +268,7 @@ Some HTML elements have default ARIA roles. Giving these elements an ARIA role t Tab key navigation should be limited to elements on the page that can be interacted with. ```sv - +
``` diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index f194090870..3a11e04bd1 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -185,7 +185,7 @@ export default { }), a11y_no_noninteractive_tabindex: { code: 'a11y-no-noninteractive-tabindex', - message: 'A11y: not interactive element cannot have positive tabIndex value' + message: 'A11y: noninteractive element cannot have positive tabIndex value' }, redundant_event_modifier_for_touch: { code: 'redundant-event-modifier', diff --git a/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json b/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json index 97b8ccd9b3..740d9b346c 100644 --- a/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json +++ b/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json @@ -6,7 +6,7 @@ "column": 20, "line": 11 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 221, "start": { "character": 221, @@ -21,7 +21,7 @@ "column": 35, "line": 12 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 242, "start": { "character": 242, @@ -36,7 +36,7 @@ "column": 24, "line": 13 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 278, "start": { "character": 278, @@ -51,7 +51,7 @@ "column": 26, "line": 14 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 303, "start": { "character": 303, From 0eba57113be4185836a76ba894cbc29f366da252 Mon Sep 17 00:00:00 2001 From: Hofer Ivan Date: Thu, 13 Oct 2022 14:54:30 +0200 Subject: [PATCH 25/46] [chore]: store regexp as variable instead of defining it inline (#7716) * store regexp as variable instead of defining it inline * fix naming of `regex_quoted_value` * some more variables * optimize `.replace() calls * restore formatting changes * optimize `parser.*` calls * small refactor * optimize `.test() calls * rename some variables * fix tests * rename pattern variables * extract common regexes into `patters.ts` * rename variables to use snake_case * fix trim --- src/compiler/compile/Component.ts | 22 +++++++----- src/compiler/compile/css/Selector.ts | 15 +++++--- src/compiler/compile/css/Stylesheet.ts | 9 +++-- src/compiler/compile/index.ts | 7 ++-- src/compiler/compile/nodes/Binding.ts | 4 +-- src/compiler/compile/nodes/Element.ts | 26 +++++++++----- src/compiler/compile/nodes/EventHandler.ts | 6 ++-- src/compiler/compile/nodes/Head.ts | 3 +- src/compiler/compile/nodes/InlineComponent.ts | 7 ++-- src/compiler/compile/nodes/Text.ts | 7 ++-- .../compile/nodes/shared/AbstractBlock.ts | 4 ++- .../compile/nodes/shared/Expression.ts | 4 ++- src/compiler/compile/render_dom/Block.ts | 3 +- src/compiler/compile/render_dom/index.ts | 3 +- .../render_dom/wrappers/Element/Attribute.ts | 17 +++++---- .../wrappers/Element/StyleAttribute.ts | 13 ++++--- .../render_dom/wrappers/Element/index.ts | 36 ++++++++++++------- .../compile/render_dom/wrappers/Fragment.ts | 3 +- .../wrappers/InlineComponent/index.ts | 4 ++- .../compile/render_dom/wrappers/Text.ts | 4 ++- .../render_dom/wrappers/shared/add_actions.ts | 4 ++- .../shared/create_debugging_comment.ts | 4 ++- .../compile/render_ssr/handlers/Element.ts | 6 ++-- .../handlers/shared/get_attribute_value.ts | 3 +- .../utils/remove_whitespace_children.ts | 3 +- .../compile/utils/get_name_from_filename.ts | 19 ++++++---- src/compiler/compile/utils/hash.ts | 5 ++- src/compiler/compile/utils/stringify.ts | 8 +++-- src/compiler/parse/index.ts | 14 ++++---- src/compiler/parse/read/context.ts | 3 +- src/compiler/parse/read/expression.ts | 4 +-- src/compiler/parse/read/script.ts | 9 +++-- src/compiler/parse/read/style.ts | 6 ++-- src/compiler/parse/state/mustache.ts | 30 ++++++++-------- src/compiler/parse/state/tag.ts | 26 +++++++++----- src/compiler/preprocess/index.ts | 22 +++++++----- src/compiler/utils/extract_svelte_ignore.ts | 7 ++-- src/compiler/utils/get_code_frame.ts | 4 ++- src/compiler/utils/mapped_code.ts | 10 ++++-- src/compiler/utils/names.ts | 12 ++++--- src/compiler/utils/patterns.ts | 28 ++++++++++++--- src/compiler/utils/trim.ts | 6 ++-- 42 files changed, 283 insertions(+), 147 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 2f8874de7a..1bbaac3ff4 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -47,6 +47,10 @@ interface ComponentOptions { preserveWhitespace?: boolean; } +const regex_leading_directory_separator = /^[/\\]/; +const regex_starts_with_term_export = /^Export/; +const regex_contains_term_function = /Function/; + export default class Component { stats: Stats; warnings: Warning[]; @@ -136,7 +140,7 @@ export default class Component { (typeof process !== 'undefined' ? compile_options.filename .replace(process.cwd(), '') - .replace(/^[/\\]/, '') + .replace(regex_leading_directory_separator, '') : compile_options.filename); this.locate = getLocator(this.source, { offsetLine: 1 }); @@ -638,7 +642,7 @@ export default class Component { body.splice(i, 1); } - if (/^Export/.test(node.type)) { + if (regex_starts_with_term_export.test(node.type)) { const replacement = this.extract_exports(node, true); if (replacement) { body[i] = replacement; @@ -795,7 +799,7 @@ export default class Component { return this.skip(); } - if (/^Export/.test(node.type)) { + if (regex_starts_with_term_export.test(node.type)) { const replacement = component.extract_exports(node); if (replacement) { this.replace(replacement); @@ -918,7 +922,7 @@ export default class Component { } if (name[1] !== '$' && scope.has(name.slice(1)) && scope.find_owner(name.slice(1)) !== this.instance_scope) { - if (!((/Function/.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) { + if (!((regex_contains_term_function.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) { return this.error(node as any, compiler_errors.contextual_store); } } @@ -965,7 +969,7 @@ export default class Component { walk(this.ast.instance.content, { enter(node: Node) { - if (/Function/.test(node.type)) { + if (regex_contains_term_function.test(node.type)) { return this.skip(); } @@ -1089,7 +1093,7 @@ export default class Component { this.replace(b` ${node.declarations.length ? node : null} - ${ props.length > 0 && b`let { ${ props } } = $$props;`} + ${ props.length > 0 && b`let { ${props} } = $$props;`} ${inserts} ` as any); return this.skip(); @@ -1460,6 +1464,8 @@ export default class Component { } } +const regex_valid_tag_name = /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/; + function process_component_options(component: Component, nodes) { const component_options: ComponentOptions = { immutable: component.compile_options.immutable || false, @@ -1473,7 +1479,7 @@ function process_component_options(component: Component, nodes) { const node = nodes.find(node => node.name === 'svelte:options'); - function get_value(attribute, {code, message}) { + function get_value(attribute, { code, message }) { const { value } = attribute; const chunk = value[0]; @@ -1505,7 +1511,7 @@ function process_component_options(component: Component, nodes) { return component.error(attribute, compiler_errors.invalid_tag_attribute); } - if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { + if (tag && !regex_valid_tag_name.test(tag)) { return component.error(attribute, compiler_errors.invalid_tag_property); } diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index 17302c4abd..28cf5ba6af 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -9,6 +9,7 @@ import EachBlock from '../nodes/EachBlock'; import IfBlock from '../nodes/IfBlock'; import AwaitBlock from '../nodes/AwaitBlock'; import compiler_errors from '../compiler_errors'; +import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns'; enum BlockAppliesToNode { NotPossible, @@ -25,6 +26,8 @@ const whitelist_attribute_selector = new Map([ ['dialog', new Set(['open'])] ]); +const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/; + export default class Selector { node: CssNode; stylesheet: Stylesheet; @@ -157,7 +160,7 @@ export default class Selector { for (const block of this.blocks) { for (const selector of block.selectors) { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - if (/[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/.test(selector.children[0].value)) { + if (regex_is_single_css_selector.test(selector.children[0].value)) { component.error(selector, compiler_errors.css_invalid_global_selector); } } @@ -281,12 +284,14 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{ return true; } +const regex_backslash_and_following_character = /\\(.)/g; + function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToNode { let i = block.selectors.length; while (i--) { const selector = block.selectors[i]; - const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1'); + const name = typeof selector.name === 'string' && selector.name.replace(regex_backslash_and_following_character, '$1'); if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) { return BlockAppliesToNode.NotPossible; @@ -371,7 +376,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, const start_with_space = []; const remaining = []; current_possible_values.forEach((current_possible_value: string) => { - if (/^\s/.test(current_possible_value)) { + if (regex_starts_with_whitespace.test(current_possible_value)) { start_with_space.push(current_possible_value); } else { remaining.push(current_possible_value); @@ -392,7 +397,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, prev_values = combined; start_with_space.forEach((value: string) => { - if (/\s$/.test(value)) { + if (regex_ends_with_whitespace.test(value)) { possible_values.add(value); } else { prev_values.push(value); @@ -406,7 +411,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, } current_possible_values.forEach((current_possible_value: string) => { - if (/\s$/.test(current_possible_value)) { + if (regex_ends_with_whitespace.test(current_possible_value)) { possible_values.add(current_possible_value); } else { prev_values.push(current_possible_value); diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index 1a9ea7feeb..9a3cbe9d13 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -9,9 +9,12 @@ import hash from '../utils/hash'; import compiler_warnings from '../compiler_warnings'; import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore'; import { push_array } from '../../utils/push_array'; +import { regex_only_whitespaces, regex_whitespace } from '../../utils/patterns'; + +const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/; function remove_css_prefix(name: string): string { - return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, ''); + return name.replace(regex_css_browser_prefix, ''); } const is_keyframes_node = (node: CssNode) => @@ -147,10 +150,10 @@ class Declaration { // Don't minify whitespace in custom properties, since some browsers (Chromium < 99) // treat --foo: ; and --foo:; differently - if (first.type === 'Raw' && /^\s+$/.test(first.value)) return; + if (first.type === 'Raw' && regex_only_whitespaces.test(first.value)) return; let start = first.start; - while (/\s/.test(code.original[start])) start += 1; + while (regex_whitespace.test(code.original[start])) start += 1; if (start - c > 1) { code.overwrite(c, start, ':'); diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index afe9c56cf4..a5edc3a6f7 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -35,6 +35,9 @@ const valid_options = [ 'cssHash' ]; +const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/; +const regex_starts_with_lowercase_character = /^[a-z]/; + function validate_options(options: CompileOptions, warnings: Warning[]) { const { name, filename, loopGuardTimeout, dev, namespace } = options; @@ -48,11 +51,11 @@ function validate_options(options: CompileOptions, warnings: Warning[]) { } }); - if (name && !/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name)) { + if (name && !regex_valid_identifier.test(name)) { throw new Error(`options.name must be a valid identifier (got '${name}')`); } - if (name && /^[a-z]/.test(name)) { + if (name && regex_starts_with_lowercase_character.test(name)) { const message = 'options.name should be capitalised'; warnings.push({ code: 'options-lowercase-name', diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 594490a5fb..f826df4828 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 {dimensions} from '../../utils/patterns'; +import { regex_dimensions } from '../../utils/patterns'; import { Node as ESTreeNode } from 'estree'; import { TemplateNode } from '../../interfaces'; import Element from './Element'; @@ -88,7 +88,7 @@ export default class Binding extends Node { const type = parent.get_static_attribute_value('type'); this.is_readonly = - dimensions.test(this.name) || + regex_dimensions.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 79f2800437..d5fad18f54 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -11,7 +11,7 @@ import StyleDirective from './StyleDirective'; import Text from './Text'; import { namespaces } from '../../utils/namespaces'; import map_children from './shared/map_children'; -import { dimensions, start_newline } from '../../utils/patterns'; +import { regex_dimensions, regex_starts_with_newline, regex_non_whitespace_character } from '../../utils/patterns'; import fuzzymatch from '../../utils/fuzzymatch'; import list from '../../utils/list'; import Let from './Let'; @@ -203,6 +203,10 @@ function is_valid_aria_attribute_value(schema: ARIAPropertyDefinition, value: st } } +const regex_any_repeated_whitespaces = /[\s]+/g; +const regex_heading_tags = /^h[1-6]$/; +const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/; + export default class Element extends Node { type: 'Element'; name: string; @@ -253,7 +257,7 @@ export default class Element extends Node { // places if there's another newline afterwards. // see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions // see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element - first.data = first.data.replace(start_newline, ''); + first.data = first.data.replace(regex_starts_with_newline, ''); } } @@ -398,7 +402,7 @@ export default class Element extends Node { // Errors - if (/(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/.test(name)) { + if (regex_illegal_attribute_character.test(name)) { return component.error(attribute, compiler_errors.illegal_attribute(name)); } @@ -464,7 +468,7 @@ export default class Element extends Node { component.warn(attribute, compiler_warnings.a11y_unknown_aria_attribute(type, match)); } - if (name === 'aria-hidden' && /^h[1-6]$/.test(this.name)) { + if (name === 'aria-hidden' && regex_heading_tags.test(this.name)) { component.warn(attribute, compiler_warnings.a11y_hidden(this.name)); } @@ -729,7 +733,7 @@ export default class Element extends Node { if (this.name === 'figure') { const children = this.children.filter(node => { if (node.type === 'Comment') return false; - if (node.type === 'Text') return /\S/.test(node.data); + if (node.type === 'Text') return regex_non_whitespace_character.test(node.data); return true; }); @@ -861,7 +865,7 @@ export default class Element extends Node { if (this.name !== 'video') { return component.error(binding, compiler_errors.invalid_binding_element_with('