();
let active = 0;
// https://github.com/darkskyapp/string-hash/blob/master/index.js
@@ -18,6 +20,12 @@ function hash(str: string) {
return hash >>> 0;
}
+function create_style_information(doc: Document | ShadowRoot, node: Element & ElementCSSInlineStyle) {
+ const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
+ managed_styles.set(doc, info);
+ return info;
+}
+
export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: number, duration: number, delay: number, ease: (t: number) => number, fn: (t: number, u: number) => string, uid: number = 0) {
const step = 16.666 / duration;
let keyframes = '{\n';
@@ -29,13 +37,12 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
const name = `__svelte_${hash(rule)}_${uid}`;
- const doc = get_root_for_style(node) as ExtendedDoc;
- active_docs.add(doc);
- const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet as CSSStyleSheet);
- const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});
+ const doc = get_root_for_style(node);
+
+ const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
- if (!current_rules[name]) {
- current_rules[name] = true;
+ if (!rules[name]) {
+ rules[name] = true;
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
}
@@ -63,12 +70,12 @@ export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string
export function clear_rules() {
raf(() => {
if (active) return;
- active_docs.forEach(doc => {
- const stylesheet = doc.__svelte_stylesheet;
+ managed_styles.forEach(info => {
+ const { stylesheet } = info;
let i = stylesheet.cssRules.length;
while (i--) stylesheet.deleteRule(i);
- doc.__svelte_rules = {};
+ info.rules = {};
});
- active_docs.clear();
+ managed_styles.clear();
});
}
diff --git a/test/hydration/samples/raw-with-empty-line-at-top/_after.html b/test/hydration/samples/raw-with-empty-line-at-top/_after.html
new file mode 100644
index 0000000000..28ee6452d3
--- /dev/null
+++ b/test/hydration/samples/raw-with-empty-line-at-top/_after.html
@@ -0,0 +1,6 @@
+
+before
+
+
+a b c
+after
diff --git a/test/hydration/samples/raw-with-empty-line-at-top/_before.html b/test/hydration/samples/raw-with-empty-line-at-top/_before.html
new file mode 100644
index 0000000000..28ee6452d3
--- /dev/null
+++ b/test/hydration/samples/raw-with-empty-line-at-top/_before.html
@@ -0,0 +1,6 @@
+
+before
+
+
+a b c
+after
diff --git a/test/hydration/samples/raw-with-empty-line-at-top/main.svelte b/test/hydration/samples/raw-with-empty-line-at-top/main.svelte
new file mode 100644
index 0000000000..2d08a3f418
--- /dev/null
+++ b/test/hydration/samples/raw-with-empty-line-at-top/main.svelte
@@ -0,0 +1,11 @@
+
+
+before
+
+
+{#each content as c}
+ {@html c}
+{/each}
+after
diff --git a/test/js/samples/debug-ssr-foo/expected.js b/test/js/samples/debug-ssr-foo/expected.js
index 69da37b2d9..e0a29f18de 100644
--- a/test/js/samples/debug-ssr-foo/expected.js
+++ b/test/js/samples/debug-ssr-foo/expected.js
@@ -7,8 +7,10 @@ const Component = create_ssr_component(($$result, $$props, $$bindings, slots) =>
if ($$props.things === void 0 && $$bindings.things && things !== void 0) $$bindings.things(things);
if ($$props.foo === void 0 && $$bindings.foo && foo !== void 0) $$bindings.foo(foo);
- return `${each(things, thing => `${escape(thing.name)}
- ${debug(null, 7, 2, { foo })}`)}
+ return `${each(things, thing => {
+ return `${escape(thing.name)}
+ ${debug(null, 7, 2, { foo })}`;
+ })}
foo: ${escape(foo)}
`;
});
diff --git a/test/parser/samples/attribute-style-directive-shorthand/output.json b/test/parser/samples/attribute-style-directive-shorthand/output.json
index d4e81b6e4b..ac658408e6 100644
--- a/test/parser/samples/attribute-style-directive-shorthand/output.json
+++ b/test/parser/samples/attribute-style-directive-shorthand/output.json
@@ -13,19 +13,13 @@
{
"start": 5,
"end": 16,
- "type": "Style",
+ "type": "StyleDirective",
"name": "color",
- "modifiers": [],
- "expression": {
- "start": 11,
- "end": 16,
- "name": "color",
- "type": "Identifier"
- }
+ "value": true
}
],
"children": []
}
]
}
-}
+}
\ No newline at end of file
diff --git a/test/parser/samples/attribute-style-directive-string/input.svelte b/test/parser/samples/attribute-style-directive-string/input.svelte
index b2eb6bfef8..dd8ec8dc94 100644
--- a/test/parser/samples/attribute-style-directive-string/input.svelte
+++ b/test/parser/samples/attribute-style-directive-string/input.svelte
@@ -1 +1,7 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/parser/samples/attribute-style-directive-string/output.json b/test/parser/samples/attribute-style-directive-string/output.json
index 4cf2bd30c2..e81e7be3c9 100644
--- a/test/parser/samples/attribute-style-directive-string/output.json
+++ b/test/parser/samples/attribute-style-directive-string/output.json
@@ -1,7 +1,7 @@
{
"html": {
"start": 0,
- "end": 29,
+ "end": 252,
"type": "Fragment",
"children": [
{
@@ -13,23 +13,339 @@
{
"start": 5,
"end": 22,
- "type": "Style",
+ "type": "StyleDirective",
"name": "color",
- "modifiers": [],
- "expression": {
- "type": "TemplateLiteral",
- "expressions": [],
- "quasis": [
- {
- "type": "TemplateElement",
- "value": {
- "raw": "red",
- "cooked": null
+ "value": [
+ {
+ "start": 18,
+ "end": 21,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 29,
+ "end": 30,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 30,
+ "end": 59,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 35,
+ "end": 52,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 48,
+ "end": 51,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 59,
+ "end": 60,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 60,
+ "end": 87,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 65,
+ "end": 80,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 77,
+ "end": 80,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 87,
+ "end": 88,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 88,
+ "end": 127,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 93,
+ "end": 120,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 106,
+ "end": 109,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ },
+ {
+ "start": 109,
+ "end": 119,
+ "type": "MustacheTag",
+ "expression": {
+ "type": "Identifier",
+ "start": 110,
+ "end": 118,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 22
+ },
+ "end": {
+ "line": 4,
+ "column": 30
+ }
+ },
+ "name": "variable"
+ }
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 127,
+ "end": 128,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 128,
+ "end": 167,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 133,
+ "end": 160,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 146,
+ "end": 149,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ },
+ {
+ "start": 149,
+ "end": 159,
+ "type": "MustacheTag",
+ "expression": {
+ "type": "Identifier",
+ "start": 150,
+ "end": 158,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 22
+ },
+ "end": {
+ "line": 5,
+ "column": 30
+ }
+ },
+ "name": "variable"
+ }
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 167,
+ "end": 168,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 168,
+ "end": 205,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 173,
+ "end": 198,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 185,
+ "end": 188,
+ "type": "Text",
+ "raw": "red",
+ "data": "red"
+ },
+ {
+ "start": 188,
+ "end": 198,
+ "type": "MustacheTag",
+ "expression": {
+ "type": "Identifier",
+ "start": 189,
+ "end": 197,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 21
+ },
+ "end": {
+ "line": 6,
+ "column": 29
+ }
+ },
+ "name": "variable"
+ }
+ }
+ ]
+ }
+ ],
+ "children": []
+ },
+ {
+ "start": 205,
+ "end": 206,
+ "type": "Text",
+ "raw": "\n",
+ "data": "\n"
+ },
+ {
+ "start": 206,
+ "end": 252,
+ "type": "Element",
+ "name": "div",
+ "attributes": [
+ {
+ "start": 211,
+ "end": 245,
+ "type": "StyleDirective",
+ "name": "color",
+ "value": [
+ {
+ "start": 223,
+ "end": 245,
+ "type": "MustacheTag",
+ "expression": {
+ "type": "TemplateLiteral",
+ "start": 224,
+ "end": 244,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 18
+ },
+ "end": {
+ "line": 7,
+ "column": 38
+ }
},
- "tail": true
+ "expressions": [
+ {
+ "type": "Identifier",
+ "start": 235,
+ "end": 242,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 29
+ },
+ "end": {
+ "line": 7,
+ "column": 36
+ }
+ },
+ "name": "literal"
+ }
+ ],
+ "quasis": [
+ {
+ "type": "TemplateElement",
+ "start": 225,
+ "end": 233,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 19
+ },
+ "end": {
+ "line": 7,
+ "column": 27
+ }
+ },
+ "value": {
+ "raw": "template",
+ "cooked": "template"
+ },
+ "tail": false
+ },
+ {
+ "type": "TemplateElement",
+ "start": 243,
+ "end": 243,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 37
+ },
+ "end": {
+ "line": 7,
+ "column": 37
+ }
+ },
+ "value": {
+ "raw": "",
+ "cooked": ""
+ },
+ "tail": true
+ }
+ ]
}
- ]
- }
+ }
+ ]
}
],
"children": []
diff --git a/test/parser/samples/attribute-style-directive/output.json b/test/parser/samples/attribute-style-directive/output.json
index 36906045ce..f36ce6b01e 100644
--- a/test/parser/samples/attribute-style-directive/output.json
+++ b/test/parser/samples/attribute-style-directive/output.json
@@ -13,25 +13,31 @@
{
"start": 5,
"end": 26,
- "type": "Style",
+ "type": "StyleDirective",
"name": "color",
- "modifiers": [],
- "expression": {
- "type": "Identifier",
- "start": 18,
- "end": 25,
- "loc": {
- "start": {
- "line": 1,
- "column": 18
- },
- "end": {
- "line": 1,
- "column": 25
+ "value": [
+ {
+ "start": 17,
+ "end": 26,
+ "type": "MustacheTag",
+ "expression": {
+ "type": "Identifier",
+ "start": 18,
+ "end": 25,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 18
+ },
+ "end": {
+ "line": 1,
+ "column": 25
+ }
+ },
+ "name": "myColor"
}
- },
- "name": "myColor"
- }
+ }
+ ]
}
],
"children": []
diff --git a/test/runtime/index.ts b/test/runtime/index.ts
index 4fc3880208..f3c962f1d9 100644
--- a/test/runtime/index.ts
+++ b/test/runtime/index.ts
@@ -245,7 +245,7 @@ describe('runtime', () => {
.catch(err => {
// print a clickable link to open the directory
err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`;
- done();
+ done(err);
throw err;
})
.then(() => {
diff --git a/test/runtime/samples/const-tag-await-then-destructuring/_config.js b/test/runtime/samples/const-tag-await-then-destructuring/_config.js
new file mode 100644
index 0000000000..cb40e7c456
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring/_config.js
@@ -0,0 +1,19 @@
+export default {
+ html: '12 120 70, 30+4=34
',
+ async test({ component, target, assert }) {
+ component.promise1 = Promise.resolve({width: 5, height: 6});
+ component.promise2 = Promise.reject({width: 6, height: 7});
+
+ await Promise.resolve();
+ assert.htmlEqual(target.innerHTML, `
+ 30 300 110, 50+6=56
+ 42 420 130, 60+7=67
+ `);
+
+ component.constant = 20;
+ assert.htmlEqual(target.innerHTML, `
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-await-then-destructuring/main.svelte b/test/runtime/samples/const-tag-await-then-destructuring/main.svelte
new file mode 100644
index 0000000000..ab450822c6
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring/main.svelte
@@ -0,0 +1,23 @@
+
+
+{#await promise1 then { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/await}
+
+{#await promise2 catch { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/await}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-await-then/_config.js b/test/runtime/samples/const-tag-await-then/_config.js
new file mode 100644
index 0000000000..cb40e7c456
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then/_config.js
@@ -0,0 +1,19 @@
+export default {
+ html: '12 120 70, 30+4=34
',
+ async test({ component, target, assert }) {
+ component.promise1 = Promise.resolve({width: 5, height: 6});
+ component.promise2 = Promise.reject({width: 6, height: 7});
+
+ await Promise.resolve();
+ assert.htmlEqual(target.innerHTML, `
+ 30 300 110, 50+6=56
+ 42 420 130, 60+7=67
+ `);
+
+ component.constant = 20;
+ assert.htmlEqual(target.innerHTML, `
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-await-then/main.svelte b/test/runtime/samples/const-tag-await-then/main.svelte
new file mode 100644
index 0000000000..f8eed87115
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then/main.svelte
@@ -0,0 +1,23 @@
+
+
+{#await promise1 then box}
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+{/await}
+
+{#await promise2 catch box}
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+{/await}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-component/Component.svelte b/test/runtime/samples/const-tag-component/Component.svelte
new file mode 100644
index 0000000000..68ba82f245
--- /dev/null
+++ b/test/runtime/samples/const-tag-component/Component.svelte
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-component/_config.js b/test/runtime/samples/const-tag-component/_config.js
new file mode 100644
index 0000000000..66399a06fa
--- /dev/null
+++ b/test/runtime/samples/const-tag-component/_config.js
@@ -0,0 +1,46 @@
+export default {
+ html: `
+ 12 120 70, 30+4=34
+ 12 120 70, 30+4=34
+ 12 120 70, 30+4=34
+
+
+ 12 120 70, 30+4=34
+ 12 120 70, 30+4=34
+ `,
+ async test({ component, target, assert }) {
+ component.constant = 20;
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 12 240 140, 60+4=64
+ 12 240 140, 60+4=64
+
+
+ 12 240 140, 60+4=64
+ 12 240 140, 60+4=64
+ `);
+
+ component.box = {width: 5, height: 6};
+ assert.htmlEqual(target.innerHTML, `
+ 30 600 220, 100+6=106
+ 30 600 220, 100+6=106
+ 30 600 220, 100+6=106
+
+
30 600 220, 100+6=106
+
+
+
30 600 220, 100+6=106
+
+ 30 600 220, 100+6=106
+ 30 600 220, 100+6=106
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-component/main.svelte b/test/runtime/samples/const-tag-component/main.svelte
new file mode 100644
index 0000000000..1854c3fba9
--- /dev/null
+++ b/test/runtime/samples/const-tag-component/main.svelte
@@ -0,0 +1,60 @@
+
+
+
+
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+
+
+
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+
+
+
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+
+
+
+
+
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+
{area} {volume} {perimeter}, {width}+{height}={sum}
+
+
+
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+
{area} {volume} {perimeter}, {_width}+{_height}={sum}
+
+
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+
+
+
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+
diff --git a/test/runtime/samples/const-tag-dependencies/_config.js b/test/runtime/samples/const-tag-dependencies/_config.js
new file mode 100644
index 0000000000..5cbb2da587
--- /dev/null
+++ b/test/runtime/samples/const-tag-dependencies/_config.js
@@ -0,0 +1,12 @@
+export default {
+ html: `
+ 7
+ `,
+ async test({ component, target, assert }) {
+ component.a = 5;
+
+ assert.htmlEqual(target.innerHTML, `
+ 9
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-dependencies/main.svelte b/test/runtime/samples/const-tag-dependencies/main.svelte
new file mode 100644
index 0000000000..cce3b0c1f7
--- /dev/null
+++ b/test/runtime/samples/const-tag-dependencies/main.svelte
@@ -0,0 +1,10 @@
+
+
+{#each [value] as n}
+ {@const ab = a + b}
+ {ab}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-each-destructure/_config.js b/test/runtime/samples/const-tag-each-destructure/_config.js
new file mode 100644
index 0000000000..00f8b31540
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure/_config.js
@@ -0,0 +1,30 @@
+export default {
+ html: `
+ 12 120 70, 30+4=34
+ 35 350 120, 50+7=57
+ 48 480 140, 60+8=68
+ `,
+ async test({ component, target, assert }) {
+ component.constant = 20;
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 35 700 240, 100+7=107
+ 48 960 280, 120+8=128
+ `);
+
+ component.boxes = [
+ {width: 3, height: 4},
+ {width: 4, height: 5},
+ {width: 5, height: 6},
+ {width: 6, height: 7}
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 20 400 180, 80+5=85
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-each-destructure/main.svelte b/test/runtime/samples/const-tag-each-destructure/main.svelte
new file mode 100644
index 0000000000..7f0391d5e6
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure/main.svelte
@@ -0,0 +1,19 @@
+
+
+{#each boxes as { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-each/_config.js b/test/runtime/samples/const-tag-each/_config.js
new file mode 100644
index 0000000000..00f8b31540
--- /dev/null
+++ b/test/runtime/samples/const-tag-each/_config.js
@@ -0,0 +1,30 @@
+export default {
+ html: `
+ 12 120 70, 30+4=34
+ 35 350 120, 50+7=57
+ 48 480 140, 60+8=68
+ `,
+ async test({ component, target, assert }) {
+ component.constant = 20;
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 35 700 240, 100+7=107
+ 48 960 280, 120+8=128
+ `);
+
+ component.boxes = [
+ {width: 3, height: 4},
+ {width: 4, height: 5},
+ {width: 5, height: 6},
+ {width: 6, height: 7}
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 20 400 180, 80+5=85
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-each/main.svelte b/test/runtime/samples/const-tag-each/main.svelte
new file mode 100644
index 0000000000..004f81cd83
--- /dev/null
+++ b/test/runtime/samples/const-tag-each/main.svelte
@@ -0,0 +1,19 @@
+
+
+{#each boxes as box}
+ {@const {area, volume} = calculate(box.width, box.height, constant)}
+ {@const perimeter = (box.width + box.height) * constant}
+ {@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
+ {area} {volume} {perimeter}, {width}+{height}={sum}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-hoisting/_config.js b/test/runtime/samples/const-tag-hoisting/_config.js
new file mode 100644
index 0000000000..f8f4a5c85e
--- /dev/null
+++ b/test/runtime/samples/const-tag-hoisting/_config.js
@@ -0,0 +1,12 @@
+export default {
+ html: `
+ 4 ^ 4 = 256
+ `,
+ async test({ component, target, assert }) {
+ component.value = 3;
+
+ assert.htmlEqual(target.innerHTML, `
+ 3 ^ 4 = 81
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-hoisting/main.svelte b/test/runtime/samples/const-tag-hoisting/main.svelte
new file mode 100644
index 0000000000..49fce93274
--- /dev/null
+++ b/test/runtime/samples/const-tag-hoisting/main.svelte
@@ -0,0 +1,11 @@
+
+
+{#each [value] as n}
+ {n} ^ 4 = {hypercubed}
+
+ {@const squared = n * n}
+ {@const cubed = squared * n}
+ {@const hypercubed = cubed * n}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-ordering/_config.js b/test/runtime/samples/const-tag-ordering/_config.js
new file mode 100644
index 0000000000..f8f4a5c85e
--- /dev/null
+++ b/test/runtime/samples/const-tag-ordering/_config.js
@@ -0,0 +1,12 @@
+export default {
+ html: `
+ 4 ^ 4 = 256
+ `,
+ async test({ component, target, assert }) {
+ component.value = 3;
+
+ assert.htmlEqual(target.innerHTML, `
+ 3 ^ 4 = 81
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-ordering/main.svelte b/test/runtime/samples/const-tag-ordering/main.svelte
new file mode 100644
index 0000000000..49fce93274
--- /dev/null
+++ b/test/runtime/samples/const-tag-ordering/main.svelte
@@ -0,0 +1,11 @@
+
+
+{#each [value] as n}
+ {n} ^ 4 = {hypercubed}
+
+ {@const squared = n * n}
+ {@const cubed = squared * n}
+ {@const hypercubed = cubed * n}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/const-tag-shadow/_config.js b/test/runtime/samples/const-tag-shadow/_config.js
new file mode 100644
index 0000000000..312235f113
--- /dev/null
+++ b/test/runtime/samples/const-tag-shadow/_config.js
@@ -0,0 +1,43 @@
+export default {
+ html: `
+ 7
+ 11
+ 15
+ 7
+ 19
+ 23
+ 27
+ 19
+ `,
+ async test({ component, target, assert }) {
+ component.numbers = [
+ {
+ a: 4,
+ b: 5,
+ children: [
+ { a: 6, b: 7 },
+ { a: 8, b: 9 }
+ ]
+ },
+ {
+ a: 10,
+ b: 11,
+ children: [
+ { a: 12, b: 13 },
+ { a: 14, b: 15 }
+ ]
+ }
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 9
+ 13
+ 17
+ 9
+ 21
+ 25
+ 29
+ 21
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-shadow/main.svelte b/test/runtime/samples/const-tag-shadow/main.svelte
new file mode 100644
index 0000000000..725121c733
--- /dev/null
+++ b/test/runtime/samples/const-tag-shadow/main.svelte
@@ -0,0 +1,31 @@
+
+
+{#each numbers as {a, b, children}}
+ {@const ab = a + b}
+ {ab}
+ {#each children as {a, b}}
+ {@const ab = a + b}
+ {ab}
+ {/each}
+ {ab}
+{/each}
\ No newline at end of file
diff --git a/test/runtime/samples/inline-style-directive-shorthand/_config.js b/test/runtime/samples/inline-style-directive-shorthand/_config.js
index 64043e5269..352d4a139c 100644
--- a/test/runtime/samples/inline-style-directive-shorthand/_config.js
+++ b/test/runtime/samples/inline-style-directive-shorthand/_config.js
@@ -1,18 +1,25 @@
export default {
html: `
+
`,
test({ assert, component, target, window }) {
- const p = target.querySelector('p');
+ const [p1, p2] = target.querySelectorAll('p');
- let styles = window.getComputedStyle(p);
- assert.equal(styles.color, 'red');
+ assert.equal(window.getComputedStyle(p1).color, 'red');
+ assert.equal(window.getComputedStyle(p2).color, 'red');
component.color = 'blue';
- assert.htmlEqual(target.innerHTML, '');
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+ `
+ );
- styles = window.getComputedStyle(p);
- assert.equal(styles.color, 'blue');
+ assert.equal(window.getComputedStyle(p1).color, 'blue');
+ assert.equal(window.getComputedStyle(p2).color, 'blue');
}
};
diff --git a/test/runtime/samples/inline-style-directive-shorthand/main.svelte b/test/runtime/samples/inline-style-directive-shorthand/main.svelte
index 65bbff910b..2b5f8e3db7 100644
--- a/test/runtime/samples/inline-style-directive-shorthand/main.svelte
+++ b/test/runtime/samples/inline-style-directive-shorthand/main.svelte
@@ -3,3 +3,7 @@
+
+{#each [1] as _}
+
+{/each}
diff --git a/test/runtime/samples/inline-style-directive-string-variable-kebab-case/_config.js b/test/runtime/samples/inline-style-directive-string-variable-kebab-case/_config.js
new file mode 100644
index 0000000000..3bd6d38ea5
--- /dev/null
+++ b/test/runtime/samples/inline-style-directive-string-variable-kebab-case/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `
+
+ `,
+
+ test({ assert, target, window }) {
+ const div = target.querySelector('div');
+ const styles = window.getComputedStyle(div);
+
+ assert.equal(styles['background-image'], 'url(https://raw.githubusercontent.com/sveltejs/branding/master/svelte-vertical.png)');
+ assert.equal(styles.getPropertyValue('--css-variable'), 'rgba(0, 0, 0, 1)');
+
+ assert.htmlEqual(
+ target.innerHTML,
+ ''
+ );
+ }
+};
diff --git a/test/runtime/samples/inline-style-directive-string-variable-kebab-case/main.svelte b/test/runtime/samples/inline-style-directive-string-variable-kebab-case/main.svelte
new file mode 100644
index 0000000000..dbfba08d64
--- /dev/null
+++ b/test/runtime/samples/inline-style-directive-string-variable-kebab-case/main.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/test/server-side-rendering/index.ts b/test/server-side-rendering/index.ts
index f4175c73a6..98d19a6006 100644
--- a/test/server-side-rendering/index.ts
+++ b/test/server-side-rendering/index.ts
@@ -128,13 +128,13 @@ describe('ssr', () => {
}
if (show) showOutput(dir, { generate: 'ssr', format: 'cjs' });
+ done();
} catch (err) {
showOutput(dir, { generate: 'ssr', format: 'cjs' });
err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), dir)}/main.svelte`;
- throw err;
+ done(err);
} finally {
set_current_component(null);
- done();
}
});
});
diff --git a/test/validator/samples/animation-each-with-const/errors.json b/test/validator/samples/animation-each-with-const/errors.json
new file mode 100644
index 0000000000..0637a088a0
--- /dev/null
+++ b/test/validator/samples/animation-each-with-const/errors.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/test/validator/samples/animation-each-with-const/input.svelte b/test/validator/samples/animation-each-with-const/input.svelte
new file mode 100644
index 0000000000..1e236de76f
--- /dev/null
+++ b/test/validator/samples/animation-each-with-const/input.svelte
@@ -0,0 +1,10 @@
+
+
+
+ {#each [] as n (n)}
+ {@const a = n}
+
+ {/each}
+
diff --git a/test/validator/samples/animation-each-with-whitespace/errors.json b/test/validator/samples/animation-each-with-whitespace/errors.json
new file mode 100644
index 0000000000..0637a088a0
--- /dev/null
+++ b/test/validator/samples/animation-each-with-whitespace/errors.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/test/validator/samples/animation-each-with-whitespace/input.svelte b/test/validator/samples/animation-each-with-whitespace/input.svelte
new file mode 100644
index 0000000000..3e063716ab
--- /dev/null
+++ b/test/validator/samples/animation-each-with-whitespace/input.svelte
@@ -0,0 +1,7 @@
+
+
+
+ {#each [] as n (n)}
{/each}
+
diff --git a/test/validator/samples/const-tag-conflict-1/errors.json b/test/validator/samples/const-tag-conflict-1/errors.json
new file mode 100644
index 0000000000..59c72eccaf
--- /dev/null
+++ b/test/validator/samples/const-tag-conflict-1/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-declaration",
+ "message": "'a' has already been declared",
+ "start": { "line": 7, "column": 2, "character": 84 },
+ "end": { "line": 7, "column": 19, "character": 101 },
+ "pos": 84
+ }
+]
diff --git a/test/validator/samples/const-tag-conflict-1/input.svelte b/test/validator/samples/const-tag-conflict-1/input.svelte
new file mode 100644
index 0000000000..6633eeacdf
--- /dev/null
+++ b/test/validator/samples/const-tag-conflict-1/input.svelte
@@ -0,0 +1,8 @@
+
+
+{#each array as item}
+ {@const a = item}
+ {@const a = item}
+{/each}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-conflict-2/errors.json b/test/validator/samples/const-tag-conflict-2/errors.json
new file mode 100644
index 0000000000..11e2535091
--- /dev/null
+++ b/test/validator/samples/const-tag-conflict-2/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-declaration",
+ "message": "'item' has already been declared",
+ "start": { "line": 6, "column": 2, "character": 64 },
+ "end": { "line": 6, "column": 21, "character": 83 },
+ "pos": 64
+ }
+]
diff --git a/test/validator/samples/const-tag-conflict-2/input.svelte b/test/validator/samples/const-tag-conflict-2/input.svelte
new file mode 100644
index 0000000000..2be7207691
--- /dev/null
+++ b/test/validator/samples/const-tag-conflict-2/input.svelte
@@ -0,0 +1,7 @@
+
+
+{#each array as item}
+ {@const item = 123}
+{/each}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-cyclical/errors.json b/test/validator/samples/const-tag-cyclical/errors.json
new file mode 100644
index 0000000000..1436747798
--- /dev/null
+++ b/test/validator/samples/const-tag-cyclical/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "cyclical-const-tags",
+ "message": "Cyclical dependency detected: b → c → b",
+ "start": { "line": 6, "column": 2, "character": 61 },
+ "end": { "line": 6, "column": 20, "character": 79 },
+ "pos": 61
+ }
+]
diff --git a/test/validator/samples/const-tag-cyclical/input.svelte b/test/validator/samples/const-tag-cyclical/input.svelte
new file mode 100644
index 0000000000..2f694be808
--- /dev/null
+++ b/test/validator/samples/const-tag-cyclical/input.svelte
@@ -0,0 +1,8 @@
+
+
+{#each array as a}
+ {@const b = a + c}
+ {@const c = b + a}
+{/each}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-out-of-scope/input.svelte b/test/validator/samples/const-tag-out-of-scope/input.svelte
new file mode 100644
index 0000000000..d1c2723e22
--- /dev/null
+++ b/test/validator/samples/const-tag-out-of-scope/input.svelte
@@ -0,0 +1,10 @@
+
+
+{#each array as a}
+ {@const b = a + 1}
+
+{/each}
+
+{b}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-out-of-scope/warnings.json b/test/validator/samples/const-tag-out-of-scope/warnings.json
new file mode 100644
index 0000000000..c85b417fe4
--- /dev/null
+++ b/test/validator/samples/const-tag-out-of-scope/warnings.json
@@ -0,0 +1,17 @@
+[
+ {
+ "code": "missing-declaration",
+ "message": "'b' is not defined",
+ "pos": 100,
+ "start": {
+ "character": 100,
+ "column": 1,
+ "line": 10
+ },
+ "end": {
+ "character": 101,
+ "column": 2,
+ "line": 10
+ }
+ }
+]
diff --git a/test/validator/samples/const-tag-placement-1/errors.json b/test/validator/samples/const-tag-placement-1/errors.json
new file mode 100644
index 0000000000..52de84e2be
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-1/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-placement",
+ "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
+ "start": { "line": 5, "column": 0, "character": 36 },
+ "end": { "line": 5, "column": 18, "character": 54 },
+ "pos": 36
+ }
+]
diff --git a/test/validator/samples/const-tag-placement-1/input.svelte b/test/validator/samples/const-tag-placement-1/input.svelte
new file mode 100644
index 0000000000..14ea3085e7
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-1/input.svelte
@@ -0,0 +1,5 @@
+
+
+{@const b = a + 1}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-placement-2/errors.json b/test/validator/samples/const-tag-placement-2/errors.json
new file mode 100644
index 0000000000..005b3ff8b7
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-2/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-placement",
+ "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
+ "start": { "line": 6, "column": 2, "character": 46 },
+ "end": { "line": 6, "column": 20, "character": 64 },
+ "pos": 46
+ }
+]
diff --git a/test/validator/samples/const-tag-placement-2/input.svelte b/test/validator/samples/const-tag-placement-2/input.svelte
new file mode 100644
index 0000000000..a843f9d58f
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-2/input.svelte
@@ -0,0 +1,7 @@
+
+
+{#if a}
+ {@const b = a + 1}
+{/if}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-placement-3/errors.json b/test/validator/samples/const-tag-placement-3/errors.json
new file mode 100644
index 0000000000..cc0ba4d74d
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-3/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-placement",
+ "message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, or ",
+ "start": { "line": 7, "column": 4, "character": 63 },
+ "end": { "line": 7, "column": 18, "character": 77 },
+ "pos": 63
+ }
+]
diff --git a/test/validator/samples/const-tag-placement-3/input.svelte b/test/validator/samples/const-tag-placement-3/input.svelte
new file mode 100644
index 0000000000..2519482a98
--- /dev/null
+++ b/test/validator/samples/const-tag-placement-3/input.svelte
@@ -0,0 +1,9 @@
+
+
+{#each a as i}
+
+ {@const b = i}
+
+{/each}
\ No newline at end of file
diff --git a/test/validator/samples/const-tag-readonly-1/errors.json b/test/validator/samples/const-tag-readonly-1/errors.json
new file mode 100644
index 0000000000..ed60c529fd
--- /dev/null
+++ b/test/validator/samples/const-tag-readonly-1/errors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "code": "invalid-const-update",
+ "message": "'b' is declared using {@const ...} and is read-only",
+ "start": { "line": 7, "column": 26, "character": 106 },
+ "end": { "line": 7, "column": 30, "character": 110 },
+ "pos": 106
+ }
+]
diff --git a/test/validator/samples/const-tag-readonly-1/input.svelte b/test/validator/samples/const-tag-readonly-1/input.svelte
new file mode 100644
index 0000000000..b5a37c8608
--- /dev/null
+++ b/test/validator/samples/const-tag-readonly-1/input.svelte
@@ -0,0 +1,8 @@
+
+
+{#each array as a}
+ {@const b = a + 1}
+