diff --git a/src/compiler/compile/nodes/Attribute.ts b/src/compiler/compile/nodes/Attribute.ts index 97d2fd7b2e..cde21b344d 100644 --- a/src/compiler/compile/nodes/Attribute.ts +++ b/src/compiler/compile/nodes/Attribute.ts @@ -18,6 +18,7 @@ export default class Attribute extends Node { parent: Element; name: string; is_spread: boolean; + is_boolean: boolean; is_true: boolean; is_static: boolean; expression?: Expression; @@ -31,7 +32,7 @@ export default class Attribute extends Node { if (info.type === 'Spread') { this.name = null; this.is_spread = true; - this.is_true = false; + this.is_boolean = false; this.expression = new Expression(component, this, scope, info.expression); this.dependencies = this.expression.dependencies; @@ -42,12 +43,13 @@ export default class Attribute extends Node { else { this.name = info.name; - this.is_true = info.value === true; + this.is_boolean = typeof (info.value) === 'boolean'; + if (this.is_boolean) this.is_true = info.value === true; this.is_static = true; this.dependencies = new Set(); - this.chunks = this.is_true + this.chunks = this.is_boolean ? [] : info.value.map(node => { if (node.type === 'Text') return node; @@ -76,7 +78,7 @@ export default class Attribute extends Node { } get_value(block) { - if (this.is_true) return x`true`; + if (this.is_boolean) return this.is_true ? x`true` : x`false`; if (this.chunks.length === 0) return x`""`; if (this.chunks.length === 1) { @@ -99,8 +101,8 @@ export default class Attribute extends Node { get_static_value() { if (this.is_spread || this.dependencies.size > 0) return null; - return this.is_true - ? true + return this.is_boolean + ? this.is_true : this.chunks[0] // method should be called only when `is_static = true` ? (this.chunks[0] as Text).data diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 8c9c0bfe18..92531f17f9 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -721,7 +721,7 @@ export default class Element extends Node { const class_attribute = this.attributes.find(a => a.name === 'class'); - if (class_attribute && !class_attribute.is_true) { + if (class_attribute && !class_attribute.is_boolean) { if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') { (class_attribute.chunks[0] as Text).data += ` ${id}`; } else { diff --git a/src/compiler/compile/nodes/shared/Node.ts b/src/compiler/compile/nodes/shared/Node.ts index 7435f1eab8..b323a095d3 100644 --- a/src/compiler/compile/nodes/shared/Node.ts +++ b/src/compiler/compile/nodes/shared/Node.ts @@ -53,7 +53,7 @@ export default class Node { if (!attribute) return null; - if (attribute.is_true) return true; + if (attribute.is_boolean) return attribute.is_true; if (attribute.chunks.length === 0) return ''; if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') { diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index 85f252f57e..2b30ebfcf3 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -193,10 +193,10 @@ export default class AttributeWrapper { } get_value(block) { - if (this.node.is_true) { + if (this.node.is_boolean) { const metadata = this.get_metadata(); if (metadata && boolean_attribute.has(metadata.property_name.toLowerCase())) { - return x`true`; + return this.node.is_true ? x`true` : x`false`; } return x`""`; } @@ -245,7 +245,7 @@ export default class AttributeWrapper { } stringify() { - if (this.node.is_true) return ''; + if (this.node.is_boolean) return ''; const value = this.node.chunks; if (value.length === 0) return `=""`; diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index ad5c421bc4..d9a59693ef 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -54,8 +54,8 @@ export default function(node: Element, renderer: Renderer, options: RenderOption const name = attribute.name.toLowerCase(); if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); - } else if (attribute.is_true) { - args.push(x`{ ${attribute.name}: true }`); + } else if (attribute.is_boolean) { + args.push(attribute.is_true ? x`{ ${attribute.name}: true }` : x`{ ${attribute.name}: false }` ); } else if ( boolean_attributes.has(name) && attribute.chunks.length === 1 && @@ -76,8 +76,12 @@ export default function(node: Element, renderer: Renderer, options: RenderOption const name = attribute.name.toLowerCase(); if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); - } else if (attribute.is_true) { - renderer.add_string(` ${attribute.name}`); + } else if (attribute.is_boolean) { + if (attribute.is_true) { + renderer.add_string(` ${attribute.name}`); + } else { + renderer.add_string(` !${attribute.name}`); + } } else if ( boolean_attributes.has(name) && attribute.chunks.length === 1 && diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index 37f05a941c..e45812acc0 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -6,7 +6,7 @@ import remove_whitespace_children from './utils/remove_whitespace_children'; import { p, x } from 'code-red'; function get_prop_value(attribute) { - if (attribute.is_true) return x`true`; + if (attribute.is_boolean) return attribute.is_true ? x`true` : x`false`; if (attribute.chunks.length === 0) return x`''`; return attribute.chunks diff --git a/src/compiler/compile/utils/get_slot_data.ts b/src/compiler/compile/utils/get_slot_data.ts index a118d52023..4aa0f5f1ac 100644 --- a/src/compiler/compile/utils/get_slot_data.ts +++ b/src/compiler/compile/utils/get_slot_data.ts @@ -16,7 +16,9 @@ export default function get_slot_data(values: Map, block: Blo } function get_value(block: Block, attribute: Attribute) { - if (attribute.is_true) return x`true`; + if (attribute.is_boolean) { + return attribute.is_true ? x`true` : x`false`; + } if (attribute.chunks.length === 0) return x`""`; let value = attribute.chunks diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index ce8a0a9aa7..85f82c56f8 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -343,7 +343,7 @@ function read_attribute(parser: Parser, unique_names: Set) { } // eslint-disable-next-line no-useless-escape - const name = parser.read_until(/[\s=\/>"']/); + let name = parser.read_until(/[\s=\/>"']/); if (!name) return null; let end = parser.index; @@ -353,8 +353,12 @@ function read_attribute(parser: Parser, unique_names: Set) { const colon_index = name.indexOf(':'); const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index)); - let value: any[] | true = true; - if (parser.eat('=')) { + let value: any[] | boolean = true ; + + if (name.startsWith('!')) { + value = false; + name = name.slice(1); + } else if (parser.eat('=')) { parser.allow_whitespace(); value = read_attribute_value(parser); end = parser.index; diff --git a/test/runtime/samples/component-data-static-boolean-false/Foo.svelte b/test/runtime/samples/component-data-static-boolean-false/Foo.svelte new file mode 100644 index 0000000000..15f9f556de --- /dev/null +++ b/test/runtime/samples/component-data-static-boolean-false/Foo.svelte @@ -0,0 +1,5 @@ + + +

x: {x} ({typeof x})

\ No newline at end of file diff --git a/test/runtime/samples/component-data-static-boolean-false/_config.js b/test/runtime/samples/component-data-static-boolean-false/_config.js new file mode 100644 index 0000000000..47bcf31ec1 --- /dev/null +++ b/test/runtime/samples/component-data-static-boolean-false/_config.js @@ -0,0 +1,3 @@ +export default { + html: `

x: false (boolean)

` +}; diff --git a/test/runtime/samples/component-data-static-boolean-false/main.svelte b/test/runtime/samples/component-data-static-boolean-false/main.svelte new file mode 100644 index 0000000000..41a7c09553 --- /dev/null +++ b/test/runtime/samples/component-data-static-boolean-false/main.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file