From 867e61e501aa95f1716795197f549b6a10324271 Mon Sep 17 00:00:00 2001 From: bre30kra69cs Date: Wed, 31 Jul 2019 15:14:18 +0300 Subject: [PATCH 01/12] fix Literally undefined class #3283 --- .../render_dom/wrappers/Element/Attribute.ts | 13 +++++-- .../attribute-null-classname/_config.js | 32 +++++++++++++++ .../attribute-null-classname/main.svelte | 11 ++++++ .../attribute-null-classnames/_config.js | 39 +++++++++++++++++++ .../attribute-null-classnames/main.svelte | 12 ++++++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/attribute-null-classname/_config.js create mode 100644 test/runtime/samples/attribute-null-classname/main.svelte create mode 100644 test/runtime/samples/attribute-null-classnames/_config.js create mode 100644 test/runtime/samples/attribute-null-classnames/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index f55e731fdb..aff06e4150 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -91,9 +91,16 @@ export default class AttributeWrapper { if (chunk.type === 'Text') { return stringify(chunk.data); } else { - return chunk.get_precedence() <= 13 - ? `(${chunk.render()})` - : chunk.render(); + const renderedChunk = chunk.render(); + if (this.node.name === 'class') { + return chunk.get_precedence() <= 13 + ? `(${renderedChunk})` + : `(${renderedChunk} || '')`; + } else { + return chunk.get_precedence() <= 13 + ? `(${renderedChunk})` + : renderedChunk; + } } }) .join(' + '); diff --git a/test/runtime/samples/attribute-null-classname/_config.js b/test/runtime/samples/attribute-null-classname/_config.js new file mode 100644 index 0000000000..856bd8bcd0 --- /dev/null +++ b/test/runtime/samples/attribute-null-classname/_config.js @@ -0,0 +1,32 @@ +export default { + skip_if_ssr: true, + + props: { + testName: "testClassName" + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'testClassName svelte-x1o6ra'); + + component.testName = null; + assert.equal(div.className, ' svelte-x1o6ra'); + + component.testName = undefined; + assert.equal(div.className, ' svelte-x1o6ra'); + + component.testName = undefined + ''; + assert.equal(div.className, 'undefined svelte-x1o6ra'); + + component.testName = null + ''; + assert.equal(div.className, 'null svelte-x1o6ra'); + + component.testName = 1; + assert.equal(div.className, '1 svelte-x1o6ra'); + + component.testName = ''; + assert.equal(div.className, ' svelte-x1o6ra'); + } +}; diff --git a/test/runtime/samples/attribute-null-classname/main.svelte b/test/runtime/samples/attribute-null-classname/main.svelte new file mode 100644 index 0000000000..013647952d --- /dev/null +++ b/test/runtime/samples/attribute-null-classname/main.svelte @@ -0,0 +1,11 @@ + + + + +
diff --git a/test/runtime/samples/attribute-null-classnames/_config.js b/test/runtime/samples/attribute-null-classnames/_config.js new file mode 100644 index 0000000000..6eca569250 --- /dev/null +++ b/test/runtime/samples/attribute-null-classnames/_config.js @@ -0,0 +1,39 @@ +export default { + skip_if_ssr: true, + + props: { + testName1: "test1", + testName2: "test2", + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = null; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = "test"; + assert.equal(div.className, 'nulltest svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = "test"; + assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = undefined; + assert.equal(div.className, 'NaN svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = 1; + assert.equal(div.className, '1 svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = 1; + assert.equal(div.className, 'NaN svelte-x1o6ra'); + } +}; diff --git a/test/runtime/samples/attribute-null-classnames/main.svelte b/test/runtime/samples/attribute-null-classnames/main.svelte new file mode 100644 index 0000000000..06098fd50b --- /dev/null +++ b/test/runtime/samples/attribute-null-classnames/main.svelte @@ -0,0 +1,12 @@ + + + + +
From 0315b723b00a5eff36b6156d0cb80c1fe8b3dd82 Mon Sep 17 00:00:00 2001 From: bre30kra69cs Date: Thu, 1 Aug 2019 15:46:53 +0300 Subject: [PATCH 02/12] optimize class name runtime calc & add tests & ref --- .../render_dom/wrappers/Element/Attribute.ts | 63 +++++++++++++------ src/runtime/internal/utils.ts | 4 ++ .../samples/attribute-false/_config.js | 5 ++ .../samples/attribute-false/main.svelte | 1 + .../_config.js | 44 +++++++++++++ .../main.svelte | 5 ++ .../_config.js | 12 ++++ .../main.svelte | 0 .../_config.js | 47 ++++++++++++++ .../main.svelte | 6 ++ .../_config.js | 8 +++ .../main.svelte | 0 .../_config.js | 44 +++++++++++++ .../main.svelte | 9 +++ .../_config.js | 44 +++++++++++++ .../main.svelte | 15 +++++ .../_config.js | 47 ++++++++++++++ .../main.svelte | 11 ++++ .../_config.js | 47 ++++++++++++++ .../main.svelte | 17 +++++ .../runtime/samples/attribute-null/_config.js | 5 ++ .../samples/attribute-null/main.svelte | 1 + .../samples/attribute-undefined/_config.js | 5 ++ .../samples/attribute-undefined/main.svelte | 1 + 24 files changed, 421 insertions(+), 20 deletions(-) create mode 100644 test/runtime/samples/attribute-false/_config.js create mode 100644 test/runtime/samples/attribute-false/main.svelte create mode 100644 test/runtime/samples/attribute-null-classname-no-style/_config.js create mode 100644 test/runtime/samples/attribute-null-classname-no-style/main.svelte rename test/runtime/samples/{attribute-null-classname => attribute-null-classname-with-style}/_config.js (70%) rename test/runtime/samples/{attribute-null-classname => attribute-null-classname-with-style}/main.svelte (100%) create mode 100644 test/runtime/samples/attribute-null-classnames-no-style/_config.js create mode 100644 test/runtime/samples/attribute-null-classnames-no-style/main.svelte rename test/runtime/samples/{attribute-null-classnames => attribute-null-classnames-with-style}/_config.js (81%) rename test/runtime/samples/{attribute-null-classnames => attribute-null-classnames-with-style}/main.svelte (100%) create mode 100644 test/runtime/samples/attribute-null-func-classname-no-style/_config.js create mode 100644 test/runtime/samples/attribute-null-func-classname-no-style/main.svelte create mode 100644 test/runtime/samples/attribute-null-func-classname-with-style/_config.js create mode 100644 test/runtime/samples/attribute-null-func-classname-with-style/main.svelte create mode 100644 test/runtime/samples/attribute-null-func-classnames-no-style/_config.js create mode 100644 test/runtime/samples/attribute-null-func-classnames-no-style/main.svelte create mode 100644 test/runtime/samples/attribute-null-func-classnames-with-style/_config.js create mode 100644 test/runtime/samples/attribute-null-func-classnames-with-style/main.svelte create mode 100644 test/runtime/samples/attribute-null/_config.js create mode 100644 test/runtime/samples/attribute-null/main.svelte create mode 100644 test/runtime/samples/attribute-undefined/_config.js create mode 100644 test/runtime/samples/attribute-undefined/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index aff06e4150..7c174acb46 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -5,6 +5,7 @@ import ElementWrapper from './index'; import { stringify } from '../../../utils/stringify'; import deindent from '../../../utils/deindent'; import Expression from '../../../nodes/shared/Expression'; +import Text from '../../../nodes/Text'; export default class AttributeWrapper { node: Attribute; @@ -84,26 +85,13 @@ export default class AttributeWrapper { value = (this.node.chunks[0] as Expression).render(block); } else { // '{foo} {bar}' — treat as string concatenation - value = - (this.node.chunks[0].type === 'Text' ? '' : `"" + `) + - this.node.chunks - .map((chunk) => { - if (chunk.type === 'Text') { - return stringify(chunk.data); - } else { - const renderedChunk = chunk.render(); - if (this.node.name === 'class') { - return chunk.get_precedence() <= 13 - ? `(${renderedChunk})` - : `(${renderedChunk} || '')`; - } else { - return chunk.get_precedence() <= 13 - ? `(${renderedChunk})` - : renderedChunk; - } - } - }) - .join(' + '); + const attrPrefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `; + + const attrText = this.node.name === 'class' + ? this.get_class_name_text() + : this.get_attr_text(); + + value = `${attrPrefix}${attrText}`; } const is_select_value_attribute = @@ -217,6 +205,41 @@ export default class AttributeWrapper { } } + get_class_name_text() { + const isStyled = this.node.chunks + .filter((chunk) => chunk.type === 'Text') + .some((chunk: Text) => !chunk.start && !chunk.end); + + const classNameStringArray = this.render_attr(); + + + if (!isStyled || classNameStringArray.length !== 2) { + return classNameStringArray.join(' + '); + } + + const targetToken = 0; + return classNameStringArray + .map((token, index) => index === targetToken ? `@class_name_resolver(${token})` : token) + .join(' + '); + } + + get_attr_text() { + return this.render_attr().join(' + '); + } + + render_attr() { + return this.node.chunks.map((chunk) => { + if (chunk.type === 'Text') { + return stringify(chunk.data); + } + + const renderedChunk = chunk.render(); + return chunk.get_precedence() <= 13 + ? `(${renderedChunk})` + : renderedChunk; + }); + } + stringify() { if (this.node.is_true) return ''; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 08410ec33a..1aec30ef2f 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -89,3 +89,7 @@ export function once(fn) { fn.call(this, ...args); }; } + +export function class_name_resolver(nextClassName) { + return nextClassName == undefined ? '' : nextClassName; +} \ No newline at end of file diff --git a/test/runtime/samples/attribute-false/_config.js b/test/runtime/samples/attribute-false/_config.js new file mode 100644 index 0000000000..0f37392806 --- /dev/null +++ b/test/runtime/samples/attribute-false/_config.js @@ -0,0 +1,5 @@ +export default { + skip_if_ssr: true, + + html: `
`, +}; diff --git a/test/runtime/samples/attribute-false/main.svelte b/test/runtime/samples/attribute-false/main.svelte new file mode 100644 index 0000000000..ca78020a39 --- /dev/null +++ b/test/runtime/samples/attribute-false/main.svelte @@ -0,0 +1 @@ +
diff --git a/test/runtime/samples/attribute-null-classname-no-style/_config.js b/test/runtime/samples/attribute-null-classname-no-style/_config.js new file mode 100644 index 0000000000..e2098430e4 --- /dev/null +++ b/test/runtime/samples/attribute-null-classname-no-style/_config.js @@ -0,0 +1,44 @@ +export default { + skip_if_ssr: true, + + props: { + testName: "testClassName" + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'testClassName'); + + component.testName = null; + assert.equal(div.className, ''); + + component.testName = undefined; + assert.equal(div.className, ''); + + component.testName = undefined + ''; + assert.equal(div.className, 'undefined'); + + component.testName = null + ''; + assert.equal(div.className, 'null'); + + component.testName = 1; + assert.equal(div.className, '1'); + + component.testName = 0; + assert.equal(div.className, '0'); + + component.testName = false; + assert.equal(div.className, 'false'); + + component.testName = true; + assert.equal(div.className, 'true'); + + component.testName = {}; + assert.equal(div.className, '[object Object]'); + + component.testName = ''; + assert.equal(div.className, ''); + } +}; diff --git a/test/runtime/samples/attribute-null-classname-no-style/main.svelte b/test/runtime/samples/attribute-null-classname-no-style/main.svelte new file mode 100644 index 0000000000..8dfcd7af54 --- /dev/null +++ b/test/runtime/samples/attribute-null-classname-no-style/main.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/test/runtime/samples/attribute-null-classname/_config.js b/test/runtime/samples/attribute-null-classname-with-style/_config.js similarity index 70% rename from test/runtime/samples/attribute-null-classname/_config.js rename to test/runtime/samples/attribute-null-classname-with-style/_config.js index 856bd8bcd0..635dd058ca 100644 --- a/test/runtime/samples/attribute-null-classname/_config.js +++ b/test/runtime/samples/attribute-null-classname-with-style/_config.js @@ -26,6 +26,18 @@ export default { component.testName = 1; assert.equal(div.className, '1 svelte-x1o6ra'); + component.testName = 0; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName = false; + assert.equal(div.className, 'false svelte-x1o6ra'); + + component.testName = true; + assert.equal(div.className, 'true svelte-x1o6ra'); + + component.testName = {}; + assert.equal(div.className, '[object Object] svelte-x1o6ra'); + component.testName = ''; assert.equal(div.className, ' svelte-x1o6ra'); } diff --git a/test/runtime/samples/attribute-null-classname/main.svelte b/test/runtime/samples/attribute-null-classname-with-style/main.svelte similarity index 100% rename from test/runtime/samples/attribute-null-classname/main.svelte rename to test/runtime/samples/attribute-null-classname-with-style/main.svelte diff --git a/test/runtime/samples/attribute-null-classnames-no-style/_config.js b/test/runtime/samples/attribute-null-classnames-no-style/_config.js new file mode 100644 index 0000000000..6a734efff5 --- /dev/null +++ b/test/runtime/samples/attribute-null-classnames-no-style/_config.js @@ -0,0 +1,47 @@ +export default { + skip_if_ssr: true, + + props: { + testName1: "test1", + testName2: "test2", + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'test1test2'); + + component.testName1 = null; + component.testName2 = null; + assert.equal(div.className, '0'); + + component.testName1 = null; + component.testName2 = "test"; + assert.equal(div.className, 'nulltest'); + + component.testName1 = undefined; + component.testName2 = "test"; + assert.equal(div.className, 'undefinedtest'); + + component.testName1 = undefined; + component.testName2 = undefined; + assert.equal(div.className, 'NaN'); + + component.testName1 = null; + component.testName2 = 1; + assert.equal(div.className, '1'); + + component.testName1 = undefined; + component.testName2 = 1; + assert.equal(div.className, 'NaN'); + + component.testName1 = null; + component.testName2 = 0; + assert.equal(div.className, '0'); + + component.testName1 = undefined; + component.testName2 = 0; + assert.equal(div.className, 'NaN'); + } +}; diff --git a/test/runtime/samples/attribute-null-classnames-no-style/main.svelte b/test/runtime/samples/attribute-null-classnames-no-style/main.svelte new file mode 100644 index 0000000000..f8cf7d803e --- /dev/null +++ b/test/runtime/samples/attribute-null-classnames-no-style/main.svelte @@ -0,0 +1,6 @@ + + +
diff --git a/test/runtime/samples/attribute-null-classnames/_config.js b/test/runtime/samples/attribute-null-classnames-with-style/_config.js similarity index 81% rename from test/runtime/samples/attribute-null-classnames/_config.js rename to test/runtime/samples/attribute-null-classnames-with-style/_config.js index 6eca569250..22f7a4d7e2 100644 --- a/test/runtime/samples/attribute-null-classnames/_config.js +++ b/test/runtime/samples/attribute-null-classnames-with-style/_config.js @@ -35,5 +35,13 @@ export default { component.testName1 = undefined; component.testName2 = 1; assert.equal(div.className, 'NaN svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = 0; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = 0; + assert.equal(div.className, 'NaN svelte-x1o6ra'); } }; diff --git a/test/runtime/samples/attribute-null-classnames/main.svelte b/test/runtime/samples/attribute-null-classnames-with-style/main.svelte similarity index 100% rename from test/runtime/samples/attribute-null-classnames/main.svelte rename to test/runtime/samples/attribute-null-classnames-with-style/main.svelte diff --git a/test/runtime/samples/attribute-null-func-classname-no-style/_config.js b/test/runtime/samples/attribute-null-func-classname-no-style/_config.js new file mode 100644 index 0000000000..e2098430e4 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classname-no-style/_config.js @@ -0,0 +1,44 @@ +export default { + skip_if_ssr: true, + + props: { + testName: "testClassName" + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'testClassName'); + + component.testName = null; + assert.equal(div.className, ''); + + component.testName = undefined; + assert.equal(div.className, ''); + + component.testName = undefined + ''; + assert.equal(div.className, 'undefined'); + + component.testName = null + ''; + assert.equal(div.className, 'null'); + + component.testName = 1; + assert.equal(div.className, '1'); + + component.testName = 0; + assert.equal(div.className, '0'); + + component.testName = false; + assert.equal(div.className, 'false'); + + component.testName = true; + assert.equal(div.className, 'true'); + + component.testName = {}; + assert.equal(div.className, '[object Object]'); + + component.testName = ''; + assert.equal(div.className, ''); + } +}; diff --git a/test/runtime/samples/attribute-null-func-classname-no-style/main.svelte b/test/runtime/samples/attribute-null-func-classname-no-style/main.svelte new file mode 100644 index 0000000000..e2754cd421 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classname-no-style/main.svelte @@ -0,0 +1,9 @@ + + +
diff --git a/test/runtime/samples/attribute-null-func-classname-with-style/_config.js b/test/runtime/samples/attribute-null-func-classname-with-style/_config.js new file mode 100644 index 0000000000..635dd058ca --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classname-with-style/_config.js @@ -0,0 +1,44 @@ +export default { + skip_if_ssr: true, + + props: { + testName: "testClassName" + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'testClassName svelte-x1o6ra'); + + component.testName = null; + assert.equal(div.className, ' svelte-x1o6ra'); + + component.testName = undefined; + assert.equal(div.className, ' svelte-x1o6ra'); + + component.testName = undefined + ''; + assert.equal(div.className, 'undefined svelte-x1o6ra'); + + component.testName = null + ''; + assert.equal(div.className, 'null svelte-x1o6ra'); + + component.testName = 1; + assert.equal(div.className, '1 svelte-x1o6ra'); + + component.testName = 0; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName = false; + assert.equal(div.className, 'false svelte-x1o6ra'); + + component.testName = true; + assert.equal(div.className, 'true svelte-x1o6ra'); + + component.testName = {}; + assert.equal(div.className, '[object Object] svelte-x1o6ra'); + + component.testName = ''; + assert.equal(div.className, ' svelte-x1o6ra'); + } +}; diff --git a/test/runtime/samples/attribute-null-func-classname-with-style/main.svelte b/test/runtime/samples/attribute-null-func-classname-with-style/main.svelte new file mode 100644 index 0000000000..bcb858ab8d --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classname-with-style/main.svelte @@ -0,0 +1,15 @@ + + + + +
diff --git a/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js b/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js new file mode 100644 index 0000000000..6a734efff5 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js @@ -0,0 +1,47 @@ +export default { + skip_if_ssr: true, + + props: { + testName1: "test1", + testName2: "test2", + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'test1test2'); + + component.testName1 = null; + component.testName2 = null; + assert.equal(div.className, '0'); + + component.testName1 = null; + component.testName2 = "test"; + assert.equal(div.className, 'nulltest'); + + component.testName1 = undefined; + component.testName2 = "test"; + assert.equal(div.className, 'undefinedtest'); + + component.testName1 = undefined; + component.testName2 = undefined; + assert.equal(div.className, 'NaN'); + + component.testName1 = null; + component.testName2 = 1; + assert.equal(div.className, '1'); + + component.testName1 = undefined; + component.testName2 = 1; + assert.equal(div.className, 'NaN'); + + component.testName1 = null; + component.testName2 = 0; + assert.equal(div.className, '0'); + + component.testName1 = undefined; + component.testName2 = 0; + assert.equal(div.className, 'NaN'); + } +}; diff --git a/test/runtime/samples/attribute-null-func-classnames-no-style/main.svelte b/test/runtime/samples/attribute-null-func-classnames-no-style/main.svelte new file mode 100644 index 0000000000..e1cdf51412 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classnames-no-style/main.svelte @@ -0,0 +1,11 @@ + + +
diff --git a/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js b/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js new file mode 100644 index 0000000000..22f7a4d7e2 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js @@ -0,0 +1,47 @@ +export default { + skip_if_ssr: true, + + props: { + testName1: "test1", + testName2: "test2", + }, + + html: `
`, + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div.className, 'test1test2 svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = null; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = "test"; + assert.equal(div.className, 'nulltest svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = "test"; + assert.equal(div.className, 'undefinedtest svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = undefined; + assert.equal(div.className, 'NaN svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = 1; + assert.equal(div.className, '1 svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = 1; + assert.equal(div.className, 'NaN svelte-x1o6ra'); + + component.testName1 = null; + component.testName2 = 0; + assert.equal(div.className, '0 svelte-x1o6ra'); + + component.testName1 = undefined; + component.testName2 = 0; + assert.equal(div.className, 'NaN svelte-x1o6ra'); + } +}; diff --git a/test/runtime/samples/attribute-null-func-classnames-with-style/main.svelte b/test/runtime/samples/attribute-null-func-classnames-with-style/main.svelte new file mode 100644 index 0000000000..af43778365 --- /dev/null +++ b/test/runtime/samples/attribute-null-func-classnames-with-style/main.svelte @@ -0,0 +1,17 @@ + + + + +
diff --git a/test/runtime/samples/attribute-null/_config.js b/test/runtime/samples/attribute-null/_config.js new file mode 100644 index 0000000000..938d525cf6 --- /dev/null +++ b/test/runtime/samples/attribute-null/_config.js @@ -0,0 +1,5 @@ +export default { + skip_if_ssr: true, + + html: `
`, +}; diff --git a/test/runtime/samples/attribute-null/main.svelte b/test/runtime/samples/attribute-null/main.svelte new file mode 100644 index 0000000000..9b7f48eda3 --- /dev/null +++ b/test/runtime/samples/attribute-null/main.svelte @@ -0,0 +1 @@ +
diff --git a/test/runtime/samples/attribute-undefined/_config.js b/test/runtime/samples/attribute-undefined/_config.js new file mode 100644 index 0000000000..938d525cf6 --- /dev/null +++ b/test/runtime/samples/attribute-undefined/_config.js @@ -0,0 +1,5 @@ +export default { + skip_if_ssr: true, + + html: `
`, +}; diff --git a/test/runtime/samples/attribute-undefined/main.svelte b/test/runtime/samples/attribute-undefined/main.svelte new file mode 100644 index 0000000000..5108aa873f --- /dev/null +++ b/test/runtime/samples/attribute-undefined/main.svelte @@ -0,0 +1 @@ +
From 4bb10d30c4fc862770dd3aec86f8a2f7e5a73e72 Mon Sep 17 00:00:00 2001 From: bre30kra69cs Date: Thu, 1 Aug 2019 15:58:50 +0300 Subject: [PATCH 03/12] ref spaces --- src/compiler/compile/render_dom/wrappers/Element/Attribute.ts | 1 - src/runtime/internal/utils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index 7c174acb46..eb242e994b 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -212,7 +212,6 @@ export default class AttributeWrapper { const classNameStringArray = this.render_attr(); - if (!isStyled || classNameStringArray.length !== 2) { return classNameStringArray.join(' + '); } diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 1aec30ef2f..3cef77e7be 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -92,4 +92,4 @@ export function once(fn) { export function class_name_resolver(nextClassName) { return nextClassName == undefined ? '' : nextClassName; -} \ No newline at end of file +} From 27c5509e02e2ba115a47d6baa9859bc26d8eca0e Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 2 Aug 2019 09:11:02 -0400 Subject: [PATCH 04/12] make code style more consistent --- src/compiler/compile/nodes/Element.ts | 12 ++++-- src/compiler/compile/nodes/Text.ts | 2 + .../render_dom/wrappers/Element/Attribute.ts | 37 +++++++------------ src/runtime/internal/utils.ts | 4 +- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index a3030f52a5..c5f7852750 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -702,16 +702,20 @@ export default class Element extends Node { return this.name === 'audio' || this.name === 'video'; } - add_css_class(class_name = this.component.stylesheet.id) { + add_css_class() { + const { id } = this.component.stylesheet; + const class_attribute = this.attributes.find(a => a.name === 'class'); + if (class_attribute && !class_attribute.is_true) { if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') { - (class_attribute.chunks[0] as Text).data += ` ${class_name}`; + (class_attribute.chunks[0] as Text).data += ` ${id}`; } else { (class_attribute.chunks as Node[]).push( new Text(this.component, this, this.scope, { type: 'Text', - data: ` ${class_name}` + data: ` ${id}`, + synthetic: true }) ); } @@ -720,7 +724,7 @@ export default class Element extends Node { new Attribute(this.component, this, this.scope, { type: 'Attribute', name: 'class', - value: [{ type: 'Text', data: class_name }] + value: [{ type: 'Text', data: id, synthetic: true }] }) ); } diff --git a/src/compiler/compile/nodes/Text.ts b/src/compiler/compile/nodes/Text.ts index eff3efe06e..bfd28a5073 100644 --- a/src/compiler/compile/nodes/Text.ts +++ b/src/compiler/compile/nodes/Text.ts @@ -6,9 +6,11 @@ import { INode } from './interfaces'; export default class Text extends Node { type: 'Text'; data: string; + synthetic: boolean; constructor(component: Component, parent: INode, scope: TemplateScope, info: any) { super(component, parent, scope, info); this.data = info.data; + this.synthetic = info.synthetic || false; } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index eb242e994b..f83b1f2acf 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -85,13 +85,13 @@ export default class AttributeWrapper { value = (this.node.chunks[0] as Expression).render(block); } else { // '{foo} {bar}' — treat as string concatenation - const attrPrefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `; + const prefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `; - const attrText = this.node.name === 'class' + const text = this.node.name === 'class' ? this.get_class_name_text() - : this.get_attr_text(); + : this.render_chunks().join(' + '); - value = `${attrPrefix}${attrText}`; + value = `${prefix}${text}`; } const is_select_value_attribute = @@ -206,36 +206,27 @@ export default class AttributeWrapper { } get_class_name_text() { - const isStyled = this.node.chunks - .filter((chunk) => chunk.type === 'Text') - .some((chunk: Text) => !chunk.start && !chunk.end); + const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic); + const rendered = this.render_chunks(); - const classNameStringArray = this.render_attr(); - - if (!isStyled || classNameStringArray.length !== 2) { - return classNameStringArray.join(' + '); + if (scoped_css && rendered.length === 2) { + // we have a situation like class={possiblyUndefined} + rendered[0] = `@null_to_empty(${rendered[0]})`; } - const targetToken = 0; - return classNameStringArray - .map((token, index) => index === targetToken ? `@class_name_resolver(${token})` : token) - .join(' + '); - } - - get_attr_text() { - return this.render_attr().join(' + '); + return rendered.join(' + '); } - render_attr() { + render_chunks() { return this.node.chunks.map((chunk) => { if (chunk.type === 'Text') { return stringify(chunk.data); } - const renderedChunk = chunk.render(); + const rendered = chunk.render(); return chunk.get_precedence() <= 13 - ? `(${renderedChunk})` - : renderedChunk; + ? `(${rendered})` + : rendered; }); } diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 3cef77e7be..47ada924ae 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -90,6 +90,6 @@ export function once(fn) { }; } -export function class_name_resolver(nextClassName) { - return nextClassName == undefined ? '' : nextClassName; +export function null_to_empty(value) { + return value == null ? '' : value; } From cbddd2f9611ff63a3d274c5d221ba88c2253feb4 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 2 Aug 2019 09:11:22 -0400 Subject: [PATCH 05/12] remove unused code --- .../render_dom/wrappers/Element/index.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 9d3f60ac7e..b769eb7931 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -816,29 +816,4 @@ export default class ElementWrapper extends Wrapper { } }); } - - // todo: looks to be dead code copypasted from Element.add_css_class in src/compile/nodes/Element.ts - // add_css_class(class_name = this.component.stylesheet.id) { - // const class_attribute = this.attributes.find(a => a.name === 'class'); - // if (class_attribute && !class_attribute.is_true) { - // if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') { - // (class_attribute.chunks[0] as Text).data += ` ${class_name}`; - // } else { - // (class_attribute.chunks as Node[]).push( - // new Text(this.component, this, this.scope, { - // type: 'Text', - // data: ` ${class_name}` - // }) - // ); - // } - // } else { - // this.attributes.push( - // new Attribute(this.component, this, this.scope, { - // type: 'Attribute', - // name: 'class', - // value: [{ type: 'Text', data: class_name }] - // }) - // ); - // } - // } } From 41e865f1f693fde5ffc32879ad0dc6e11bc74989 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 2 Aug 2019 09:33:20 -0400 Subject: [PATCH 06/12] failing SSR test, plus some adjacent changes --- src/compiler/compile/render_ssr/handlers/Element.ts | 3 +-- src/runtime/internal/ssr.ts | 2 +- test/runtime/samples/attribute-false/_config.js | 2 -- .../attribute-null-classname-no-style/_config.js | 2 -- .../attribute-null-classname-with-style/_config.js | 12 ++++-------- .../attribute-null-classnames-no-style/_config.js | 2 -- .../attribute-null-classnames-with-style/_config.js | 2 -- .../_config.js | 2 -- .../_config.js | 2 -- .../_config.js | 2 -- .../_config.js | 2 -- test/runtime/samples/attribute-null/_config.js | 2 -- test/runtime/samples/attribute-undefined/_config.js | 2 -- 13 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 0cd101df72..8b64361c4f 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -140,8 +140,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { const { name } = attribute; const snippet = snip(attribute.chunks[0]); - - opening_tag += '${(v => v == null ? "" : ` ' + name + '="${@escape(' + snippet + ')}"`)(' + snippet + ')}'; + opening_tag += '${@add_attribute("' + name + '", ' + snippet + ')}'; } else { opening_tag += ` ${attribute.name}="${stringify_attribute(attribute, true)}"`; } diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 1ae1ae1d12..4bc06f4351 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -134,7 +134,7 @@ export function get_store_value(store: Readable): T | undefined { } export function add_attribute(name, value) { - if (!value) return ''; + if (value == null) return ''; return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(value) : `"${value}"`}`}`; } diff --git a/test/runtime/samples/attribute-false/_config.js b/test/runtime/samples/attribute-false/_config.js index 0f37392806..9fd08a2a48 100644 --- a/test/runtime/samples/attribute-false/_config.js +++ b/test/runtime/samples/attribute-false/_config.js @@ -1,5 +1,3 @@ export default { - skip_if_ssr: true, - html: `
`, }; diff --git a/test/runtime/samples/attribute-null-classname-no-style/_config.js b/test/runtime/samples/attribute-null-classname-no-style/_config.js index e2098430e4..4a78b680ef 100644 --- a/test/runtime/samples/attribute-null-classname-no-style/_config.js +++ b/test/runtime/samples/attribute-null-classname-no-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName: "testClassName" }, diff --git a/test/runtime/samples/attribute-null-classname-with-style/_config.js b/test/runtime/samples/attribute-null-classname-with-style/_config.js index 635dd058ca..3ebc593b5d 100644 --- a/test/runtime/samples/attribute-null-classname-with-style/_config.js +++ b/test/runtime/samples/attribute-null-classname-with-style/_config.js @@ -1,15 +1,8 @@ export default { - skip_if_ssr: true, - - props: { - testName: "testClassName" - }, - - html: `
`, + html: `
`, test({ assert, component, target }) { const div = target.querySelector('div'); - assert.equal(div.className, 'testClassName svelte-x1o6ra'); component.testName = null; assert.equal(div.className, ' svelte-x1o6ra'); @@ -40,5 +33,8 @@ export default { component.testName = ''; assert.equal(div.className, ' svelte-x1o6ra'); + + component.testName = 'testClassName'; + assert.equal(div.className, 'testClassName svelte-x1o6ra'); } }; diff --git a/test/runtime/samples/attribute-null-classnames-no-style/_config.js b/test/runtime/samples/attribute-null-classnames-no-style/_config.js index 6a734efff5..917cf565c0 100644 --- a/test/runtime/samples/attribute-null-classnames-no-style/_config.js +++ b/test/runtime/samples/attribute-null-classnames-no-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName1: "test1", testName2: "test2", diff --git a/test/runtime/samples/attribute-null-classnames-with-style/_config.js b/test/runtime/samples/attribute-null-classnames-with-style/_config.js index 22f7a4d7e2..5762f628fb 100644 --- a/test/runtime/samples/attribute-null-classnames-with-style/_config.js +++ b/test/runtime/samples/attribute-null-classnames-with-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName1: "test1", testName2: "test2", diff --git a/test/runtime/samples/attribute-null-func-classname-no-style/_config.js b/test/runtime/samples/attribute-null-func-classname-no-style/_config.js index e2098430e4..4a78b680ef 100644 --- a/test/runtime/samples/attribute-null-func-classname-no-style/_config.js +++ b/test/runtime/samples/attribute-null-func-classname-no-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName: "testClassName" }, diff --git a/test/runtime/samples/attribute-null-func-classname-with-style/_config.js b/test/runtime/samples/attribute-null-func-classname-with-style/_config.js index 635dd058ca..1ed43d05b9 100644 --- a/test/runtime/samples/attribute-null-func-classname-with-style/_config.js +++ b/test/runtime/samples/attribute-null-func-classname-with-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName: "testClassName" }, diff --git a/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js b/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js index 6a734efff5..917cf565c0 100644 --- a/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js +++ b/test/runtime/samples/attribute-null-func-classnames-no-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName1: "test1", testName2: "test2", diff --git a/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js b/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js index 22f7a4d7e2..5762f628fb 100644 --- a/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js +++ b/test/runtime/samples/attribute-null-func-classnames-with-style/_config.js @@ -1,6 +1,4 @@ export default { - skip_if_ssr: true, - props: { testName1: "test1", testName2: "test2", diff --git a/test/runtime/samples/attribute-null/_config.js b/test/runtime/samples/attribute-null/_config.js index 938d525cf6..ae2f0a8af6 100644 --- a/test/runtime/samples/attribute-null/_config.js +++ b/test/runtime/samples/attribute-null/_config.js @@ -1,5 +1,3 @@ export default { - skip_if_ssr: true, - html: `
`, }; diff --git a/test/runtime/samples/attribute-undefined/_config.js b/test/runtime/samples/attribute-undefined/_config.js index 938d525cf6..ae2f0a8af6 100644 --- a/test/runtime/samples/attribute-undefined/_config.js +++ b/test/runtime/samples/attribute-undefined/_config.js @@ -1,5 +1,3 @@ export default { - skip_if_ssr: true, - html: `
`, }; From 505717e636890600b27b859a4d624ce6e876df75 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 3 Aug 2019 00:30:38 -0400 Subject: [PATCH 07/12] don't warn when using each index in key (#3274) --- src/compiler/compile/nodes/EachBlock.ts | 10 +++++----- test/validator/samples/undefined-value/input.svelte | 10 +++++----- test/validator/samples/undefined-value/warnings.json | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts index adaa46b8db..225f3b441e 100644 --- a/src/compiler/compile/nodes/EachBlock.ts +++ b/src/compiler/compile/nodes/EachBlock.ts @@ -83,16 +83,16 @@ export default class EachBlock extends AbstractBlock { this.scope.add(context.key.name, this.expression.dependencies, this); }); - this.key = info.key - ? new Expression(component, this, this.scope, info.key) - : null; - if (this.index) { // index can only change if this is a keyed each block - const dependencies = this.key ? this.expression.dependencies : new Set([]); + const dependencies = info.key ? this.expression.dependencies : new Set([]); this.scope.add(this.index, dependencies, this); } + this.key = info.key + ? new Expression(component, this, this.scope, info.key) + : null; + this.has_animation = false; this.children = map_children(component, this, this.scope, info.children); diff --git a/test/validator/samples/undefined-value/input.svelte b/test/validator/samples/undefined-value/input.svelte index 03dda2c44f..0dec9632a7 100644 --- a/test/validator/samples/undefined-value/input.svelte +++ b/test/validator/samples/undefined-value/input.svelte @@ -1,6 +1,6 @@ - -

{potato}

-

{Math.max(1, 2)}

\ No newline at end of file +

{Math.max(1, 2)}

+ +{#each window.something as foo, i (foo.x + i)} + hello +{/each} diff --git a/test/validator/samples/undefined-value/warnings.json b/test/validator/samples/undefined-value/warnings.json index c1813e6ab0..790f83acfd 100644 --- a/test/validator/samples/undefined-value/warnings.json +++ b/test/validator/samples/undefined-value/warnings.json @@ -1,15 +1,15 @@ [{ "code": "missing-declaration", - "message": "'potato' is not defined", - "pos": 67, + "message": "'potato' is not defined. Consider adding a