diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d0d58a15..3f15d40206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Support nullish coalescing (`??`) and optional chaining (`?.`) operators ([#1972](https://github.com/sveltejs/svelte/issues/1972)) +* Support `import.meta` ([#4379](https://github.com/sveltejs/svelte/issues/4379)) * Fix placement of `{@html}` when used at the root of a slot or the root of a component ([#5012](https://github.com/sveltejs/svelte/issues/5012)) * Fix handling of `import`ed value that is used as a store and is also mutated ([#5019](https://github.com/sveltejs/svelte/issues/5019)) * Do not display `a11y-missing-content` warning on elements with `contenteditable` bindings ([#5020](https://github.com/sveltejs/svelte/issues/5020)) diff --git a/package-lock.json b/package-lock.json index c4f096b44e..25b0b67035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -167,9 +167,9 @@ "dev": true }, "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", "dev": true }, "@types/is-windows": { @@ -315,9 +315,9 @@ "dev": true }, "acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true }, "acorn-globals": { @@ -769,12 +769,12 @@ "dev": true }, "code-red": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.1.2.tgz", - "integrity": "sha512-GFNCdH1eTXpmZGzG+/w0L60gO35Kf803UPmAIDlD1220EUk+AtfQuvxnyeRzxIa7FXte0aANnQgySGdoU67I3Q==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.1.3.tgz", + "integrity": "sha512-3n9i1Jv0om4+2Aq7pCL8M5xRgc2wTXsblsYUxXJDpgBZ3wJP1zbcNzu4XgUqrG0rjc1to2yh+3n9rwHsJa7qSA==", "dev": true, "requires": { - "acorn": "^7.1.0", + "acorn": "^7.3.1", "is-reference": "^1.1.4", "periscopic": "^2.0.1", "sourcemap-codec": "^1.4.6" @@ -2250,12 +2250,12 @@ "dev": true }, "is-reference": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", - "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "requires": { - "@types/estree": "0.0.39" + "@types/estree": "*" } }, "is-regex": { diff --git a/package.json b/package.json index 865eed5bad..b2031f45e5 100644 --- a/package.json +++ b/package.json @@ -68,10 +68,10 @@ "@types/node": "^8.10.53", "@typescript-eslint/eslint-plugin": "^3.0.2", "@typescript-eslint/parser": "^3.0.2", - "acorn": "^7.1.0", + "acorn": "^7.3.1", "agadoo": "^1.1.0", "c8": "^5.0.1", - "code-red": "0.1.2", + "code-red": "^0.1.3", "codecov": "^3.5.0", "css-tree": "1.0.0-alpha22", "eslint": "^7.1.0", diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts index 797405b0fe..bcc0521ffa 100644 --- a/src/compiler/compile/nodes/shared/Context.ts +++ b/src/compiler/compile/nodes/shared/Context.ts @@ -1,5 +1,5 @@ import { x } from 'code-red'; -import { Node, Identifier, RestElement, Property } from 'estree'; +import { Node, Identifier } from 'estree'; export interface Context { key: Identifier; @@ -34,12 +34,10 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: const used_properties = []; node.properties.forEach((property) => { - const props: (RestElement | Property) = (property as any); - - if (props.type === 'RestElement') { + if (property.type === 'RestElement') { unpack_destructuring( contexts, - props.argument, + property.argument, node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node ); } else { diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index c7c66940fd..d4a219418b 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -198,7 +198,7 @@ export default class Expression { scope = map.get(node); } - if (is_reference(node, parent)) { + if (node.type === 'Identifier' && is_reference(node, parent)) { const { name } = flatten_reference(node); if (scope.has(name)) return; diff --git a/test/js/samples/optional-chaining/expected.js b/test/js/samples/optional-chaining/expected.js new file mode 100644 index 0000000000..a28dc129aa --- /dev/null +++ b/test/js/samples/optional-chaining/expected.js @@ -0,0 +1,189 @@ +/* generated by Svelte vX.Y.Z */ +import { + SvelteComponent, + attr, + create_component, + destroy_component, + detach, + element, + init, + insert, + mount_component, + safe_not_equal, + set_data, + space, + text, + transition_in, + transition_out +} from "svelte/internal"; + +function create_fragment(ctx) { + let t0_value = /*a*/ ctx[0].normal + ""; + let t0; + let t1_value = /*b*/ ctx[1]?.optional + ""; + let t1; + let t2; + let t3_value = /*c*/ ctx[2]["computed"] + ""; + let t3; + let t4_value = /*d*/ ctx[3]?.["computed_optional"] + ""; + let t4; + let t5; + let t6_value = /*e*/ ctx[4]() + ""; + let t6; + let t7_value = /*f*/ ctx[5]?.() + ""; + let t7; + let t8; + let div; + let div_a_value; + let div_b_value; + let div_c_value; + let div_d_value; + let div_e_value; + let div_f_value; + let t9; + let component; + let current; + + component = new /*Component*/ ctx[6]({ + props: { + a: /*a*/ ctx[0].normal, + b: /*b*/ ctx[1]?.optional, + c: /*c*/ ctx[2]["computed"], + d: /*d*/ ctx[3]?.["computed_optional"], + e: /*e*/ ctx[4](), + f: /*f*/ ctx[5]?.() + } + }); + + return { + c() { + t0 = text(t0_value); + t1 = text(t1_value); + t2 = space(); + t3 = text(t3_value); + t4 = text(t4_value); + t5 = space(); + t6 = text(t6_value); + t7 = text(t7_value); + t8 = space(); + div = element("div"); + t9 = space(); + create_component(component.$$.fragment); + attr(div, "a", div_a_value = /*a*/ ctx[0].normal); + attr(div, "b", div_b_value = /*b*/ ctx[1]?.optional); + attr(div, "c", div_c_value = /*c*/ ctx[2]["computed"]); + attr(div, "d", div_d_value = /*d*/ ctx[3]?.["computed_optional"]); + attr(div, "e", div_e_value = /*e*/ ctx[4]()); + attr(div, "f", div_f_value = /*f*/ ctx[5]?.()); + }, + m(target, anchor) { + insert(target, t0, anchor); + insert(target, t1, anchor); + insert(target, t2, anchor); + insert(target, t3, anchor); + insert(target, t4, anchor); + insert(target, t5, anchor); + insert(target, t6, anchor); + insert(target, t7, anchor); + insert(target, t8, anchor); + insert(target, div, anchor); + insert(target, t9, anchor); + mount_component(component, target, anchor); + current = true; + }, + p(ctx, [dirty]) { + if ((!current || dirty & /*a*/ 1) && t0_value !== (t0_value = /*a*/ ctx[0].normal + "")) set_data(t0, t0_value); + if ((!current || dirty & /*b*/ 2) && t1_value !== (t1_value = /*b*/ ctx[1]?.optional + "")) set_data(t1, t1_value); + if ((!current || dirty & /*c*/ 4) && t3_value !== (t3_value = /*c*/ ctx[2]["computed"] + "")) set_data(t3, t3_value); + if ((!current || dirty & /*d*/ 8) && t4_value !== (t4_value = /*d*/ ctx[3]?.["computed_optional"] + "")) set_data(t4, t4_value); + if ((!current || dirty & /*e*/ 16) && t6_value !== (t6_value = /*e*/ ctx[4]() + "")) set_data(t6, t6_value); + if ((!current || dirty & /*f*/ 32) && t7_value !== (t7_value = /*f*/ ctx[5]?.() + "")) set_data(t7, t7_value); + + if (!current || dirty & /*a*/ 1 && div_a_value !== (div_a_value = /*a*/ ctx[0].normal)) { + attr(div, "a", div_a_value); + } + + if (!current || dirty & /*b*/ 2 && div_b_value !== (div_b_value = /*b*/ ctx[1]?.optional)) { + attr(div, "b", div_b_value); + } + + if (!current || dirty & /*c*/ 4 && div_c_value !== (div_c_value = /*c*/ ctx[2]["computed"])) { + attr(div, "c", div_c_value); + } + + if (!current || dirty & /*d*/ 8 && div_d_value !== (div_d_value = /*d*/ ctx[3]?.["computed_optional"])) { + attr(div, "d", div_d_value); + } + + if (!current || dirty & /*e*/ 16 && div_e_value !== (div_e_value = /*e*/ ctx[4]())) { + attr(div, "e", div_e_value); + } + + if (!current || dirty & /*f*/ 32 && div_f_value !== (div_f_value = /*f*/ ctx[5]?.())) { + attr(div, "f", div_f_value); + } + + const component_changes = {}; + if (dirty & /*a*/ 1) component_changes.a = /*a*/ ctx[0].normal; + if (dirty & /*b*/ 2) component_changes.b = /*b*/ ctx[1]?.optional; + if (dirty & /*c*/ 4) component_changes.c = /*c*/ ctx[2]["computed"]; + if (dirty & /*d*/ 8) component_changes.d = /*d*/ ctx[3]?.["computed_optional"]; + if (dirty & /*e*/ 16) component_changes.e = /*e*/ ctx[4](); + if (dirty & /*f*/ 32) component_changes.f = /*f*/ ctx[5]?.(); + component.$set(component_changes); + }, + i(local) { + if (current) return; + transition_in(component.$$.fragment, local); + current = true; + }, + o(local) { + transition_out(component.$$.fragment, local); + current = false; + }, + d(detaching) { + if (detaching) detach(t0); + if (detaching) detach(t1); + if (detaching) detach(t2); + if (detaching) detach(t3); + if (detaching) detach(t4); + if (detaching) detach(t5); + if (detaching) detach(t6); + if (detaching) detach(t7); + if (detaching) detach(t8); + if (detaching) detach(div); + if (detaching) detach(t9); + destroy_component(component, detaching); + } + }; +} + +function instance($$self, $$props, $$invalidate) { + let { a } = $$props; + let { b } = $$props; + let { c } = $$props; + let { d } = $$props; + let { e } = $$props; + let { f } = $$props; + let Component; + + $$self.$set = $$props => { + if ("a" in $$props) $$invalidate(0, a = $$props.a); + if ("b" in $$props) $$invalidate(1, b = $$props.b); + if ("c" in $$props) $$invalidate(2, c = $$props.c); + if ("d" in $$props) $$invalidate(3, d = $$props.d); + if ("e" in $$props) $$invalidate(4, e = $$props.e); + if ("f" in $$props) $$invalidate(5, f = $$props.f); + }; + + return [a, b, c, d, e, f, Component]; +} + +class Component_1 extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, { a: 0, b: 1, c: 2, d: 3, e: 4, f: 5 }); + } +} + +export default Component_1; \ No newline at end of file diff --git a/test/js/samples/optional-chaining/input.svelte b/test/js/samples/optional-chaining/input.svelte new file mode 100644 index 0000000000..78edf2ca85 --- /dev/null +++ b/test/js/samples/optional-chaining/input.svelte @@ -0,0 +1,31 @@ + + +{a.normal}{b?.optional} +{c['computed']}{d?.['computed_optional']} +{e()}{f?.()} + +
+ +