From 54be0fbe440b54b3d459ade81bacfa4767662cc7 Mon Sep 17 00:00:00 2001 From: Vlad Glushchuk Date: Mon, 8 Apr 2019 20:28:16 +0200 Subject: [PATCH 01/22] Add bind:text and bind:html support for contenteditable elements Fixes #310 --- src/compile/nodes/Element.ts | 21 ++++++++- .../render-dom/wrappers/Element/Binding.ts | 16 +++++++ .../render-dom/wrappers/Element/index.ts | 6 +++ src/compile/render-ssr/handlers/Element.ts | 23 +++++++--- .../samples/contenteditable-html/_config.js | 43 +++++++++++++++++++ .../samples/contenteditable-html/main.svelte | 6 +++ .../samples/contenteditable-text/_config.js | 37 ++++++++++++++++ .../samples/contenteditable-text/main.svelte | 6 +++ .../contenteditable-dynamic/errors.json | 15 +++++++ .../contenteditable-dynamic/input.svelte | 6 +++ .../contenteditable-missing/errors.json | 15 +++++++ .../contenteditable-missing/input.svelte | 4 ++ 12 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 test/runtime/samples/contenteditable-html/_config.js create mode 100644 test/runtime/samples/contenteditable-html/main.svelte create mode 100644 test/runtime/samples/contenteditable-text/_config.js create mode 100644 test/runtime/samples/contenteditable-text/main.svelte create mode 100644 test/validator/samples/contenteditable-dynamic/errors.json create mode 100644 test/validator/samples/contenteditable-dynamic/input.svelte create mode 100644 test/validator/samples/contenteditable-missing/errors.json create mode 100644 test/validator/samples/contenteditable-missing/input.svelte diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index bc1991c57e..2838129e36 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -570,7 +570,26 @@ export default class Element extends Node { message: `'${binding.name}' is not a valid binding on void elements like <${this.name}>. Use a wrapper element instead` }); } - } else if (name !== 'this') { + } else if ( + name === 'text' || + name === 'html' + ){ + const contenteditable = this.attributes.find( + (attribute: Attribute) => attribute.name === 'contenteditable' + ); + + if (!contenteditable) { + component.error(binding, { + code: `missing-contenteditable-attribute`, + message: `'contenteditable' attribute is required for text and html two-way bindings` + }); + } else if (contenteditable && !contenteditable.is_static) { + component.error(contenteditable, { + code: `dynamic-contenteditable-attribute`, + message: `'contenteditable' attribute cannot be dynamic if element uses two-way binding` + }); + } + } else if (name !== 'this') { component.error(binding, { code: `invalid-binding`, message: `'${binding.name}' is not a valid binding` diff --git a/src/compile/render-dom/wrappers/Element/Binding.ts b/src/compile/render-dom/wrappers/Element/Binding.ts index 828d664001..d0e5375aef 100644 --- a/src/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compile/render-dom/wrappers/Element/Binding.ts @@ -201,6 +201,14 @@ function get_dom_updater( return `${element.var}.checked = ${condition};` } + if (binding.node.name === 'text') { + return `${element.var}.textContent = ${binding.snippet};`; + } + + if (binding.node.name === 'html') { + return `${element.var}.innerHTML = ${binding.snippet};`; + } + return `${element.var}.${binding.node.name} = ${binding.snippet};`; } @@ -313,6 +321,14 @@ function get_value_from_dom( return `@time_ranges_to_array(this.${name})` } + if (name === 'text') { + return `this.textContent`; + } + + if (name === 'html') { + return `this.innerHTML`; + } + // everything else return `this.${name}`; } diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 80a8308b93..10a56eaace 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -29,6 +29,12 @@ const events = [ node.name === 'textarea' || node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type')) }, + { + event_names: ['input'], + filter: (node: Element, name: string) => + (name === 'text' || name === 'html') && + node.attributes.some(attribute => attribute.name === 'contenteditable') + }, { event_names: ['change'], filter: (node: Element, name: string) => diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index 0c2cdc489e..f38bc0e151 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -48,7 +48,12 @@ const boolean_attributes = new Set([ export default function(node, renderer, options) { let opening_tag = `<${node.name}`; - let textarea_contents; // awkward special case + let node_contents; // awkward special case + const contenteditable = ( + node.name !== 'textarea' && + node.name !== 'input' && + node.attributes.some((attribute: Node) => attribute.name === 'contenteditable') + ); const slot = node.get_static_attribute_value('slot'); if (slot && node.has_ancestor('InlineComponent')) { @@ -77,7 +82,7 @@ export default function(node, renderer, options) { args.push(snip(attribute.expression)); } else { if (attribute.name === 'value' && node.name === 'textarea') { - textarea_contents = stringify_attribute(attribute, true); + node_contents = stringify_attribute(attribute, true); } else if (attribute.is_true) { args.push(`{ ${quote_name_if_necessary(attribute.name)}: true }`); } else if ( @@ -99,7 +104,7 @@ export default function(node, renderer, options) { if (attribute.type !== 'Attribute') return; if (attribute.name === 'value' && node.name === 'textarea') { - textarea_contents = stringify_attribute(attribute, true); + node_contents = stringify_attribute(attribute, true); } else if (attribute.is_true) { opening_tag += ` ${attribute.name}`; } else if ( @@ -128,6 +133,14 @@ export default function(node, renderer, options) { if (name === 'group') { // TODO server-render group bindings + } else if (contenteditable && (node === 'text' || node === 'html')) { + const snippet = snip(expression) + if (name == 'text') { + node_contents = '${@escape(' + snippet + ')}' + } else { + // Do not escape HTML content + node_contents = '${' + snippet + '}' + } } else { const snippet = snip(expression); opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}'; @@ -142,8 +155,8 @@ export default function(node, renderer, options) { renderer.append(opening_tag); - if (node.name === 'textarea' && textarea_contents !== undefined) { - renderer.append(textarea_contents); + if ((node.name === 'textarea' || contenteditable) && node_contents !== undefined) { + renderer.append(node_contents); } else { renderer.render(node.children, options); } diff --git a/test/runtime/samples/contenteditable-html/_config.js b/test/runtime/samples/contenteditable-html/_config.js new file mode 100644 index 0000000000..cd2a822655 --- /dev/null +++ b/test/runtime/samples/contenteditable-html/_config.js @@ -0,0 +1,43 @@ +export default { + props: { + name: 'world', + }, + + html: ` + world +

hello world

+ `, + + ssrHtml: ` + world +

hello world

+ `, + + async test({ assert, component, target, window }) { + const el = target.querySelector('editor'); + assert.equal(el.innerHTML, 'world'); + + el.innerHTML = 'everybody'; + + // No updates to data yet + assert.htmlEqual(target.innerHTML, ` + everybody +

hello world

+ `); + + // Handle user input + const event = new window.Event('input'); + await el.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` + everybody +

hello everybody

+ `); + + component.name = 'goodbye'; + assert.equal(el.innerHTML, 'goodbye'); + assert.htmlEqual(target.innerHTML, ` + goodbye +

hello goodbye

+ `); + }, +}; diff --git a/test/runtime/samples/contenteditable-html/main.svelte b/test/runtime/samples/contenteditable-html/main.svelte new file mode 100644 index 0000000000..53b4e81c88 --- /dev/null +++ b/test/runtime/samples/contenteditable-html/main.svelte @@ -0,0 +1,6 @@ + + + +

hello {@html name}

\ No newline at end of file diff --git a/test/runtime/samples/contenteditable-text/_config.js b/test/runtime/samples/contenteditable-text/_config.js new file mode 100644 index 0000000000..4935a3a9a7 --- /dev/null +++ b/test/runtime/samples/contenteditable-text/_config.js @@ -0,0 +1,37 @@ +export default { + props: { + name: 'world', + }, + + html: ` + world +

hello world

+ `, + + ssrHtml: ` + world +

hello world

+ `, + + async test({ assert, component, target, window }) { + const el = target.querySelector('editor'); + assert.equal(el.textContent, 'world'); + + const event = new window.Event('input'); + + el.textContent = 'everybody'; + await el.dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + everybody +

hello everybody

+ `); + + component.name = 'goodbye'; + assert.equal(el.textContent, 'goodbye'); + assert.htmlEqual(target.innerHTML, ` + goodbye +

hello goodbye

+ `); + }, +}; diff --git a/test/runtime/samples/contenteditable-text/main.svelte b/test/runtime/samples/contenteditable-text/main.svelte new file mode 100644 index 0000000000..a71d9f0c5b --- /dev/null +++ b/test/runtime/samples/contenteditable-text/main.svelte @@ -0,0 +1,6 @@ + + + +

hello {name}

\ No newline at end of file diff --git a/test/validator/samples/contenteditable-dynamic/errors.json b/test/validator/samples/contenteditable-dynamic/errors.json new file mode 100644 index 0000000000..0c4c5585a6 --- /dev/null +++ b/test/validator/samples/contenteditable-dynamic/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "dynamic-contenteditable-attribute", + "message": "'contenteditable' attribute cannot be dynamic if element uses two-way binding", + "start": { + "line": 6, + "column": 8, + "character": 73 + }, + "end": { + "line": 6, + "column": 32, + "character": 97 + }, + "pos": 73 +}] \ No newline at end of file diff --git a/test/validator/samples/contenteditable-dynamic/input.svelte b/test/validator/samples/contenteditable-dynamic/input.svelte new file mode 100644 index 0000000000..97d2c9228c --- /dev/null +++ b/test/validator/samples/contenteditable-dynamic/input.svelte @@ -0,0 +1,6 @@ + + diff --git a/test/validator/samples/contenteditable-missing/errors.json b/test/validator/samples/contenteditable-missing/errors.json new file mode 100644 index 0000000000..9cadb20629 --- /dev/null +++ b/test/validator/samples/contenteditable-missing/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "missing-contenteditable-attribute", + "message": "'contenteditable' attribute is required for text and html two-way bindings", + "start": { + "line": 4, + "column": 8, + "character": 48 + }, + "end": { + "line": 4, + "column": 24, + "character": 64 + }, + "pos": 48 +}] \ No newline at end of file diff --git a/test/validator/samples/contenteditable-missing/input.svelte b/test/validator/samples/contenteditable-missing/input.svelte new file mode 100644 index 0000000000..47f125894a --- /dev/null +++ b/test/validator/samples/contenteditable-missing/input.svelte @@ -0,0 +1,4 @@ + + From 25b6c07816b54bf36aa96507e452c4bdad00733e Mon Sep 17 00:00:00 2001 From: Vlad Glushchuk Date: Mon, 8 Apr 2019 20:56:52 +0200 Subject: [PATCH 02/22] Fix a typo --- src/compile/render-ssr/handlers/Element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index f38bc0e151..cefe44ede8 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -133,7 +133,7 @@ export default function(node, renderer, options) { if (name === 'group') { // TODO server-render group bindings - } else if (contenteditable && (node === 'text' || node === 'html')) { + } else if (contenteditable && (name === 'text' || name === 'html')) { const snippet = snip(expression) if (name == 'text') { node_contents = '${@escape(' + snippet + ')}' From e30d7b97d0e43c56c7b87012a8cbb9f589a2e3fa Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:31:20 -0400 Subject: [PATCH 03/22] initialise text/html bindings if necessary --- src/compiler/compile/nodes/Element.ts | 4 +- .../render-dom/wrappers/Element/Binding.ts | 28 +++++-------- .../render-dom/wrappers/Element/index.ts | 20 ++++++---- .../compile/render-ssr/handlers/Element.ts | 24 +++++++---- .../_config.js | 40 +++++++++++++++++++ .../main.svelte | 8 ++++ .../_config.js | 6 +-- .../main.svelte | 2 +- .../_config.js | 34 ++++++++++++++++ .../main.svelte | 8 ++++ .../_config.js | 0 .../main.svelte | 2 +- 12 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 test/runtime/samples/binding-contenteditable-html-initial/_config.js create mode 100644 test/runtime/samples/binding-contenteditable-html-initial/main.svelte rename test/runtime/samples/{contenteditable-html => binding-contenteditable-html}/_config.js (89%) rename test/runtime/samples/{contenteditable-html => binding-contenteditable-html}/main.svelte (62%) create mode 100644 test/runtime/samples/binding-contenteditable-text-initial/_config.js create mode 100644 test/runtime/samples/binding-contenteditable-text-initial/main.svelte rename test/runtime/samples/{contenteditable-text => binding-contenteditable-text}/_config.js (100%) rename test/runtime/samples/{contenteditable-text => binding-contenteditable-text}/main.svelte (66%) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index c37ea5020a..3e883200a7 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -610,7 +610,7 @@ export default class Element extends Node { } else if ( name === 'text' || name === 'html' - ){ + ) { const contenteditable = this.attributes.find( (attribute: Attribute) => attribute.name === 'contenteditable' ); @@ -626,7 +626,7 @@ export default class Element extends Node { message: `'contenteditable' attribute cannot be dynamic if element uses two-way binding` }); } - } else if (name !== 'this') { + } else if (name !== 'this') { component.error(binding, { code: `invalid-binding`, message: `'${binding.name}' is not a valid binding` diff --git a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts index 22f1f682e4..7de661e3d1 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts @@ -133,6 +133,14 @@ export default class BindingWrapper { break; } + case 'text': + update_conditions.push(`${this.snippet} !== ${parent.var}.textContent`); + break; + + case 'html': + update_conditions.push(`${this.snippet} !== ${parent.var}.innerHTML`); + break; + case 'currentTime': case 'playbackRate': case 'volume': @@ -162,7 +170,9 @@ export default class BindingWrapper { ); } - if (!/(currentTime|paused)/.test(this.node.name)) { + if (this.node.name === 'html' || this.node.name === 'text') { + block.builders.mount.add_block(`if (${this.snippet} !== void 0) ${update_dom}`); + } else if (!/(currentTime|paused)/.test(this.node.name)) { block.builders.mount.add_block(update_dom); } } @@ -198,14 +208,6 @@ function get_dom_updater( return `${element.var}.checked = ${condition};`; } - if (binding.node.name === 'text') { - return `if (${binding.snippet} !== ${element.var}.textContent) ${element.var}.textContent = ${binding.snippet};`; - } - - if (binding.node.name === 'html') { - return `if (${binding.snippet} !== ${element.var}.innerHTML) ${element.var}.innerHTML = ${binding.snippet};`; - } - if (binding.node.name === 'text') { return `${element.var}.textContent = ${binding.snippet};`; } @@ -334,14 +336,6 @@ function get_value_from_dom( return `this.innerHTML`; } - if (name === 'text') { - return `this.textContent`; - } - - if (name === 'html') { - return `this.innerHTML`; - } - // everything else return `this.${name}`; } diff --git a/src/compiler/compile/render-dom/wrappers/Element/index.ts b/src/compiler/compile/render-dom/wrappers/Element/index.ts index c63ebf1693..527a101f6a 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/index.ts @@ -33,12 +33,6 @@ const events = [ (name === 'text' || name === 'html') && node.attributes.some(attribute => attribute.name === 'contenteditable') }, - { - event_names: ['change'], - filter: (node: Element, name: string) => - (name === 'text' || name === 'html') && - node.attributes.some(attribute => attribute.name === 'contenteditable') - }, { event_names: ['change'], filter: (node: Element, _name: string) => @@ -514,7 +508,19 @@ export default class ElementWrapper extends Wrapper { .map(binding => `${binding.snippet} === void 0`) .join(' || '); - if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.is_readonly_media_attribute())) { + const should_initialise = ( + this.node.name === 'select' || + group.bindings.find(binding => { + return ( + binding.node.name === 'indeterminate' || + binding.node.name === 'text' || + binding.node.name === 'html' || + binding.is_readonly_media_attribute() + ); + }) + ); + + if (should_initialise) { const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`; block.builders.hydrate.add_line( `if (${some_initial_state_is_undefined}) @add_render_callback(${callback});` diff --git a/src/compiler/compile/render-ssr/handlers/Element.ts b/src/compiler/compile/render-ssr/handlers/Element.ts index 681e0d4c7b..fb9b935f9a 100644 --- a/src/compiler/compile/render-ssr/handlers/Element.ts +++ b/src/compiler/compile/render-ssr/handlers/Element.ts @@ -53,7 +53,11 @@ export default function(node: Element, renderer: Renderer, options: RenderOption slot_scopes: Map; }) { let opening_tag = `<${node.name}`; - let node_contents; // awkward special case + + // awkward special case + let node_contents; + let value; + const contenteditable = ( node.name !== 'textarea' && node.name !== 'input' && @@ -151,16 +155,16 @@ export default function(node: Element, renderer: Renderer, options: RenderOption if (name === 'group') { // TODO server-render group bindings } else if (contenteditable && (name === 'text' || name === 'html')) { - const snippet = snip(expression) + node_contents = snip(expression); if (name == 'text') { - node_contents = '${@escape(' + snippet + ')}' + value = '@escape($$value)'; } else { // Do not escape HTML content - node_contents = '${' + snippet + '}' + value = '$$value'; } } else if (binding.name === 'value' && node.name === 'textarea') { const snippet = snip(expression); - node_contents='${(' + snippet + ') || ""}'; + node_contents = '${(' + snippet + ') || ""}'; } else { const snippet = snip(expression); opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}'; @@ -175,8 +179,14 @@ export default function(node: Element, renderer: Renderer, options: RenderOption renderer.append(opening_tag); - if ((node.name === 'textarea' || contenteditable) && node_contents !== undefined) { - renderer.append(node_contents); + if (node_contents !== undefined) { + if (contenteditable) { + renderer.append('${($$value => $$value === void 0 ? `'); + renderer.render(node.children, options); + renderer.append('` : ' + value + ')(' + node_contents + ')}'); + } else { + renderer.append(node_contents); + } } else { renderer.render(node.children, options); } diff --git a/test/runtime/samples/binding-contenteditable-html-initial/_config.js b/test/runtime/samples/binding-contenteditable-html-initial/_config.js new file mode 100644 index 0000000000..0b1f656a54 --- /dev/null +++ b/test/runtime/samples/binding-contenteditable-html-initial/_config.js @@ -0,0 +1,40 @@ +export default { + html: ` + world +

hello world

+ `, + + ssrHtml: ` + world +

hello undefined

+ `, + + async test({ assert, component, target, window }) { + assert.equal(component.name, 'world'); + + const el = target.querySelector('editor'); + + el.innerHTML = 'everybody'; + + // No updates to data yet + assert.htmlEqual(target.innerHTML, ` + everybody +

hello world

+ `); + + // Handle user input + const event = new window.Event('input'); + await el.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` + everybody +

hello everybody

+ `); + + component.name = 'goodbye'; + assert.equal(el.innerHTML, 'goodbye'); + assert.htmlEqual(target.innerHTML, ` + goodbye +

hello goodbye

+ `); + }, +}; diff --git a/test/runtime/samples/binding-contenteditable-html-initial/main.svelte b/test/runtime/samples/binding-contenteditable-html-initial/main.svelte new file mode 100644 index 0000000000..1c05a0950d --- /dev/null +++ b/test/runtime/samples/binding-contenteditable-html-initial/main.svelte @@ -0,0 +1,8 @@ + + + + world + +

hello {@html name}

\ No newline at end of file diff --git a/test/runtime/samples/contenteditable-html/_config.js b/test/runtime/samples/binding-contenteditable-html/_config.js similarity index 89% rename from test/runtime/samples/contenteditable-html/_config.js rename to test/runtime/samples/binding-contenteditable-html/_config.js index cd2a822655..013fa30f39 100644 --- a/test/runtime/samples/contenteditable-html/_config.js +++ b/test/runtime/samples/binding-contenteditable-html/_config.js @@ -19,14 +19,14 @@ export default { el.innerHTML = 'everybody'; - // No updates to data yet + // No updates to data yet assert.htmlEqual(target.innerHTML, ` everybody

hello world

`); - // Handle user input - const event = new window.Event('input'); + // Handle user input + const event = new window.Event('input'); await el.dispatchEvent(event); assert.htmlEqual(target.innerHTML, ` everybody diff --git a/test/runtime/samples/contenteditable-html/main.svelte b/test/runtime/samples/binding-contenteditable-html/main.svelte similarity index 62% rename from test/runtime/samples/contenteditable-html/main.svelte rename to test/runtime/samples/binding-contenteditable-html/main.svelte index 53b4e81c88..09195ed558 100644 --- a/test/runtime/samples/contenteditable-html/main.svelte +++ b/test/runtime/samples/binding-contenteditable-html/main.svelte @@ -1,5 +1,5 @@ diff --git a/test/runtime/samples/binding-contenteditable-text-initial/_config.js b/test/runtime/samples/binding-contenteditable-text-initial/_config.js new file mode 100644 index 0000000000..7345687d29 --- /dev/null +++ b/test/runtime/samples/binding-contenteditable-text-initial/_config.js @@ -0,0 +1,34 @@ +export default { + html: ` + world +

hello world

+ `, + + ssrHtml: ` + world +

hello undefined

+ `, + + async test({ assert, component, target, window }) { + assert.equal(component.name, 'world'); + + const el = target.querySelector('editor'); + + const event = new window.Event('input'); + + el.textContent = 'everybody'; + await el.dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + everybody +

hello everybody

+ `); + + component.name = 'goodbye'; + assert.equal(el.textContent, 'goodbye'); + assert.htmlEqual(target.innerHTML, ` + goodbye +

hello goodbye

+ `); + }, +}; diff --git a/test/runtime/samples/binding-contenteditable-text-initial/main.svelte b/test/runtime/samples/binding-contenteditable-text-initial/main.svelte new file mode 100644 index 0000000000..633d268f43 --- /dev/null +++ b/test/runtime/samples/binding-contenteditable-text-initial/main.svelte @@ -0,0 +1,8 @@ + + + + world + +

hello {name}

\ No newline at end of file diff --git a/test/runtime/samples/contenteditable-text/_config.js b/test/runtime/samples/binding-contenteditable-text/_config.js similarity index 100% rename from test/runtime/samples/contenteditable-text/_config.js rename to test/runtime/samples/binding-contenteditable-text/_config.js diff --git a/test/runtime/samples/contenteditable-text/main.svelte b/test/runtime/samples/binding-contenteditable-text/main.svelte similarity index 66% rename from test/runtime/samples/contenteditable-text/main.svelte rename to test/runtime/samples/binding-contenteditable-text/main.svelte index a71d9f0c5b..c47f5ee477 100644 --- a/test/runtime/samples/contenteditable-text/main.svelte +++ b/test/runtime/samples/binding-contenteditable-text/main.svelte @@ -1,5 +1,5 @@ From ceca640822aef19e0ae9692bc1440018086f3a08 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:35:09 -0400 Subject: [PATCH 04/22] tidy up, prevent collisions --- src/compiler/compile/render-ssr/handlers/Element.ts | 11 +++-------- src/runtime/internal/ssr.ts | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/compiler/compile/render-ssr/handlers/Element.ts b/src/compiler/compile/render-ssr/handlers/Element.ts index fb9b935f9a..4029a7f579 100644 --- a/src/compiler/compile/render-ssr/handlers/Element.ts +++ b/src/compiler/compile/render-ssr/handlers/Element.ts @@ -156,23 +156,18 @@ export default function(node: Element, renderer: Renderer, options: RenderOption // TODO server-render group bindings } else if (contenteditable && (name === 'text' || name === 'html')) { node_contents = snip(expression); - if (name == 'text') { - value = '@escape($$value)'; - } else { - // Do not escape HTML content - value = '$$value'; - } + value = name === 'text' ? '@escape($$value)' : '$$value'; } else if (binding.name === 'value' && node.name === 'textarea') { const snippet = snip(expression); node_contents = '${(' + snippet + ') || ""}'; } else { const snippet = snip(expression); - opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}'; + opening_tag += ' ${($$value => $$value ? ("' + name + '" + ($$value === true ? "" : "=" + JSON.stringify($$value))) : "")(' + snippet + ')}'; } }); if (add_class_attribute) { - opening_tag += `\${((v) => v ? ' class="' + v + '"' : '')([${class_expression}].join(' ').trim())}`; + opening_tag += `\${(($$value) => $$value ? ' class="' + $$value + '"' : '')([${class_expression}].join(' ').trim())}`; } opening_tag += '>'; diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index c80b4d9f45..f88f704779 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -118,4 +118,4 @@ export function get_store_value(store: Readable): T | undefined { let value; store.subscribe(_ => value = _)(); return value; -} +} \ No newline at end of file From b502022d67749c9a83ecd6d7f1d53e8850c94305 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:50:04 -0400 Subject: [PATCH 05/22] use helpers --- src/compiler/compile/render-ssr/handlers/Element.ts | 4 ++-- src/runtime/internal/ssr.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/render-ssr/handlers/Element.ts b/src/compiler/compile/render-ssr/handlers/Element.ts index 4029a7f579..0c2181a605 100644 --- a/src/compiler/compile/render-ssr/handlers/Element.ts +++ b/src/compiler/compile/render-ssr/handlers/Element.ts @@ -162,12 +162,12 @@ export default function(node: Element, renderer: Renderer, options: RenderOption node_contents = '${(' + snippet + ') || ""}'; } else { const snippet = snip(expression); - opening_tag += ' ${($$value => $$value ? ("' + name + '" + ($$value === true ? "" : "=" + JSON.stringify($$value))) : "")(' + snippet + ')}'; + opening_tag += '${@add_attribute("' + name + '", ' + snippet + ')}'; } }); if (add_class_attribute) { - opening_tag += `\${(($$value) => $$value ? ' class="' + $$value + '"' : '')([${class_expression}].join(' ').trim())}`; + opening_tag += `\${@add_classes([${class_expression}].join(' ').trim())}`; } opening_tag += '>'; diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index f88f704779..91d6e452fe 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -118,4 +118,13 @@ export function get_store_value(store: Readable): T | undefined { let value; store.subscribe(_ => value = _)(); return value; +} + +export function add_attribute(name, value) { + if (!value) return ''; + return ` ${name}${value === true ? '' : `=${JSON.stringify(value)}`}`; +} + +export function add_classes(classes) { + return classes ? ` class="${classes}"` : ``; } \ No newline at end of file From 42d5231f7748dff6b7e5027f7c2f8e1833eb0dcf Mon Sep 17 00:00:00 2001 From: benib Date: Sat, 22 Jun 2019 17:21:38 +0200 Subject: [PATCH 06/22] =?UTF-8?q?Add=20Neue=20Z=C3=BCrcher=20Zeitung=20log?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/routes/_components/WhosUsingSvelte.svelte | 1 + site/static/organisations/nzz.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 site/static/organisations/nzz.svg diff --git a/site/src/routes/_components/WhosUsingSvelte.svelte b/site/src/routes/_components/WhosUsingSvelte.svelte index 98a2696f64..918c07b1bf 100644 --- a/site/src/routes/_components/WhosUsingSvelte.svelte +++ b/site/src/routes/_components/WhosUsingSvelte.svelte @@ -52,6 +52,7 @@ itslearning logo Mustlab logo Nesta logo + Neue Zürcher Zeitung logo The New York Times logo Open State Foundation logo Razorpay logo diff --git a/site/static/organisations/nzz.svg b/site/static/organisations/nzz.svg new file mode 100644 index 0000000000..70f9486263 --- /dev/null +++ b/site/static/organisations/nzz.svg @@ -0,0 +1 @@ + \ No newline at end of file From 31d357b7cc16f22cd86032d9feca943d49dda7d2 Mon Sep 17 00:00:00 2001 From: Luca Bonavita Date: Sat, 22 Jun 2019 17:42:00 +0100 Subject: [PATCH 07/22] Docs: document rest in array/object destructuring in each blocks (#2676) --- site/content/docs/02-template-syntax.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index a6e17f8bc2..aabcf1f2f3 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -188,12 +188,20 @@ If a *key* expression is provided — which must uniquely identify each list ite --- -You can freely use destructuring patterns in each blocks. +You can freely use destructuring and rest patterns in each blocks. ```html {#each items as { id, name, qty }, i (id)}
  • {i + 1}: {name} x {qty}
  • {/each} + +{#each objects as { id, ...rest }} +
  • {id}
  • +{/each} + +{#each items as [id, ...rest]} +
  • {id}
  • +{/each} ``` --- From fdadb708a11544fab57d7fc8e7b247cb90bda83f Mon Sep 17 00:00:00 2001 From: Luca Bonavita Date: Thu, 20 Jun 2019 16:28:03 +0100 Subject: [PATCH 08/22] Use consistent tag closing for elements Fixes #3061 --- site/content/docs/02-template-syntax.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index aabcf1f2f3..682012d88b 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1249,7 +1249,7 @@ It cannot appear at the top level of your markup; it must be inside an if or eac ### `` ```sv - + ``` --- @@ -1326,7 +1326,7 @@ As with ``, this element allows you to add listeners to events on ### `` ```sv - +... ``` --- @@ -1343,7 +1343,7 @@ This element makes it possible to insert elements into `document.head`. During s ### `` ```sv - + ``` --- @@ -1359,4 +1359,4 @@ The `` element provides a place to specify per-component compile ```html -``` \ No newline at end of file +``` From dc3cb06f919b7bda17dd24a30fd8d6c2a2a97457 Mon Sep 17 00:00:00 2001 From: Bryan Terce Date: Sun, 23 Jun 2019 05:26:00 -0700 Subject: [PATCH 09/22] Fix dynamic `bind:this` on components (#2333) --- .../render-dom/wrappers/InlineComponent/index.ts | 6 ++++-- src/compiler/compile/utils/flatten_reference.ts | 14 ++++++++------ .../binding-this-component-computed-key/Foo.svelte | 1 + .../binding-this-component-computed-key/_config.js | 8 ++++++++ .../main.svelte | 9 +++++++++ .../Foo.svelte | 1 + .../_config.js | 12 ++++++++++++ .../main.svelte | 11 +++++++++++ .../binding-this-component-each-block/Foo.svelte | 1 + .../binding-this-component-each-block/_config.js | 12 ++++++++++++ .../binding-this-component-each-block/main.svelte | 11 +++++++++++ 11 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 test/runtime/samples/binding-this-component-computed-key/Foo.svelte create mode 100644 test/runtime/samples/binding-this-component-computed-key/_config.js create mode 100644 test/runtime/samples/binding-this-component-computed-key/main.svelte create mode 100644 test/runtime/samples/binding-this-component-each-block-value/Foo.svelte create mode 100644 test/runtime/samples/binding-this-component-each-block-value/_config.js create mode 100644 test/runtime/samples/binding-this-component-each-block-value/main.svelte create mode 100644 test/runtime/samples/binding-this-component-each-block/Foo.svelte create mode 100644 test/runtime/samples/binding-this-component-each-block/_config.js create mode 100644 test/runtime/samples/binding-this-component-each-block/main.svelte diff --git a/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts index 8d5c751add..46b3df4c31 100644 --- a/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts @@ -276,15 +276,17 @@ export default class InlineComponentWrapper extends Wrapper { lhs = component.source.slice(binding.expression.node.start, binding.expression.node.end).trim(); } + const contextual_dependencies = [...binding.expression.contextual_dependencies]; + component.partly_hoisted.push(deindent` - function ${fn}($$component) { + function ${fn}(${['$$component', ...contextual_dependencies].join(', ')}) { ${lhs} = $$component; ${object && component.invalidate(object)} } `); block.builders.destroy.add_line(`ctx.${fn}(null);`); - return `@add_binding_callback(() => ctx.${fn}(${this.var}));`; + return `@add_binding_callback(() => ctx.${fn}(${[this.var, ...contextual_dependencies.map(name => `ctx.${name}`)].join(', ')}));`; } const name = component.get_unique_name(`${this.var}_${binding.name}_binding`); diff --git a/src/compiler/compile/utils/flatten_reference.ts b/src/compiler/compile/utils/flatten_reference.ts index e7d66d01ca..460cd2f1e9 100644 --- a/src/compiler/compile/utils/flatten_reference.ts +++ b/src/compiler/compile/utils/flatten_reference.ts @@ -7,10 +7,11 @@ export default function flatten_reference(node: Node) { const prop_end = node.end; while (node.type === 'MemberExpression') { - if (node.computed) return null; - nodes.unshift(node.property); - parts.unshift(node.property.name); + + if (!node.computed) { + parts.unshift(node.property.name); + } node = node.object; } @@ -20,10 +21,11 @@ export default function flatten_reference(node: Node) { ? node.name : node.type === 'ThisExpression' ? 'this' : null; - if (!name) return null; - - parts.unshift(name); nodes.unshift(node); + if (!node.computed) { + parts.unshift(name); + } + return { name, nodes, parts, keypath: `${name}[✂${prop_start}-${prop_end}✂]` }; } diff --git a/test/runtime/samples/binding-this-component-computed-key/Foo.svelte b/test/runtime/samples/binding-this-component-computed-key/Foo.svelte new file mode 100644 index 0000000000..066a48b65e --- /dev/null +++ b/test/runtime/samples/binding-this-component-computed-key/Foo.svelte @@ -0,0 +1 @@ +
    foo
    \ No newline at end of file diff --git a/test/runtime/samples/binding-this-component-computed-key/_config.js b/test/runtime/samples/binding-this-component-computed-key/_config.js new file mode 100644 index 0000000000..415d2e641c --- /dev/null +++ b/test/runtime/samples/binding-this-component-computed-key/_config.js @@ -0,0 +1,8 @@ +export default { + skip_if_ssr: true, + + html: ` +
    foo
    +
    has foo: true
    + ` +}; diff --git a/test/runtime/samples/binding-this-component-computed-key/main.svelte b/test/runtime/samples/binding-this-component-computed-key/main.svelte new file mode 100644 index 0000000000..af450ea1fe --- /dev/null +++ b/test/runtime/samples/binding-this-component-computed-key/main.svelte @@ -0,0 +1,9 @@ + + + +
    + has foo: {!!foo.computed} +
    diff --git a/test/runtime/samples/binding-this-component-each-block-value/Foo.svelte b/test/runtime/samples/binding-this-component-each-block-value/Foo.svelte new file mode 100644 index 0000000000..066a48b65e --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block-value/Foo.svelte @@ -0,0 +1 @@ +
    foo
    \ No newline at end of file diff --git a/test/runtime/samples/binding-this-component-each-block-value/_config.js b/test/runtime/samples/binding-this-component-each-block-value/_config.js new file mode 100644 index 0000000000..2b5df8c595 --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block-value/_config.js @@ -0,0 +1,12 @@ +export default { + skip_if_ssr: true, + + html: ` +
    foo
    +
    first has foo: true
    +
    foo
    +
    second has foo: true
    +
    foo
    +
    third has foo: true
    + ` +}; diff --git a/test/runtime/samples/binding-this-component-each-block-value/main.svelte b/test/runtime/samples/binding-this-component-each-block-value/main.svelte new file mode 100644 index 0000000000..5093eecd3f --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block-value/main.svelte @@ -0,0 +1,11 @@ + + +{#each ["first", "second", "third"] as value} + +
    + {value} has foo: {!!foo[value]} +
    +{/each} diff --git a/test/runtime/samples/binding-this-component-each-block/Foo.svelte b/test/runtime/samples/binding-this-component-each-block/Foo.svelte new file mode 100644 index 0000000000..066a48b65e --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block/Foo.svelte @@ -0,0 +1 @@ +
    foo
    \ No newline at end of file diff --git a/test/runtime/samples/binding-this-component-each-block/_config.js b/test/runtime/samples/binding-this-component-each-block/_config.js new file mode 100644 index 0000000000..358c5b5afc --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block/_config.js @@ -0,0 +1,12 @@ +export default { + skip_if_ssr: true, + + html: ` +
    foo
    +
    0 has foo: true
    +
    foo
    +
    1 has foo: true
    +
    foo
    +
    2 has foo: true
    + ` +}; diff --git a/test/runtime/samples/binding-this-component-each-block/main.svelte b/test/runtime/samples/binding-this-component-each-block/main.svelte new file mode 100644 index 0000000000..49d1d76172 --- /dev/null +++ b/test/runtime/samples/binding-this-component-each-block/main.svelte @@ -0,0 +1,11 @@ + + +{#each Array(3) as _, i} + +
    + {i} has foo: {!!foo[i]} +
    +{/each} From 34ac1ff42e8d6e044ea75948390ee033c2068e49 Mon Sep 17 00:00:00 2001 From: Bryan Terce Date: Sun, 23 Jun 2019 05:34:36 -0700 Subject: [PATCH 10/22] Fix binding to values in a component when it uses `$$props` (#2725) --- src/compiler/compile/render-dom/index.ts | 2 +- .../samples/binding-using-props/TextInput.svelte | 6 ++++++ .../runtime/samples/binding-using-props/_config.js | 14 ++++++++++++++ .../samples/binding-using-props/main.svelte | 7 +++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/binding-using-props/TextInput.svelte create mode 100644 test/runtime/samples/binding-using-props/_config.js create mode 100644 test/runtime/samples/binding-using-props/main.svelte diff --git a/src/compiler/compile/render-dom/index.ts b/src/compiler/compile/render-dom/index.ts index 46aa705bfb..9a49bc0c35 100644 --- a/src/compiler/compile/render-dom/index.ts +++ b/src/compiler/compile/render-dom/index.ts @@ -80,7 +80,7 @@ export default function dom( ${$$props} => { ${uses_props && component.invalidate('$$props', `$$props = @assign(@assign({}, $$props), $$new_props)`)} ${writable_props.map(prop => - `if ('${prop.export_name}' in $$props) ${component.invalidate(prop.name, `${prop.name} = $$props.${prop.export_name}`)};` + `if ('${prop.export_name}' in ${$$props}) ${component.invalidate(prop.name, `${prop.name} = ${$$props}.${prop.export_name}`)};` )} ${component.slots.size > 0 && `if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', `$$scope = ${$$props}.$$scope`)};`} diff --git a/test/runtime/samples/binding-using-props/TextInput.svelte b/test/runtime/samples/binding-using-props/TextInput.svelte new file mode 100644 index 0000000000..da9e7e4a0e --- /dev/null +++ b/test/runtime/samples/binding-using-props/TextInput.svelte @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/binding-using-props/_config.js b/test/runtime/samples/binding-using-props/_config.js new file mode 100644 index 0000000000..dcb34c4357 --- /dev/null +++ b/test/runtime/samples/binding-using-props/_config.js @@ -0,0 +1,14 @@ +export default { + async test({ assert, target, window }) { + const input = target.querySelector('input'); + + const event = new window.Event('input'); + input.value = 'changed'; + await input.dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + +

    changed

    + `); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/binding-using-props/main.svelte b/test/runtime/samples/binding-using-props/main.svelte new file mode 100644 index 0000000000..543bd28d49 --- /dev/null +++ b/test/runtime/samples/binding-using-props/main.svelte @@ -0,0 +1,7 @@ + + + +

    {actualValue}

    \ No newline at end of file From d6b5c42f7f31a70a4381cb29759f58f46f476420 Mon Sep 17 00:00:00 2001 From: Thomas Ghysels Date: Sun, 23 Jun 2019 14:41:56 +0200 Subject: [PATCH 11/22] Fix parsing ambiguous HTML entities (#3071) Fixes sveltejs/sapper#759 --- src/compiler/parse/utils/html.ts | 2 +- test/parser/samples/attribute-escaped/input.svelte | 2 +- test/parser/samples/attribute-escaped/output.json | 12 ++++++------ test/runtime/samples/html-entities/_config.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/compiler/parse/utils/html.ts b/src/compiler/parse/utils/html.ts index b49989eacd..13ff553a3f 100644 --- a/src/compiler/parse/utils/html.ts +++ b/src/compiler/parse/utils/html.ts @@ -36,7 +36,7 @@ const windows_1252 = [ ]; const entity_pattern = new RegExp( - `&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}));?`, + `&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(entities).join('|')}))(?:;|\\b)`, 'g' ); diff --git a/test/parser/samples/attribute-escaped/input.svelte b/test/parser/samples/attribute-escaped/input.svelte index 82186dcee4..4c9cd5ff68 100644 --- a/test/parser/samples/attribute-escaped/input.svelte +++ b/test/parser/samples/attribute-escaped/input.svelte @@ -1 +1 @@ -
    +
    diff --git a/test/parser/samples/attribute-escaped/output.json b/test/parser/samples/attribute-escaped/output.json index e60a8c2531..a8498bd574 100644 --- a/test/parser/samples/attribute-escaped/output.json +++ b/test/parser/samples/attribute-escaped/output.json @@ -1,27 +1,27 @@ { "html": { "start": 0, - "end": 41, + "end": 83, "type": "Fragment", "children": [ { "start": 0, - "end": 41, + "end": 83, "type": "Element", "name": "div", "attributes": [ { "start": 5, - "end": 34, + "end": 76, "type": "Attribute", "name": "data-foo", "value": [ { "start": 15, - "end": 33, + "end": 75, "type": "Text", - "raw": ""quoted"", - "data": "\"quoted\"" + "raw": "semi:"space:" letter:"e number:"1 end:"", + "data": "semi:\"space:\" letter:"e number:"1 end:\"" } ] } diff --git a/test/runtime/samples/html-entities/_config.js b/test/runtime/samples/html-entities/_config.js index 1591f31a53..c8c2de8403 100644 --- a/test/runtime/samples/html-entities/_config.js +++ b/test/runtime/samples/html-entities/_config.js @@ -9,6 +9,6 @@ export default { A - ¬anentity; + &notanentity; ` }; \ No newline at end of file From 3fe8736bea68610055abe2d09837ebb9e7b45a44 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 23 Jun 2019 08:48:04 -0400 Subject: [PATCH 12/22] -> v3.5.4 --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94d7326eea..2849115008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Svelte changelog +## 3.5.4 + +* Preserve whitespace at the boundaries of `{#each}` blocks ([#713](https://github.com/sveltejs/svelte/issues/713)) +* Fix dynamic `bind:this` on components ([#2333](https://github.com/sveltejs/svelte/issues/2333)) +* Fix binding to values in a component when it uses `$$props` ([#2725](https://github.com/sveltejs/svelte/issues/2725)) +* Fix parsing ambiguous HTML entities ([#3071](https://github.com/sveltejs/svelte/pull/3071)) + ## 3.5.3 * Don't double-destroy keyed each blocks with outros ([#3055](https://github.com/sveltejs/svelte/issues/3055)) diff --git a/package-lock.json b/package-lock.json index 8a28bf9997..0aef0fa227 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.5.3", + "version": "3.5.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9047af529e..96a1dd8f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.5.3", + "version": "3.5.4", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 5a57ce4f949a32d846d8ff4ea1834f8bdb959b87 Mon Sep 17 00:00:00 2001 From: Jan C Date: Sun, 23 Jun 2019 19:48:21 +0200 Subject: [PATCH 13/22] Update site README for local REPL (#3078) * Update site README for local REPL * Change the local install instructions to be able to use the local repl out of the box This avoids potential confusion in setting the website up for local use. * Add sections for Testing and Building * Add a note that the local REPL will only work for the development page * Update README.md --- site/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/site/README.md b/site/README.md index 59380feffc..259e956003 100644 --- a/site/README.md +++ b/site/README.md @@ -4,7 +4,10 @@ Set up the project: ```bash git clone https://github.com/sveltejs/svelte.git -cd svelte/site +cd svelte +npm ci +PUBLISH=1 npm run build +cd site npm ci npm run update ``` @@ -17,7 +20,7 @@ By default, the REPL will fetch the most recent version of Svelte from https://u To produce the proper browser-compatible UMD build of the compiler, you will need to run `npm run build` (or `npm run dev`) in the root of this repository with the `PUBLISH` environment variable set to any non-empty string. -Then visit the REPL at [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). +Then visit the REPL at [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). Please note that the local REPL only works with `npm run dev` and not when building the site for production usage. ## REPL GitHub integration @@ -32,6 +35,13 @@ In order for the REPL's GitHub integration to work properly when running locally GITHUB_CLIENT_SECRET=[your app's Client Secret] BASEURL=http://localhost:3000 ``` +## Building the site + +To build the website, run `npm run sapper`. The output can be found in `__sapper__/build`. + +## Testing + +Tests can be run using `npm run test`. ## Translating the API docs From 5ddb60705cd37f1d68ffec60e9cfdc46264a7108 Mon Sep 17 00:00:00 2001 From: Lwazi Dlamini Date: Sun, 23 Jun 2019 19:59:24 +0200 Subject: [PATCH 14/22] Added Nonkosi Telecoms Logo --- site/static/organisations/nonkosi.svg | 428 ++++++++++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 site/static/organisations/nonkosi.svg diff --git a/site/static/organisations/nonkosi.svg b/site/static/organisations/nonkosi.svg new file mode 100644 index 0000000000..c9e32c133c --- /dev/null +++ b/site/static/organisations/nonkosi.svg @@ -0,0 +1,428 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + + + + + + + From 69a0328c98a964c0f9e6c0d4100200a5ecbd4c60 Mon Sep 17 00:00:00 2001 From: Lwazi Dlamini Date: Sun, 23 Jun 2019 20:01:21 +0200 Subject: [PATCH 15/22] Updated organizations link list --- site/src/routes/_components/WhosUsingSvelte.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/routes/_components/WhosUsingSvelte.svelte b/site/src/routes/_components/WhosUsingSvelte.svelte index 98a2696f64..a855639c40 100644 --- a/site/src/routes/_components/WhosUsingSvelte.svelte +++ b/site/src/routes/_components/WhosUsingSvelte.svelte @@ -52,6 +52,7 @@ itslearning logo Mustlab logo Nesta logo + Nonkosi Telecoms logo The New York Times logo Open State Foundation logo Razorpay logo From 3bcb56b804217db5462fbd5e82406c8548724fae Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Jun 2019 11:40:41 -0400 Subject: [PATCH 16/22] update tests --- .../binding-contenteditable-html-initial/_config.js | 8 ++++---- .../samples/binding-contenteditable-html/_config.js | 5 ----- .../binding-contenteditable-text-initial/_config.js | 6 +++--- .../samples/binding-contenteditable-text/_config.js | 5 ----- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/test/runtime/samples/binding-contenteditable-html-initial/_config.js b/test/runtime/samples/binding-contenteditable-html-initial/_config.js index 0b1f656a54..9eac2c9b17 100644 --- a/test/runtime/samples/binding-contenteditable-html-initial/_config.js +++ b/test/runtime/samples/binding-contenteditable-html-initial/_config.js @@ -1,6 +1,6 @@ export default { html: ` - world + world

    hello world

    `, @@ -18,7 +18,7 @@ export default { // No updates to data yet assert.htmlEqual(target.innerHTML, ` - everybody + everybody

    hello world

    `); @@ -26,14 +26,14 @@ export default { const event = new window.Event('input'); await el.dispatchEvent(event); assert.htmlEqual(target.innerHTML, ` - everybody + everybody

    hello everybody

    `); component.name = 'goodbye'; assert.equal(el.innerHTML, 'goodbye'); assert.htmlEqual(target.innerHTML, ` - goodbye + goodbye

    hello goodbye

    `); }, diff --git a/test/runtime/samples/binding-contenteditable-html/_config.js b/test/runtime/samples/binding-contenteditable-html/_config.js index 285512b6c9..ceb6a75c70 100644 --- a/test/runtime/samples/binding-contenteditable-html/_config.js +++ b/test/runtime/samples/binding-contenteditable-html/_config.js @@ -8,11 +8,6 @@ export default {

    hello world

    `, - ssrHtml: ` - world -

    hello world

    - `, - async test({ assert, component, target, window }) { const el = target.querySelector('editor'); assert.equal(el.innerHTML, 'world'); diff --git a/test/runtime/samples/binding-contenteditable-text-initial/_config.js b/test/runtime/samples/binding-contenteditable-text-initial/_config.js index 7345687d29..4899f30f12 100644 --- a/test/runtime/samples/binding-contenteditable-text-initial/_config.js +++ b/test/runtime/samples/binding-contenteditable-text-initial/_config.js @@ -1,6 +1,6 @@ export default { html: ` - world + world

    hello world

    `, @@ -20,14 +20,14 @@ export default { await el.dispatchEvent(event); assert.htmlEqual(target.innerHTML, ` - everybody + everybody

    hello everybody

    `); component.name = 'goodbye'; assert.equal(el.textContent, 'goodbye'); assert.htmlEqual(target.innerHTML, ` - goodbye + goodbye

    hello goodbye

    `); }, diff --git a/test/runtime/samples/binding-contenteditable-text/_config.js b/test/runtime/samples/binding-contenteditable-text/_config.js index 059cda7cfe..9f8645724d 100644 --- a/test/runtime/samples/binding-contenteditable-text/_config.js +++ b/test/runtime/samples/binding-contenteditable-text/_config.js @@ -8,11 +8,6 @@ export default {

    hello world

    `, - ssrHtml: ` - world -

    hello world

    - `, - async test({ assert, component, target, window }) { const el = target.querySelector('editor'); assert.equal(el.textContent, 'world'); From 204766ec0aa2b7b8a90371132a7cbf3d5eaef9f6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Jun 2019 13:00:16 -0400 Subject: [PATCH 17/22] add some docs, rename to textContent and innerHTML --- site/content/docs/02-template-syntax.md | 8 +++++++ src/compiler/compile/nodes/Element.ts | 6 ++--- .../render-dom/wrappers/Element/Binding.ts | 22 +++---------------- .../render-dom/wrappers/Element/index.ts | 6 ++--- .../compile/render-ssr/handlers/Element.ts | 4 ++-- .../main.svelte | 2 +- .../binding-contenteditable-html/main.svelte | 2 +- .../main.svelte | 2 +- .../binding-contenteditable-text/main.svelte | 2 +- .../contenteditable-dynamic/input.svelte | 2 +- .../contenteditable-missing/errors.json | 2 +- .../contenteditable-missing/input.svelte | 2 +- 12 files changed, 26 insertions(+), 34 deletions(-) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index 682012d88b..ceaa19a398 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -510,6 +510,14 @@ When the value of an `