From cf7c7d15eda52e0c6da08e8dd7b10805e6679910 Mon Sep 17 00:00:00 2001 From: Vlad Glushchuk Date: Mon, 8 Apr 2019 20:28:16 +0200 Subject: [PATCH 01/24] 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 8deee95f141f32cd027467eb7c1d2bb8ff597007 Mon Sep 17 00:00:00 2001 From: Vlad Glushchuk Date: Mon, 8 Apr 2019 20:56:52 +0200 Subject: [PATCH 02/24] 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 e3de705cb821de586e5f78fabc4b09692e846276 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:31:20 -0400 Subject: [PATCH 03/24] 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 4f626c45a9f520414d395543b2104b645f3688d0 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:35:09 -0400 Subject: [PATCH 04/24] 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 b589289b557e441f337647d00fcd239367804ed7 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 23:50:04 -0400 Subject: [PATCH 05/24] 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 2ac599357144f015e19ed373e04deb81225f37cd Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 5 Jun 2019 19:21:16 -0400 Subject: [PATCH 06/24] import globals from helpers (#2612) (#2947) --- src/compiler/compile/render-dom/index.ts | 12 ++++++------ .../compile/render-dom/wrappers/AwaitBlock.ts | 4 ++-- .../compile/render-dom/wrappers/EachBlock.ts | 4 ++-- .../compile/render-ssr/handlers/InlineComponent.ts | 2 +- src/runtime/internal/globals.ts | 5 +++++ src/runtime/internal/index.ts | 1 + src/runtime/internal/utils.ts | 2 +- test/js/samples/debug-empty/expected.js | 3 +++ test/js/samples/debug-foo-bar-baz-things/expected.js | 3 +++ test/js/samples/debug-foo/expected.js | 3 +++ test/js/samples/debug-hoisted/expected.js | 1 + test/js/samples/deconflict-builtins/expected.js | 1 + .../dev-warning-missing-data-computed/expected.js | 3 +++ test/js/samples/each-block-array-literal/expected.js | 1 + test/js/samples/each-block-changed-check/expected.js | 1 + .../js/samples/each-block-keyed-animated/expected.js | 2 ++ test/js/samples/each-block-keyed/expected.js | 2 ++ test/runtime/index.js | 1 + 18 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/runtime/internal/globals.ts diff --git a/src/compiler/compile/render-dom/index.ts b/src/compiler/compile/render-dom/index.ts index 46aa705bfb..a9941b838f 100644 --- a/src/compiler/compile/render-dom/index.ts +++ b/src/compiler/compile/render-dom/index.ts @@ -57,7 +57,7 @@ export default function dom( if (options.dev && !options.hydratable) { block.builders.claim.add_line( - 'throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");' + 'throw new @Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");' ); } @@ -106,7 +106,7 @@ export default function dom( } else if (component.compile_options.dev) { body.push(deindent` get ${x.export_name}() { - throw new Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''"); + throw new @Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''"); } `); } @@ -122,14 +122,14 @@ export default function dom( } else if (component.compile_options.dev) { body.push(deindent` set ${x.export_name}(value) { - throw new Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'"); + throw new @Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'"); } `); } } else if (component.compile_options.dev) { body.push(deindent` set ${x.export_name}(value) { - throw new Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ''"); + throw new @Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ''"); } `); } @@ -145,7 +145,7 @@ export default function dom( const props = ${options.customElement ? `this.attributes` : `options.props || {}`}; ${expected.map(prop => deindent` if (ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) { - console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); + @console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); }`)} `; } @@ -402,7 +402,7 @@ export default function dom( if (component.compile_options.dev && !component.var_lookup.has('$$props') && writable_props.length) { unknown_props_check = deindent` const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`).join(', ')}]; - Object.keys($$props).forEach(key => { + @Object.keys($$props).forEach(key => { if (!writable_props.includes(key) && !key.startsWith('$$')) console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); }); `; diff --git a/src/compiler/compile/render-dom/wrappers/AwaitBlock.ts b/src/compiler/compile/render-dom/wrappers/AwaitBlock.ts index c7af29a073..73ca3fe4f5 100644 --- a/src/compiler/compile/render-dom/wrappers/AwaitBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/AwaitBlock.ts @@ -142,7 +142,7 @@ export default class AwaitBlockWrapper extends Wrapper { this.catch.block.name && `catch: ${this.catch.block.name}`, this.then.block.name && `value: '${this.node.value}'`, this.catch.block.name && `error: '${this.node.error}'`, - this.pending.block.has_outro_method && `blocks: Array(3)` + this.pending.block.has_outro_method && `blocks: [,,,]` ].filter(Boolean); block.builders.init.add_block(deindent` @@ -230,4 +230,4 @@ export default class AwaitBlockWrapper extends Wrapper { branch.fragment.render(branch.block, null, 'nodes'); }); } -} \ No newline at end of file +} diff --git a/src/compiler/compile/render-dom/wrappers/EachBlock.ts b/src/compiler/compile/render-dom/wrappers/EachBlock.ts index a1a875a12f..dddf415557 100644 --- a/src/compiler/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/EachBlock.ts @@ -190,7 +190,7 @@ export default class EachBlockWrapper extends Wrapper { renderer.blocks.push(deindent` function ${this.vars.get_each_context}(ctx, list, i) { - const child_ctx = Object.create(ctx); + const child_ctx = @Object.create(ctx); ${this.context_props} return child_ctx; } @@ -296,7 +296,7 @@ export default class EachBlockWrapper extends Wrapper { const lookup = block.get_unique_name(`${this.var}_lookup`); block.add_variable(iterations, '[]'); - block.add_variable(lookup, `new Map()`); + block.add_variable(lookup, `new @Map()`); if (this.fragment.nodes[0].is_dom_node()) { this.block.first = this.fragment.nodes[0].var; diff --git a/src/compiler/compile/render-ssr/handlers/InlineComponent.ts b/src/compiler/compile/render-ssr/handlers/InlineComponent.ts index 94fdcfd434..2f407df523 100644 --- a/src/compiler/compile/render-ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render-ssr/handlers/InlineComponent.ts @@ -52,7 +52,7 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend let props; if (uses_spread) { - props = `Object.assign(${ + props = `@Object.assign(${ node.attributes .map(attribute => { if (attribute.is_spread) { diff --git a/src/runtime/internal/globals.ts b/src/runtime/internal/globals.ts new file mode 100644 index 0000000000..91a8e4a16d --- /dev/null +++ b/src/runtime/internal/globals.ts @@ -0,0 +1,5 @@ +import { is_client } from './utils'; + +const { console, Error, Map, Object } = (is_client ? window : global) as { console, Error, Map, Object }; + +export { console, Error, Map, Object }; diff --git a/src/runtime/internal/index.ts b/src/runtime/internal/index.ts index 6487f04525..b5ef3e398e 100644 --- a/src/runtime/internal/index.ts +++ b/src/runtime/internal/index.ts @@ -1,6 +1,7 @@ export * from './animations'; export * from './await-block'; export * from './dom'; +export * from './globals'; export * from './keyed-each'; export * from './lifecycle'; export * from './loop'; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 152c0e79b0..5766ac26a5 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -90,7 +90,7 @@ export function once(fn) { } } -const is_client = typeof window !== 'undefined'; +export const is_client = typeof window !== 'undefined'; export let now: () => number = is_client ? () => window.performance.now() diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 6f07993590..2e4f9feb13 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -1,8 +1,11 @@ /* generated by Svelte vX.Y.Z */ import { + Error, + Object, SvelteComponentDev, add_location, append, + console, detach, element, init, diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index eea35d5ba7..c083ea29c3 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -1,8 +1,11 @@ /* generated by Svelte vX.Y.Z */ import { + Error, + Object, SvelteComponentDev, add_location, append, + console, destroy_each, detach, element, diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index 5b931d9464..46096466c1 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -1,8 +1,11 @@ /* generated by Svelte vX.Y.Z */ import { + Error, + Object, SvelteComponentDev, add_location, append, + console, destroy_each, detach, element, diff --git a/test/js/samples/debug-hoisted/expected.js b/test/js/samples/debug-hoisted/expected.js index 51d8bf63a3..7f1b7535f3 100644 --- a/test/js/samples/debug-hoisted/expected.js +++ b/test/js/samples/debug-hoisted/expected.js @@ -1,5 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { + Error, SvelteComponentDev, init, noop, diff --git a/test/js/samples/deconflict-builtins/expected.js b/test/js/samples/deconflict-builtins/expected.js index 3294778aa3..85f3c31597 100644 --- a/test/js/samples/deconflict-builtins/expected.js +++ b/test/js/samples/deconflict-builtins/expected.js @@ -1,5 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { + Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index 5c4b2ece1b..d61f8f3de3 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -1,8 +1,11 @@ /* generated by Svelte vX.Y.Z */ import { + Error, + Object, SvelteComponentDev, add_location, append, + console, detach, element, init, diff --git a/test/js/samples/each-block-array-literal/expected.js b/test/js/samples/each-block-array-literal/expected.js index 455cb2f25f..934c5cfe92 100644 --- a/test/js/samples/each-block-array-literal/expected.js +++ b/test/js/samples/each-block-array-literal/expected.js @@ -1,5 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { + Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index 951565bae4..4ed8756b9a 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -1,5 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { + Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index ec28e60d5f..9e777f25ee 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -1,5 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { + Map, + Object, SvelteComponent, append, create_animation, diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index efb58ebf6a..7ac66a09a7 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -1,5 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { + Map, + Object, SvelteComponent, append, destroy_block, diff --git a/test/runtime/index.js b/test/runtime/index.js index 77879edc1f..900f3087fc 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -125,6 +125,7 @@ describe("runtime", () => { // Put things we need on window for testing window.SvelteComponent = SvelteComponent; + window.Error = global.Error; const target = window.document.querySelector("main"); From f60ae53d764c6d30a7827d19bc2406796a33fab0 Mon Sep 17 00:00:00 2001 From: mrkishi Date: Thu, 6 Jun 2019 10:29:09 -0300 Subject: [PATCH 07/24] deconflict more globals --- src/compiler/compile/Component.ts | 25 ++++++----- src/compiler/compile/render-dom/Block.ts | 2 +- src/compiler/compile/render-dom/index.ts | 10 +++-- .../compile/render-dom/wrappers/Body.ts | 4 +- .../compile/render-dom/wrappers/DebugTag.ts | 4 +- .../render-dom/wrappers/Element/Binding.ts | 2 +- .../render-dom/wrappers/Element/index.ts | 6 +-- .../compile/render-dom/wrappers/Head.ts | 2 +- .../compile/render-dom/wrappers/IfBlock.ts | 2 +- .../render-dom/wrappers/RawMustacheTag.ts | 2 +- .../compile/render-dom/wrappers/Title.ts | 6 +-- .../compile/render-dom/wrappers/Window.ts | 38 ++++++++--------- .../compile/render-ssr/handlers/Element.ts | 2 +- src/runtime/internal/animations.ts | 2 +- src/runtime/internal/dom.ts | 2 + src/runtime/internal/globals.ts | 42 +++++++++++++++++-- src/runtime/internal/style_manager.ts | 1 + src/runtime/internal/utils.ts | 2 + test/helpers.js | 18 +++++--- test/js/samples/bind-online/expected.js | 4 +- .../expected.js | 1 + test/js/samples/css-media-query/expected.js | 1 + .../css-shadow-dom-keyframes/expected.js | 1 + test/js/samples/debug-hoisted/expected.js | 1 + .../js/samples/head-no-whitespace/expected.js | 1 + test/js/samples/media-bindings/expected.js | 2 + test/js/samples/title/expected.js | 1 + .../samples/window-binding-scroll/expected.js | 5 ++- test/runtime/index.js | 10 +---- .../samples/deconflict-globals/_config.js | 14 +++++++ .../samples/deconflict-globals/main.svelte | 20 +++++++++ test/test.js | 4 ++ 32 files changed, 165 insertions(+), 72 deletions(-) create mode 100644 test/runtime/samples/deconflict-globals/_config.js create mode 100644 test/runtime/samples/deconflict-globals/main.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 89c1e6b4f6..2ad2e2e428 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -101,7 +101,7 @@ export default class Component { reactive_declaration_nodes: Set = new Set(); has_reactive_assignments = false; injected_reactive_declaration_vars: Set = new Set(); - helpers: Set = new Set(); + helpers: Map = new Map(); indirect_dependencies: Map> = new Map(); @@ -233,8 +233,9 @@ export default class Component { } helper(name: string) { - this.helpers.add(name); - return this.alias(name); + const alias = this.alias(name) + this.helpers.set(name, alias); + return alias; } generate(result: string) { @@ -251,23 +252,21 @@ export default class Component { .replace(/__svelte:self__/g, this.name) .replace(compile_options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (_match: string, sigil: string, name: string) => { if (sigil === '@') { - if (internal_exports.has(name)) { - if (compile_options.dev && internal_exports.has(`${name}Dev`)) name = `${name}Dev`; - this.helpers.add(name); + if (!internal_exports.has(name)) { + throw new Error(`compiler error: this shouldn't happen! generated code is trying to use inexistent internal '${name}'`); + } + + if (compile_options.dev && internal_exports.has(`${name}Dev`)) { + name = `${name}Dev`; } - return this.alias(name); + return this.helper(name); } return sigil.slice(1) + name; }); - const imported_helpers = Array.from(this.helpers) - .sort() - .map(name => { - const alias = this.alias(name); - return { name, alias }; - }); + const imported_helpers = Array.from(this.helpers, ([name, alias]) => ({ name, alias })); const module = create_module( result, diff --git a/src/compiler/compile/render-dom/Block.ts b/src/compiler/compile/render-dom/Block.ts index 0c585c5bf0..15e079ceb9 100644 --- a/src/compiler/compile/render-dom/Block.ts +++ b/src/compiler/compile/render-dom/Block.ts @@ -164,7 +164,7 @@ export default class Block { if (parent_node) { this.builders.mount.add_line(`@append(${parent_node}, ${name});`); - if (parent_node === 'document.head' && !no_detach) this.builders.destroy.add_line(`@detach(${name});`); + if (parent_node === '@document.head' && !no_detach) this.builders.destroy.add_line(`@detach(${name});`); } else { this.builders.mount.add_line(`@insert(#target, ${name}, anchor);`); if (!no_detach) this.builders.destroy.add_conditional('detaching', `@detach(${name});`); diff --git a/src/compiler/compile/render-dom/index.ts b/src/compiler/compile/render-dom/index.ts index a9941b838f..1ba53e7691 100644 --- a/src/compiler/compile/render-dom/index.ts +++ b/src/compiler/compile/render-dom/index.ts @@ -36,13 +36,15 @@ export default function dom( `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */` : css.code, { only_escape_at_symbol: true }); + const add_css = component.get_unique_name('add_css'); + if (styles && component.compile_options.css !== false && !options.customElement) { builder.add_block(deindent` - function @add_css() { + function ${add_css}() { var style = @element("style"); style.id = '${component.stylesheet.id}-style'; style.textContent = ${styles}; - @append(document.head, style); + @append(@document.head, style); } `); } @@ -481,7 +483,7 @@ export default function dom( if (component.tag != null) { builder.add_block(deindent` - customElements.define("${component.tag}", ${name}); + @customElements.define("${component.tag}", ${name}); `); } } else { @@ -491,7 +493,7 @@ export default function dom( class ${name} extends @${superclass} { constructor(options) { super(${options.dev && `options`}); - ${should_add_css && `if (!document.getElementById("${component.stylesheet.id}-style")) @add_css();`} + ${should_add_css && `if (!@document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} @init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names}); ${dev_props_check} diff --git a/src/compiler/compile/render-dom/wrappers/Body.ts b/src/compiler/compile/render-dom/wrappers/Body.ts index 04e677a090..feb28ef8f6 100644 --- a/src/compiler/compile/render-dom/wrappers/Body.ts +++ b/src/compiler/compile/render-dom/wrappers/Body.ts @@ -11,11 +11,11 @@ export default class BodyWrapper extends Wrapper { const snippet = handler.render(block); block.builders.init.add_block(deindent` - document.body.addEventListener("${handler.name}", ${snippet}); + @document.body.addEventListener("${handler.name}", ${snippet}); `); block.builders.destroy.add_block(deindent` - document.body.removeEventListener("${handler.name}", ${snippet}); + @document.body.removeEventListener("${handler.name}", ${snippet}); `); }); } diff --git a/src/compiler/compile/render-dom/wrappers/DebugTag.ts b/src/compiler/compile/render-dom/wrappers/DebugTag.ts index 713113921d..a600e26297 100644 --- a/src/compiler/compile/render-dom/wrappers/DebugTag.ts +++ b/src/compiler/compile/render-dom/wrappers/DebugTag.ts @@ -62,7 +62,7 @@ export default class DebugTagWrapper extends Wrapper { block.builders.update.add_block(deindent` if (${condition}) { const { ${ctx_identifiers} } = ctx; - console.${log}({ ${logged_identifiers} }); + @console.${log}({ ${logged_identifiers} }); debugger; } `); @@ -70,7 +70,7 @@ export default class DebugTagWrapper extends Wrapper { block.builders.create.add_block(deindent` { const { ${ctx_identifiers} } = ctx; - console.${log}({ ${logged_identifiers} }); + @console.${log}({ ${logged_identifiers} }); debugger; } `); diff --git a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts index 15ceabb356..dd31278774 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts @@ -136,7 +136,7 @@ export default class BindingWrapper { case 'currentTime': case 'playbackRate': case 'volume': - update_conditions.push(`!isNaN(${this.snippet})`); + update_conditions.push(`!@isNaN(${this.snippet})`); break; case 'paused': diff --git a/src/compiler/compile/render-dom/wrappers/Element/index.ts b/src/compiler/compile/render-dom/wrappers/Element/index.ts index 2f7452e8c6..3602ad301d 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/index.ts @@ -270,7 +270,7 @@ export default class ElementWrapper extends Wrapper { `@append(${parent_node}, ${node});` ); - if (parent_node === 'document.head') { + if (parent_node === '@document.head') { block.builders.destroy.add_line(`@detach(${node});`); } } else { @@ -379,7 +379,7 @@ export default class ElementWrapper extends Wrapper { } if (namespace) { - return `document.createElementNS("${namespace}", "${name}")`; + return `@document.createElementNS("${namespace}", "${name}")`; } return `@element("${name}")`; @@ -465,7 +465,7 @@ export default class ElementWrapper extends Wrapper { block.builders.init.add_block(deindent` function ${handler}() { ${animation_frame && deindent` - cancelAnimationFrame(${animation_frame}); + @cancelAnimationFrame(${animation_frame}); if (!${this.var}.paused) ${animation_frame} = @raf(${handler});`} ${needs_lock && `${lock} = true;`} ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''}); diff --git a/src/compiler/compile/render-dom/wrappers/Head.ts b/src/compiler/compile/render-dom/wrappers/Head.ts index d942a69dc2..794506f1be 100644 --- a/src/compiler/compile/render-dom/wrappers/Head.ts +++ b/src/compiler/compile/render-dom/wrappers/Head.ts @@ -30,6 +30,6 @@ export default class HeadWrapper extends Wrapper { } render(block: Block, _parent_node: string, _parent_nodes: string) { - this.fragment.render(block, 'document.head', 'nodes'); + this.fragment.render(block, '@document.head', 'nodes'); } } diff --git a/src/compiler/compile/render-dom/wrappers/IfBlock.ts b/src/compiler/compile/render-dom/wrappers/IfBlock.ts index 46f17653a1..3634091b99 100644 --- a/src/compiler/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/IfBlock.ts @@ -154,7 +154,7 @@ export default class IfBlockWrapper extends Wrapper { const vars = { name, anchor, if_name, has_else, has_transitions }; - const detaching = (parent_node && parent_node !== 'document.head') ? '' : 'detaching'; + const detaching = (parent_node && parent_node !== '@document.head') ? '' : 'detaching'; if (this.node.else) { if (has_outros) { diff --git a/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts b/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts index 15a7d41861..3f09b134f8 100644 --- a/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts +++ b/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts @@ -22,7 +22,7 @@ export default class RawMustacheTagWrapper extends Tag { render(block: Block, parent_node: string, parent_nodes: string) { const name = this.var; - const in_head = parent_node === 'document.head'; + const in_head = parent_node === '@document.head'; const needs_anchors = !parent_node || in_head; // if in head always needs anchors diff --git a/src/compiler/compile/render-dom/wrappers/Title.ts b/src/compiler/compile/render-dom/wrappers/Title.ts index 456a833a8f..c7f702916c 100644 --- a/src/compiler/compile/render-dom/wrappers/Title.ts +++ b/src/compiler/compile/render-dom/wrappers/Title.ts @@ -68,9 +68,9 @@ export default class TitleWrapper extends Wrapper { const init = this.node.should_cache ? `${last} = ${value}` : value; block.builders.init.add_line( - `document.title = ${init};` + `@document.title = ${init};` ); - const updater = `document.title = ${this.node.should_cache ? last : value};`; + const updater = `@document.title = ${this.node.should_cache ? last : value};`; if (all_dependencies.size) { const dependencies = Array.from(all_dependencies); @@ -95,7 +95,7 @@ export default class TitleWrapper extends Wrapper { ? stringify((this.node.children[0] as Text).data) : '""'; - block.builders.hydrate.add_line(`document.title = ${value};`); + block.builders.hydrate.add_line(`@document.title = ${value};`); } } } diff --git a/src/compiler/compile/render-dom/wrappers/Window.ts b/src/compiler/compile/render-dom/wrappers/Window.ts index 585864664a..c267b806c9 100644 --- a/src/compiler/compile/render-dom/wrappers/Window.ts +++ b/src/compiler/compile/render-dom/wrappers/Window.ts @@ -44,8 +44,8 @@ export default class WindowWrapper extends Wrapper { const events = {}; const bindings: Record = {}; - add_actions(component, block, 'window', this.node.actions); - add_event_handlers(block, 'window', this.node.handlers); + add_actions(component, block, '@window', this.node.actions); + add_event_handlers(block, '@window', this.node.handlers); this.node.bindings.forEach(binding => { // in dev mode, throw if read-only values are written to @@ -92,29 +92,29 @@ export default class WindowWrapper extends Wrapper { renderer.meta_bindings.add_block(deindent` if (${condition}) { - window.scrollTo(${x || 'window.pageXOffset'}, ${y || 'window.pageYOffset'}); + @window.scrollTo(${x || 'window.pageXOffset'}, ${y || 'window.pageYOffset'}); } - ${x && `${x} = window.pageXOffset;`} - ${y && `${y} = window.pageYOffset;`} + ${x && `${x} = @window.pageXOffset;`} + ${y && `${y} = @window.pageYOffset;`} `); block.event_listeners.push(deindent` - @listen(window, "${event}", () => { + @listen(@window, "${event}", () => { ${scrolling} = true; - clearTimeout(${scrolling_timeout}); - ${scrolling_timeout} = setTimeout(${clear_scrolling}, 100); + @clearTimeout(${scrolling_timeout}); + ${scrolling_timeout} = @setTimeout(${clear_scrolling}, 100); ctx.${handler_name}(); }) `); } else { props.forEach(prop => { renderer.meta_bindings.add_line( - `this._state.${prop.name} = window.${prop.value};` + `this._state.${prop.name} = @window.${prop.value};` ); }); block.event_listeners.push(deindent` - @listen(window, "${event}", ctx.${handler_name}) + @listen(@window, "${event}", ctx.${handler_name}) `); } @@ -126,7 +126,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(deindent` function ${handler_name}() { - ${props.map(prop => `${prop.name} = window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)} + ${props.map(prop => `${prop.name} = @window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)} } `); @@ -146,13 +146,13 @@ export default class WindowWrapper extends Wrapper { ).join(' || ') } && !${scrolling}) { ${scrolling} = true; - clearTimeout(${scrolling_timeout}); - window.scrollTo(${ - bindings.scrollX ? `ctx.${bindings.scrollX}` : `window.pageXOffset` + @clearTimeout(${scrolling_timeout}); + @window.scrollTo(${ + bindings.scrollX ? `ctx.${bindings.scrollX}` : `@window.pageXOffset` }, ${ - bindings.scrollY ? `ctx.${bindings.scrollY}` : `window.pageYOffset` + bindings.scrollY ? `ctx.${bindings.scrollY}` : `@window.pageYOffset` }); - ${scrolling_timeout} = setTimeout(${clear_scrolling}, 100); + ${scrolling_timeout} = @setTimeout(${clear_scrolling}, 100); } `); } @@ -170,7 +170,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(deindent` function ${handler_name}() { - ${name} = navigator.onLine; $$invalidate('${name}', ${name}); + ${name} = @navigator.onLine; $$invalidate('${name}', ${name}); } `); @@ -179,8 +179,8 @@ export default class WindowWrapper extends Wrapper { `); block.event_listeners.push( - `@listen(window, "online", ctx.${handler_name})`, - `@listen(window, "offline", ctx.${handler_name})` + `@listen(@window, "online", ctx.${handler_name})`, + `@listen(@window, "offline", ctx.${handler_name})` ); component.has_reactive_assignments = true; diff --git a/src/compiler/compile/render-ssr/handlers/Element.ts b/src/compiler/compile/render-ssr/handlers/Element.ts index 681e0d4c7b..637b76aad5 100644 --- a/src/compiler/compile/render-ssr/handlers/Element.ts +++ b/src/compiler/compile/render-ssr/handlers/Element.ts @@ -163,7 +163,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption node_contents='${(' + snippet + ') || ""}'; } else { const snippet = snip(expression); - opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + JSON.stringify(v))) : "")(' + snippet + ')}'; + opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + @JSON.stringify(v))) : "")(' + snippet + ')}'; } }); diff --git a/src/runtime/internal/animations.ts b/src/runtime/internal/animations.ts index 77c86aff0e..0997e9ae23 100644 --- a/src/runtime/internal/animations.ts +++ b/src/runtime/internal/animations.ts @@ -2,7 +2,7 @@ import { identity as linear, noop, now } from './utils'; import { loop } from './loop'; import { create_rule, delete_rule } from './style_manager'; import { AnimationConfig } from '../animate'; - +import { getComputedStyle } from './globals'; //todo: documentation says it is DOMRect, but in IE it would be ClientRect type PositionRect = DOMRect|ClientRect; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index f65d07117c..c1e16de447 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -1,3 +1,5 @@ +import { document, getComputedStyle, navigator } from './globals'; + export function append(target: Node, node: Node) { target.appendChild(node); } diff --git a/src/runtime/internal/globals.ts b/src/runtime/internal/globals.ts index 91a8e4a16d..789290388e 100644 --- a/src/runtime/internal/globals.ts +++ b/src/runtime/internal/globals.ts @@ -1,5 +1,41 @@ -import { is_client } from './utils'; +const { + // ecmascript + Error, + JSON, + Map, + Object, + console, + isNaN, -const { console, Error, Map, Object } = (is_client ? window : global) as { console, Error, Map, Object }; + // dom + cancelAnimationFrame, + clearTimeout, + customElements, + document, + getComputedStyle, + navigator, + requestAnimationFrame, + setTimeout: export_setTimeout, // TODO: remove when upgrading typescript, bug + window: export_window, +} = (window || global) as unknown as typeof globalThis; -export { console, Error, Map, Object }; +export { + // ecmascript + Error, + JSON, + Map, + Object, + console, + isNaN, + + // dom + cancelAnimationFrame, + clearTimeout, + customElements, + document, + getComputedStyle, + navigator, + requestAnimationFrame, + export_setTimeout as setTimeout, + export_window as window, +}; diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index 2721200627..db8c0e9d17 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,5 +1,6 @@ import { element } from './dom'; import { raf } from './utils'; +import { document } from './globals'; let stylesheet; let active = 0; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 5766ac26a5..8624c2246a 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -1,3 +1,5 @@ +import { requestAnimationFrame } from './globals'; + export function noop() {} export const identity = x => x; diff --git a/test/helpers.js b/test/helpers.js index e07d7c9b06..2e6878f8a5 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -28,7 +28,7 @@ export function exists(path) { export function tryToLoadJson(file) { try { - return JSON.parse(fs.readFileSync(file)); + return JSON.parse(fs.readFileSync(file, 'utf-8')); } catch (err) { if (err.code !== 'ENOENT') throw err; return null; @@ -44,14 +44,20 @@ export function tryToReadFile(file) { } } -export const virtualConsole = new jsdom.VirtualConsole(); -const { window } = new jsdom.JSDOM('
', {virtualConsole}); +const virtualConsole = new jsdom.VirtualConsole(); +virtualConsole.sendTo(console); + +global.window = new jsdom.JSDOM('
', {virtualConsole}).window; global.document = window.document; -global.getComputedStyle = window.getComputedStyle; -global.navigator = {userAgent: 'fake'}; + +// add missing ecmascript globals to window +for (const key of Object.getOwnPropertyNames(global)) { + window[key] = window[key] || global[key]; +} export function env() { window._svelteTransitionManager = null; + window.document.title = ''; window.document.body.innerHTML = '
'; return window; @@ -120,7 +126,7 @@ export function normalizeHtml(window, html) { .replace(//g, '') .replace(/>[\s\r\n]+<') .trim(); - cleanChildren(node, ''); + cleanChildren(node); return node.innerHTML.replace(/<\/?noscript\/?>/g, ''); } catch (err) { throw new Error(`Failed to normalize HTML:\n${html}`); diff --git a/test/js/samples/bind-online/expected.js b/test/js/samples/bind-online/expected.js index 0c9faa3ef6..9c3fdb7385 100644 --- a/test/js/samples/bind-online/expected.js +++ b/test/js/samples/bind-online/expected.js @@ -4,9 +4,11 @@ import { add_render_callback, init, listen, + navigator, noop, run_all, - safe_not_equal + safe_not_equal, + window } from "svelte/internal"; function create_fragment(ctx) { diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index 12164d0579..ed01b6430f 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -3,6 +3,7 @@ import { SvelteComponent, append, detach, + document, element, init, insert, diff --git a/test/js/samples/css-media-query/expected.js b/test/js/samples/css-media-query/expected.js index d4be134376..8a99d70e8e 100644 --- a/test/js/samples/css-media-query/expected.js +++ b/test/js/samples/css-media-query/expected.js @@ -3,6 +3,7 @@ import { SvelteComponent, append, detach, + document, element, init, insert, diff --git a/test/js/samples/css-shadow-dom-keyframes/expected.js b/test/js/samples/css-shadow-dom-keyframes/expected.js index 9f70b8ec66..3be52803d7 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected.js @@ -1,6 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteElement, + customElements, detach, element, init, diff --git a/test/js/samples/debug-hoisted/expected.js b/test/js/samples/debug-hoisted/expected.js index 7f1b7535f3..799f0d4b32 100644 --- a/test/js/samples/debug-hoisted/expected.js +++ b/test/js/samples/debug-hoisted/expected.js @@ -2,6 +2,7 @@ import { Error, SvelteComponentDev, + console, init, noop, safe_not_equal diff --git a/test/js/samples/head-no-whitespace/expected.js b/test/js/samples/head-no-whitespace/expected.js index b95177bba7..859891e689 100644 --- a/test/js/samples/head-no-whitespace/expected.js +++ b/test/js/samples/head-no-whitespace/expected.js @@ -3,6 +3,7 @@ import { SvelteComponent, append, detach, + document, element, init, noop, diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index f45f9ce8db..ec30a98161 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -2,10 +2,12 @@ import { SvelteComponent, add_render_callback, + cancelAnimationFrame, detach, element, init, insert, + isNaN, listen, noop, raf, diff --git a/test/js/samples/title/expected.js b/test/js/samples/title/expected.js index 884f39e246..2e551ae426 100644 --- a/test/js/samples/title/expected.js +++ b/test/js/samples/title/expected.js @@ -1,6 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, + document, init, noop, safe_not_equal diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 18f5210bea..1e63973518 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -3,6 +3,7 @@ import { SvelteComponent, add_render_callback, append, + clearTimeout, detach, element, init, @@ -10,8 +11,10 @@ import { listen, noop, safe_not_equal, + setTimeout, set_data, - text + text, + window } from "svelte/internal"; function create_fragment(ctx) { diff --git a/test/runtime/index.js b/test/runtime/index.js index 900f3087fc..87be528a61 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -3,7 +3,7 @@ import * as path from "path"; import * as fs from "fs"; import { rollup } from 'rollup'; import * as virtual from 'rollup-plugin-virtual'; -import { clear_loops, set_now, set_raf } from "../../internal"; +import { clear_loops, flush, set_now, set_raf } from "../../internal"; import { showOutput, @@ -20,7 +20,6 @@ let compileOptions = null; let compile = null; const sveltePath = process.cwd().split('\\').join('/'); -const internal = `${sveltePath}/internal`; describe("runtime", () => { before(() => { @@ -47,8 +46,6 @@ describe("runtime", () => { function runTest(dir, hydrate) { if (dir[0] === ".") return; - const { flush } = require(internal); - const config = loadConfig(`./runtime/samples/${dir}/_config.js`); if (hydrate && config.skip_if_hydrate) return; @@ -66,7 +63,6 @@ describe("runtime", () => { compile = (config.preserveIdentifiers ? svelte : svelte$).compile; const cwd = path.resolve(`test/runtime/samples/${dir}`); - global.document.title = ''; compileOptions = config.compileOptions || {}; compileOptions.sveltePath = sveltePath; @@ -119,13 +115,10 @@ describe("runtime", () => { throw err; } - global.window = window; - if (config.before_test) config.before_test(); // Put things we need on window for testing window.SvelteComponent = SvelteComponent; - window.Error = global.Error; const target = window.document.querySelector("main"); @@ -222,6 +215,7 @@ describe("runtime", () => { 'main.js': js.code }), { + name: 'svelte-packages', resolveId: (importee, importer) => { if (importee.startsWith('svelte/')) { return importee.replace('svelte', process.cwd()) + '/index.mjs'; diff --git a/test/runtime/samples/deconflict-globals/_config.js b/test/runtime/samples/deconflict-globals/_config.js new file mode 100644 index 0000000000..c29f022a37 --- /dev/null +++ b/test/runtime/samples/deconflict-globals/_config.js @@ -0,0 +1,14 @@ +export default { + preserveIdentifiers: true, + compileOptions: { + name: 'window' + }, + + html: ` +

I hereby declare Svelte the bestest framework.

+

nintendo sixty four

+

Woops.

+

42

+

false

+ ` +}; diff --git a/test/runtime/samples/deconflict-globals/main.svelte b/test/runtime/samples/deconflict-globals/main.svelte new file mode 100644 index 0000000000..d1928ea533 --- /dev/null +++ b/test/runtime/samples/deconflict-globals/main.svelte @@ -0,0 +1,20 @@ + + + + Cute test + + + + + +{#each everyone as someone (someone)} +

{someone}

+{/each} diff --git a/test/test.js b/test/test.js index 7759941dbb..cb89b3e9d1 100644 --- a/test/test.js +++ b/test/test.js @@ -2,6 +2,10 @@ const glob = require("tiny-glob/sync.js"); require("./setup"); +// bind internal to jsdom +require("./helpers"); +require("../internal"); + glob("*/index.{js,ts}", { cwd: "test" }).forEach((file) => { require("./" + file); }); From 788cf97a93343b7f80985ba4e31dd67312786d8f Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 9 Jun 2019 11:27:03 -0400 Subject: [PATCH 08/24] prevent compiled output blowing up in Node if window is not defined --- src/runtime/internal/globals.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/runtime/internal/globals.ts b/src/runtime/internal/globals.ts index 789290388e..314f17dfeb 100644 --- a/src/runtime/internal/globals.ts +++ b/src/runtime/internal/globals.ts @@ -1,3 +1,5 @@ +const win = typeof window !== 'undefined' ? window : global; + const { // ecmascript Error, @@ -15,9 +17,8 @@ const { getComputedStyle, navigator, requestAnimationFrame, - setTimeout: export_setTimeout, // TODO: remove when upgrading typescript, bug - window: export_window, -} = (window || global) as unknown as typeof globalThis; + setTimeout: export_setTimeout // TODO: remove when upgrading typescript, bug +} = win as unknown as typeof globalThis; export { // ecmascript @@ -37,5 +38,5 @@ export { navigator, requestAnimationFrame, export_setTimeout as setTimeout, - export_window as window, + win as window, }; From cd4c2f2075bbf720f630cfa74b29b65997b27a27 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 12 Jun 2019 21:09:42 -0400 Subject: [PATCH 09/24] use `@_` sigil for globals --- src/compiler/compile/Component.ts | 16 +++++++ src/compiler/compile/create_module.ts | 15 ++++++- src/compiler/compile/render-dom/Block.ts | 2 +- src/compiler/compile/render-dom/index.ts | 18 ++++---- .../compile/render-dom/wrappers/Body.ts | 4 +- .../compile/render-dom/wrappers/DebugTag.ts | 4 +- .../compile/render-dom/wrappers/EachBlock.ts | 4 +- .../render-dom/wrappers/Element/Binding.ts | 2 +- .../render-dom/wrappers/Element/index.ts | 6 +-- .../compile/render-dom/wrappers/Head.ts | 2 +- .../compile/render-dom/wrappers/IfBlock.ts | 2 +- .../render-dom/wrappers/RawMustacheTag.ts | 2 +- .../compile/render-dom/wrappers/Title.ts | 6 +-- .../compile/render-dom/wrappers/Window.ts | 38 ++++++++-------- .../compile/render-ssr/handlers/Element.ts | 2 +- .../render-ssr/handlers/InlineComponent.ts | 2 +- src/runtime/internal/animations.ts | 2 +- src/runtime/internal/dom.ts | 2 - src/runtime/internal/globals.ts | 43 +------------------ src/runtime/internal/style_manager.ts | 1 - src/runtime/internal/utils.ts | 2 - 21 files changed, 78 insertions(+), 97 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 2ad2e2e428..c4924697f8 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -102,6 +102,7 @@ export default class Component { has_reactive_assignments = false; injected_reactive_declaration_vars: Set = new Set(); helpers: Map = new Map(); + globals: Map = new Map(); indirect_dependencies: Map> = new Map(); @@ -238,6 +239,12 @@ export default class Component { return alias; } + global(name: string) { + const alias = this.alias(name); + this.globals.set(name, alias); + return alias; + } + generate(result: string) { let js = null; let css = null; @@ -252,6 +259,10 @@ export default class Component { .replace(/__svelte:self__/g, this.name) .replace(compile_options.generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g, (_match: string, sigil: string, name: string) => { if (sigil === '@') { + if (name[0] === '_') { + return this.global(name.slice(1)); + } + if (!internal_exports.has(name)) { throw new Error(`compiler error: this shouldn't happen! generated code is trying to use inexistent internal '${name}'`); } @@ -266,6 +277,10 @@ export default class Component { return sigil.slice(1) + name; }); + const referenced_globals = Array.from(this.globals, ([name, alias]) => name !== alias && ({ name, alias })).filter(Boolean); + if (referenced_globals.length) { + this.helper('globals'); + } const imported_helpers = Array.from(this.helpers, ([name, alias]) => ({ name, alias })); const module = create_module( @@ -275,6 +290,7 @@ export default class Component { banner, compile_options.sveltePath, imported_helpers, + referenced_globals, this.imports, this.vars.filter(variable => variable.module && variable.export_name).map(variable => ({ name: variable.name, diff --git a/src/compiler/compile/create_module.ts b/src/compiler/compile/create_module.ts index 54b891d4ac..79cb33549c 100644 --- a/src/compiler/compile/create_module.ts +++ b/src/compiler/compile/create_module.ts @@ -17,6 +17,7 @@ export default function create_module( banner: string, sveltePath = 'svelte', helpers: Array<{ name: string; alias: string }>, + globals: Array<{ name: string; alias: string }>, imports: Node[], module_exports: Export[], source: string @@ -24,10 +25,10 @@ export default function create_module( const internal_path = `${sveltePath}/internal`; if (format === 'esm') { - return esm(code, name, banner, sveltePath, internal_path, helpers, imports, module_exports, source); + return esm(code, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports, source); } - if (format === 'cjs') return cjs(code, name, banner, sveltePath, internal_path, helpers, imports, module_exports); + if (format === 'cjs') return cjs(code, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports); throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); } @@ -45,6 +46,7 @@ function esm( sveltePath: string, internal_path: string, helpers: Array<{ name: string; alias: string }>, + globals: Array<{ name: string; alias: string }>, imports: Node[], module_exports: Export[], source: string @@ -52,6 +54,9 @@ function esm( const internal_imports = helpers.length > 0 && ( `import ${stringify_props(helpers.map(h => h.name === h.alias ? h.name : `${h.name} as ${h.alias}`).sort())} from ${JSON.stringify(internal_path)};` ); + const internal_globals = globals.length > 0 && ( + `const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};` + ); const user_imports = imports.length > 0 && ( imports @@ -70,6 +75,7 @@ function esm( return deindent` ${banner} ${internal_imports} + ${internal_globals} ${user_imports} ${code} @@ -85,6 +91,7 @@ function cjs( sveltePath: string, internal_path: string, helpers: Array<{ name: string; alias: string }>, + globals: Array<{ name: string; alias: string }>, imports: Node[], module_exports: Export[] ) { @@ -93,6 +100,9 @@ function cjs( const internal_imports = helpers.length > 0 && ( `const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n` ); + const internal_globals = globals.length > 0 && ( + `const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};` + ); const requires = imports.map(node => { let lhs; @@ -127,6 +137,7 @@ function cjs( "use strict"; ${internal_imports} + ${internal_globals} ${requires} ${code} diff --git a/src/compiler/compile/render-dom/Block.ts b/src/compiler/compile/render-dom/Block.ts index 15e079ceb9..d7986e167b 100644 --- a/src/compiler/compile/render-dom/Block.ts +++ b/src/compiler/compile/render-dom/Block.ts @@ -164,7 +164,7 @@ export default class Block { if (parent_node) { this.builders.mount.add_line(`@append(${parent_node}, ${name});`); - if (parent_node === '@document.head' && !no_detach) this.builders.destroy.add_line(`@detach(${name});`); + if (parent_node === '@_document.head' && !no_detach) this.builders.destroy.add_line(`@detach(${name});`); } else { this.builders.mount.add_line(`@insert(#target, ${name}, anchor);`); if (!no_detach) this.builders.destroy.add_conditional('detaching', `@detach(${name});`); diff --git a/src/compiler/compile/render-dom/index.ts b/src/compiler/compile/render-dom/index.ts index 1ba53e7691..3dec1a81b3 100644 --- a/src/compiler/compile/render-dom/index.ts +++ b/src/compiler/compile/render-dom/index.ts @@ -44,7 +44,7 @@ export default function dom( var style = @element("style"); style.id = '${component.stylesheet.id}-style'; style.textContent = ${styles}; - @append(@document.head, style); + @append(@_document.head, style); } `); } @@ -59,7 +59,7 @@ export default function dom( if (options.dev && !options.hydratable) { block.builders.claim.add_line( - 'throw new @Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");' + 'throw new @_Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");' ); } @@ -108,7 +108,7 @@ export default function dom( } else if (component.compile_options.dev) { body.push(deindent` get ${x.export_name}() { - throw new @Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''"); + throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''"); } `); } @@ -124,14 +124,14 @@ export default function dom( } else if (component.compile_options.dev) { body.push(deindent` set ${x.export_name}(value) { - throw new @Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'"); + throw new @_Error("<${component.tag}>: Cannot set read-only property '${x.export_name}'"); } `); } } else if (component.compile_options.dev) { body.push(deindent` set ${x.export_name}(value) { - throw new @Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ''"); + throw new @_Error("<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ''"); } `); } @@ -147,7 +147,7 @@ export default function dom( const props = ${options.customElement ? `this.attributes` : `options.props || {}`}; ${expected.map(prop => deindent` if (ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) { - @console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); + @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); }`)} `; } @@ -404,7 +404,7 @@ export default function dom( if (component.compile_options.dev && !component.var_lookup.has('$$props') && writable_props.length) { unknown_props_check = deindent` const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`).join(', ')}]; - @Object.keys($$props).forEach(key => { + @_Object.keys($$props).forEach(key => { if (!writable_props.includes(key) && !key.startsWith('$$')) console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); }); `; @@ -483,7 +483,7 @@ export default function dom( if (component.tag != null) { builder.add_block(deindent` - @customElements.define("${component.tag}", ${name}); + @_customElements.define("${component.tag}", ${name}); `); } } else { @@ -493,7 +493,7 @@ export default function dom( class ${name} extends @${superclass} { constructor(options) { super(${options.dev && `options`}); - ${should_add_css && `if (!@document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} + ${should_add_css && `if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} @init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names}); ${dev_props_check} diff --git a/src/compiler/compile/render-dom/wrappers/Body.ts b/src/compiler/compile/render-dom/wrappers/Body.ts index feb28ef8f6..623625ce6d 100644 --- a/src/compiler/compile/render-dom/wrappers/Body.ts +++ b/src/compiler/compile/render-dom/wrappers/Body.ts @@ -11,11 +11,11 @@ export default class BodyWrapper extends Wrapper { const snippet = handler.render(block); block.builders.init.add_block(deindent` - @document.body.addEventListener("${handler.name}", ${snippet}); + @_document.body.addEventListener("${handler.name}", ${snippet}); `); block.builders.destroy.add_block(deindent` - @document.body.removeEventListener("${handler.name}", ${snippet}); + @_document.body.removeEventListener("${handler.name}", ${snippet}); `); }); } diff --git a/src/compiler/compile/render-dom/wrappers/DebugTag.ts b/src/compiler/compile/render-dom/wrappers/DebugTag.ts index a600e26297..6705b51cc5 100644 --- a/src/compiler/compile/render-dom/wrappers/DebugTag.ts +++ b/src/compiler/compile/render-dom/wrappers/DebugTag.ts @@ -62,7 +62,7 @@ export default class DebugTagWrapper extends Wrapper { block.builders.update.add_block(deindent` if (${condition}) { const { ${ctx_identifiers} } = ctx; - @console.${log}({ ${logged_identifiers} }); + @_console.${log}({ ${logged_identifiers} }); debugger; } `); @@ -70,7 +70,7 @@ export default class DebugTagWrapper extends Wrapper { block.builders.create.add_block(deindent` { const { ${ctx_identifiers} } = ctx; - @console.${log}({ ${logged_identifiers} }); + @_console.${log}({ ${logged_identifiers} }); debugger; } `); diff --git a/src/compiler/compile/render-dom/wrappers/EachBlock.ts b/src/compiler/compile/render-dom/wrappers/EachBlock.ts index dddf415557..3ce2a342a3 100644 --- a/src/compiler/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/EachBlock.ts @@ -190,7 +190,7 @@ export default class EachBlockWrapper extends Wrapper { renderer.blocks.push(deindent` function ${this.vars.get_each_context}(ctx, list, i) { - const child_ctx = @Object.create(ctx); + const child_ctx = @_Object.create(ctx); ${this.context_props} return child_ctx; } @@ -296,7 +296,7 @@ export default class EachBlockWrapper extends Wrapper { const lookup = block.get_unique_name(`${this.var}_lookup`); block.add_variable(iterations, '[]'); - block.add_variable(lookup, `new @Map()`); + block.add_variable(lookup, `new @_Map()`); if (this.fragment.nodes[0].is_dom_node()) { this.block.first = this.fragment.nodes[0].var; diff --git a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts index dd31278774..5b1fe6dee1 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts @@ -136,7 +136,7 @@ export default class BindingWrapper { case 'currentTime': case 'playbackRate': case 'volume': - update_conditions.push(`!@isNaN(${this.snippet})`); + update_conditions.push(`!@_isNaN(${this.snippet})`); break; case 'paused': diff --git a/src/compiler/compile/render-dom/wrappers/Element/index.ts b/src/compiler/compile/render-dom/wrappers/Element/index.ts index 3602ad301d..1b19281461 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/index.ts @@ -270,7 +270,7 @@ export default class ElementWrapper extends Wrapper { `@append(${parent_node}, ${node});` ); - if (parent_node === '@document.head') { + if (parent_node === '@_document.head') { block.builders.destroy.add_line(`@detach(${node});`); } } else { @@ -379,7 +379,7 @@ export default class ElementWrapper extends Wrapper { } if (namespace) { - return `@document.createElementNS("${namespace}", "${name}")`; + return `@_document.createElementNS("${namespace}", "${name}")`; } return `@element("${name}")`; @@ -465,7 +465,7 @@ export default class ElementWrapper extends Wrapper { block.builders.init.add_block(deindent` function ${handler}() { ${animation_frame && deindent` - @cancelAnimationFrame(${animation_frame}); + @_cancelAnimationFrame(${animation_frame}); if (!${this.var}.paused) ${animation_frame} = @raf(${handler});`} ${needs_lock && `${lock} = true;`} ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''}); diff --git a/src/compiler/compile/render-dom/wrappers/Head.ts b/src/compiler/compile/render-dom/wrappers/Head.ts index 794506f1be..a5226e7efa 100644 --- a/src/compiler/compile/render-dom/wrappers/Head.ts +++ b/src/compiler/compile/render-dom/wrappers/Head.ts @@ -30,6 +30,6 @@ export default class HeadWrapper extends Wrapper { } render(block: Block, _parent_node: string, _parent_nodes: string) { - this.fragment.render(block, '@document.head', 'nodes'); + this.fragment.render(block, '@_document.head', 'nodes'); } } diff --git a/src/compiler/compile/render-dom/wrappers/IfBlock.ts b/src/compiler/compile/render-dom/wrappers/IfBlock.ts index 3634091b99..bce4743488 100644 --- a/src/compiler/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/IfBlock.ts @@ -154,7 +154,7 @@ export default class IfBlockWrapper extends Wrapper { const vars = { name, anchor, if_name, has_else, has_transitions }; - const detaching = (parent_node && parent_node !== '@document.head') ? '' : 'detaching'; + const detaching = (parent_node && parent_node !== '@_document.head') ? '' : 'detaching'; if (this.node.else) { if (has_outros) { diff --git a/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts b/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts index 3f09b134f8..f85c48935e 100644 --- a/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts +++ b/src/compiler/compile/render-dom/wrappers/RawMustacheTag.ts @@ -22,7 +22,7 @@ export default class RawMustacheTagWrapper extends Tag { render(block: Block, parent_node: string, parent_nodes: string) { const name = this.var; - const in_head = parent_node === '@document.head'; + const in_head = parent_node === '@_document.head'; const needs_anchors = !parent_node || in_head; // if in head always needs anchors diff --git a/src/compiler/compile/render-dom/wrappers/Title.ts b/src/compiler/compile/render-dom/wrappers/Title.ts index c7f702916c..302c9f1aa9 100644 --- a/src/compiler/compile/render-dom/wrappers/Title.ts +++ b/src/compiler/compile/render-dom/wrappers/Title.ts @@ -68,9 +68,9 @@ export default class TitleWrapper extends Wrapper { const init = this.node.should_cache ? `${last} = ${value}` : value; block.builders.init.add_line( - `@document.title = ${init};` + `@_document.title = ${init};` ); - const updater = `@document.title = ${this.node.should_cache ? last : value};`; + const updater = `@_document.title = ${this.node.should_cache ? last : value};`; if (all_dependencies.size) { const dependencies = Array.from(all_dependencies); @@ -95,7 +95,7 @@ export default class TitleWrapper extends Wrapper { ? stringify((this.node.children[0] as Text).data) : '""'; - block.builders.hydrate.add_line(`@document.title = ${value};`); + block.builders.hydrate.add_line(`@_document.title = ${value};`); } } } diff --git a/src/compiler/compile/render-dom/wrappers/Window.ts b/src/compiler/compile/render-dom/wrappers/Window.ts index c267b806c9..8c8e6c623d 100644 --- a/src/compiler/compile/render-dom/wrappers/Window.ts +++ b/src/compiler/compile/render-dom/wrappers/Window.ts @@ -44,8 +44,8 @@ export default class WindowWrapper extends Wrapper { const events = {}; const bindings: Record = {}; - add_actions(component, block, '@window', this.node.actions); - add_event_handlers(block, '@window', this.node.handlers); + add_actions(component, block, '@_window', this.node.actions); + add_event_handlers(block, '@_window', this.node.handlers); this.node.bindings.forEach(binding => { // in dev mode, throw if read-only values are written to @@ -92,29 +92,29 @@ export default class WindowWrapper extends Wrapper { renderer.meta_bindings.add_block(deindent` if (${condition}) { - @window.scrollTo(${x || 'window.pageXOffset'}, ${y || 'window.pageYOffset'}); + @_scrollTo(${x || '@_pageXOffset'}, ${y || '@pageYOffset'}); } - ${x && `${x} = @window.pageXOffset;`} - ${y && `${y} = @window.pageYOffset;`} + ${x && `${x} = @_pageXOffset;`} + ${y && `${y} = @_pageYOffset;`} `); block.event_listeners.push(deindent` - @listen(@window, "${event}", () => { + @listen(@_window, "${event}", () => { ${scrolling} = true; - @clearTimeout(${scrolling_timeout}); - ${scrolling_timeout} = @setTimeout(${clear_scrolling}, 100); + @_clearTimeout(${scrolling_timeout}); + ${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100); ctx.${handler_name}(); }) `); } else { props.forEach(prop => { renderer.meta_bindings.add_line( - `this._state.${prop.name} = @window.${prop.value};` + `this._state.${prop.name} = @_window.${prop.value};` ); }); block.event_listeners.push(deindent` - @listen(@window, "${event}", ctx.${handler_name}) + @listen(@_window, "${event}", ctx.${handler_name}) `); } @@ -126,7 +126,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(deindent` function ${handler_name}() { - ${props.map(prop => `${prop.name} = @window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)} + ${props.map(prop => `${prop.name} = @_window.${prop.value}; $$invalidate('${prop.name}', ${prop.name});`)} } `); @@ -146,13 +146,13 @@ export default class WindowWrapper extends Wrapper { ).join(' || ') } && !${scrolling}) { ${scrolling} = true; - @clearTimeout(${scrolling_timeout}); - @window.scrollTo(${ - bindings.scrollX ? `ctx.${bindings.scrollX}` : `@window.pageXOffset` + @_clearTimeout(${scrolling_timeout}); + @_scrollTo(${ + bindings.scrollX ? `ctx.${bindings.scrollX}` : `@_pageXOffset` }, ${ - bindings.scrollY ? `ctx.${bindings.scrollY}` : `@window.pageYOffset` + bindings.scrollY ? `ctx.${bindings.scrollY}` : `@_pageYOffset` }); - ${scrolling_timeout} = @setTimeout(${clear_scrolling}, 100); + ${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100); } `); } @@ -170,7 +170,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(deindent` function ${handler_name}() { - ${name} = @navigator.onLine; $$invalidate('${name}', ${name}); + ${name} = @_navigator.onLine; $$invalidate('${name}', ${name}); } `); @@ -179,8 +179,8 @@ export default class WindowWrapper extends Wrapper { `); block.event_listeners.push( - `@listen(@window, "online", ctx.${handler_name})`, - `@listen(@window, "offline", ctx.${handler_name})` + `@listen(@_window, "online", ctx.${handler_name})`, + `@listen(@_window, "offline", ctx.${handler_name})` ); component.has_reactive_assignments = true; diff --git a/src/compiler/compile/render-ssr/handlers/Element.ts b/src/compiler/compile/render-ssr/handlers/Element.ts index 637b76aad5..78b95075b2 100644 --- a/src/compiler/compile/render-ssr/handlers/Element.ts +++ b/src/compiler/compile/render-ssr/handlers/Element.ts @@ -163,7 +163,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption node_contents='${(' + snippet + ') || ""}'; } else { const snippet = snip(expression); - opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + @JSON.stringify(v))) : "")(' + snippet + ')}'; + opening_tag += ' ${(v => v ? ("' + name + '" + (v === true ? "" : "=" + @_JSON.stringify(v))) : "")(' + snippet + ')}'; } }); diff --git a/src/compiler/compile/render-ssr/handlers/InlineComponent.ts b/src/compiler/compile/render-ssr/handlers/InlineComponent.ts index 2f407df523..320bf5e6a0 100644 --- a/src/compiler/compile/render-ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render-ssr/handlers/InlineComponent.ts @@ -52,7 +52,7 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend let props; if (uses_spread) { - props = `@Object.assign(${ + props = `@_Object.assign(${ node.attributes .map(attribute => { if (attribute.is_spread) { diff --git a/src/runtime/internal/animations.ts b/src/runtime/internal/animations.ts index 0997e9ae23..77c86aff0e 100644 --- a/src/runtime/internal/animations.ts +++ b/src/runtime/internal/animations.ts @@ -2,7 +2,7 @@ import { identity as linear, noop, now } from './utils'; import { loop } from './loop'; import { create_rule, delete_rule } from './style_manager'; import { AnimationConfig } from '../animate'; -import { getComputedStyle } from './globals'; + //todo: documentation says it is DOMRect, but in IE it would be ClientRect type PositionRect = DOMRect|ClientRect; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index c1e16de447..f65d07117c 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -1,5 +1,3 @@ -import { document, getComputedStyle, navigator } from './globals'; - export function append(target: Node, node: Node) { target.appendChild(node); } diff --git a/src/runtime/internal/globals.ts b/src/runtime/internal/globals.ts index 314f17dfeb..2f189d0c0a 100644 --- a/src/runtime/internal/globals.ts +++ b/src/runtime/internal/globals.ts @@ -1,42 +1 @@ -const win = typeof window !== 'undefined' ? window : global; - -const { - // ecmascript - Error, - JSON, - Map, - Object, - console, - isNaN, - - // dom - cancelAnimationFrame, - clearTimeout, - customElements, - document, - getComputedStyle, - navigator, - requestAnimationFrame, - setTimeout: export_setTimeout // TODO: remove when upgrading typescript, bug -} = win as unknown as typeof globalThis; - -export { - // ecmascript - Error, - JSON, - Map, - Object, - console, - isNaN, - - // dom - cancelAnimationFrame, - clearTimeout, - customElements, - document, - getComputedStyle, - navigator, - requestAnimationFrame, - export_setTimeout as setTimeout, - win as window, -}; +export const globals = (typeof window !== 'undefined' ? window : global) as unknown as typeof globalThis; diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index db8c0e9d17..2721200627 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,6 +1,5 @@ import { element } from './dom'; import { raf } from './utils'; -import { document } from './globals'; let stylesheet; let active = 0; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 8624c2246a..5766ac26a5 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -1,5 +1,3 @@ -import { requestAnimationFrame } from './globals'; - export function noop() {} export const identity = x => x; From e5d4162092d5a9560ed957d183759cbb1b49135a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 12 Jun 2019 21:10:25 -0400 Subject: [PATCH 10/24] update tests --- test/helpers.js | 1 + test/js/samples/bind-online/expected.js | 4 +--- test/js/samples/collapses-text-around-comments/expected.js | 1 - test/js/samples/css-media-query/expected.js | 1 - test/js/samples/css-shadow-dom-keyframes/expected.js | 1 - test/js/samples/debug-empty/expected.js | 3 --- test/js/samples/debug-foo-bar-baz-things/expected.js | 3 --- test/js/samples/debug-foo/expected.js | 3 --- test/js/samples/debug-hoisted/expected.js | 2 -- test/js/samples/deconflict-builtins/expected.js | 1 - .../samples/dev-warning-missing-data-computed/expected.js | 3 --- test/js/samples/each-block-array-literal/expected.js | 1 - test/js/samples/each-block-changed-check/expected.js | 1 - test/js/samples/each-block-keyed-animated/expected.js | 2 -- test/js/samples/each-block-keyed/expected.js | 2 -- test/js/samples/head-no-whitespace/expected.js | 1 - test/js/samples/media-bindings/expected.js | 2 -- test/js/samples/title/expected.js | 1 - test/js/samples/window-binding-scroll/expected.js | 7 ++----- 19 files changed, 4 insertions(+), 36 deletions(-) diff --git a/test/helpers.js b/test/helpers.js index 2e6878f8a5..514d084698 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -49,6 +49,7 @@ virtualConsole.sendTo(console); global.window = new jsdom.JSDOM('
', {virtualConsole}).window; global.document = window.document; +global.requestAnimationFrame = null; // placeholder, filled in using set_raf // add missing ecmascript globals to window for (const key of Object.getOwnPropertyNames(global)) { diff --git a/test/js/samples/bind-online/expected.js b/test/js/samples/bind-online/expected.js index 9c3fdb7385..0c9faa3ef6 100644 --- a/test/js/samples/bind-online/expected.js +++ b/test/js/samples/bind-online/expected.js @@ -4,11 +4,9 @@ import { add_render_callback, init, listen, - navigator, noop, run_all, - safe_not_equal, - window + safe_not_equal } from "svelte/internal"; function create_fragment(ctx) { diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index ed01b6430f..12164d0579 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -3,7 +3,6 @@ import { SvelteComponent, append, detach, - document, element, init, insert, diff --git a/test/js/samples/css-media-query/expected.js b/test/js/samples/css-media-query/expected.js index 8a99d70e8e..d4be134376 100644 --- a/test/js/samples/css-media-query/expected.js +++ b/test/js/samples/css-media-query/expected.js @@ -3,7 +3,6 @@ import { SvelteComponent, append, detach, - document, element, init, insert, diff --git a/test/js/samples/css-shadow-dom-keyframes/expected.js b/test/js/samples/css-shadow-dom-keyframes/expected.js index 3be52803d7..9f70b8ec66 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected.js @@ -1,7 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { SvelteElement, - customElements, detach, element, init, diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 2e4f9feb13..6f07993590 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -1,11 +1,8 @@ /* generated by Svelte vX.Y.Z */ import { - Error, - Object, SvelteComponentDev, add_location, append, - console, detach, element, init, diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index c083ea29c3..eea35d5ba7 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -1,11 +1,8 @@ /* generated by Svelte vX.Y.Z */ import { - Error, - Object, SvelteComponentDev, add_location, append, - console, destroy_each, detach, element, diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index 46096466c1..5b931d9464 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -1,11 +1,8 @@ /* generated by Svelte vX.Y.Z */ import { - Error, - Object, SvelteComponentDev, add_location, append, - console, destroy_each, detach, element, diff --git a/test/js/samples/debug-hoisted/expected.js b/test/js/samples/debug-hoisted/expected.js index 799f0d4b32..51d8bf63a3 100644 --- a/test/js/samples/debug-hoisted/expected.js +++ b/test/js/samples/debug-hoisted/expected.js @@ -1,8 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { - Error, SvelteComponentDev, - console, init, noop, safe_not_equal diff --git a/test/js/samples/deconflict-builtins/expected.js b/test/js/samples/deconflict-builtins/expected.js index 85f3c31597..3294778aa3 100644 --- a/test/js/samples/deconflict-builtins/expected.js +++ b/test/js/samples/deconflict-builtins/expected.js @@ -1,6 +1,5 @@ /* generated by Svelte vX.Y.Z */ import { - Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index d61f8f3de3..5c4b2ece1b 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -1,11 +1,8 @@ /* generated by Svelte vX.Y.Z */ import { - Error, - Object, SvelteComponentDev, add_location, append, - console, detach, element, init, diff --git a/test/js/samples/each-block-array-literal/expected.js b/test/js/samples/each-block-array-literal/expected.js index 934c5cfe92..455cb2f25f 100644 --- a/test/js/samples/each-block-array-literal/expected.js +++ b/test/js/samples/each-block-array-literal/expected.js @@ -1,6 +1,5 @@ /* generated by Svelte vX.Y.Z */ import { - Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index 4ed8756b9a..951565bae4 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -1,6 +1,5 @@ /* generated by Svelte vX.Y.Z */ import { - Object, SvelteComponent, append, destroy_each, diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index 9e777f25ee..ec28e60d5f 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -1,7 +1,5 @@ /* generated by Svelte vX.Y.Z */ import { - Map, - Object, SvelteComponent, append, create_animation, diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index 7ac66a09a7..efb58ebf6a 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -1,7 +1,5 @@ /* generated by Svelte vX.Y.Z */ import { - Map, - Object, SvelteComponent, append, destroy_block, diff --git a/test/js/samples/head-no-whitespace/expected.js b/test/js/samples/head-no-whitespace/expected.js index 859891e689..b95177bba7 100644 --- a/test/js/samples/head-no-whitespace/expected.js +++ b/test/js/samples/head-no-whitespace/expected.js @@ -3,7 +3,6 @@ import { SvelteComponent, append, detach, - document, element, init, noop, diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index ec30a98161..f45f9ce8db 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -2,12 +2,10 @@ import { SvelteComponent, add_render_callback, - cancelAnimationFrame, detach, element, init, insert, - isNaN, listen, noop, raf, diff --git a/test/js/samples/title/expected.js b/test/js/samples/title/expected.js index 2e551ae426..884f39e246 100644 --- a/test/js/samples/title/expected.js +++ b/test/js/samples/title/expected.js @@ -1,7 +1,6 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, - document, init, noop, safe_not_equal diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 1e63973518..ed76058ee2 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -3,7 +3,6 @@ import { SvelteComponent, add_render_callback, append, - clearTimeout, detach, element, init, @@ -11,10 +10,8 @@ import { listen, noop, safe_not_equal, - setTimeout, set_data, - text, - window + text } from "svelte/internal"; function create_fragment(ctx) { @@ -45,7 +42,7 @@ function create_fragment(ctx) { if (changed.y && !scrolling) { scrolling = true; clearTimeout(scrolling_timeout); - window.scrollTo(window.pageXOffset, ctx.y); + scrollTo(pageXOffset, ctx.y); scrolling_timeout = setTimeout(clear_scrolling, 100); } From 59f04c93c5dda9e05f286911acddc01db1be73d7 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 18 Jun 2019 11:07:00 -0400 Subject: [PATCH 11/24] don't grab page offsets too early --- src/compiler/compile/render-dom/wrappers/Window.ts | 10 +++++----- test/js/samples/window-binding-scroll/expected.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/compile/render-dom/wrappers/Window.ts b/src/compiler/compile/render-dom/wrappers/Window.ts index 8c8e6c623d..c47f12593c 100644 --- a/src/compiler/compile/render-dom/wrappers/Window.ts +++ b/src/compiler/compile/render-dom/wrappers/Window.ts @@ -92,10 +92,10 @@ export default class WindowWrapper extends Wrapper { renderer.meta_bindings.add_block(deindent` if (${condition}) { - @_scrollTo(${x || '@_pageXOffset'}, ${y || '@pageYOffset'}); + @_scrollTo(${x || '@_window.pageXOffset'}, ${y || '@_window.pageYOffset'}); } - ${x && `${x} = @_pageXOffset;`} - ${y && `${y} = @_pageYOffset;`} + ${x && `${x} = @_window.pageXOffset;`} + ${y && `${y} = @_window.pageYOffset;`} `); block.event_listeners.push(deindent` @@ -148,9 +148,9 @@ export default class WindowWrapper extends Wrapper { ${scrolling} = true; @_clearTimeout(${scrolling_timeout}); @_scrollTo(${ - bindings.scrollX ? `ctx.${bindings.scrollX}` : `@_pageXOffset` + bindings.scrollX ? `ctx.${bindings.scrollX}` : `@_window.pageXOffset` }, ${ - bindings.scrollY ? `ctx.${bindings.scrollY}` : `@_pageYOffset` + bindings.scrollY ? `ctx.${bindings.scrollY}` : `@_window.pageYOffset` }); ${scrolling_timeout} = @_setTimeout(${clear_scrolling}, 100); } diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index ed76058ee2..fbe4596e2b 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -42,7 +42,7 @@ function create_fragment(ctx) { if (changed.y && !scrolling) { scrolling = true; clearTimeout(scrolling_timeout); - scrollTo(pageXOffset, ctx.y); + scrollTo(window.pageXOffset, ctx.y); scrolling_timeout = setTimeout(clear_scrolling, 100); } From b424a88a5f8221817218d53ef1e9f41500606279 Mon Sep 17 00:00:00 2001 From: mrkishi Date: Fri, 21 Jun 2019 17:24:03 -0300 Subject: [PATCH 12/24] replace ninja globals --- src/compiler/compile/render-dom/index.ts | 2 +- src/compiler/compile/render-dom/wrappers/EachBlock.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/render-dom/index.ts b/src/compiler/compile/render-dom/index.ts index 3dec1a81b3..17b6fd86f2 100644 --- a/src/compiler/compile/render-dom/index.ts +++ b/src/compiler/compile/render-dom/index.ts @@ -405,7 +405,7 @@ export default function dom( unknown_props_check = deindent` const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`).join(', ')}]; @_Object.keys($$props).forEach(key => { - if (!writable_props.includes(key) && !key.startsWith('$$')) console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); + if (!writable_props.includes(key) && !key.startsWith('$$')) @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); }); `; } diff --git a/src/compiler/compile/render-dom/wrappers/EachBlock.ts b/src/compiler/compile/render-dom/wrappers/EachBlock.ts index 3ce2a342a3..a117a717c5 100644 --- a/src/compiler/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render-dom/wrappers/EachBlock.ts @@ -509,7 +509,7 @@ export default class EachBlockWrapper extends Wrapper { if (outro_block) { block.builders.outro.add_block(deindent` - ${iterations} = ${iterations}.filter(Boolean); + ${iterations} = ${iterations}.filter(@_Boolean); for (let #i = 0; #i < ${view_length}; #i += 1) ${outro_block}(#i, 0, 0);` ); } From 8ed9ac8dbccbe47d64e0bb963fb7f3c43380775e Mon Sep 17 00:00:00 2001 From: Lwazi Dlamini Date: Sun, 23 Jun 2019 19:59:24 +0200 Subject: [PATCH 13/24] 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 7aee021522e7a68434aa1491247ca02b78c348ac Mon Sep 17 00:00:00 2001 From: Lwazi Dlamini Date: Sun, 23 Jun 2019 20:01:21 +0200 Subject: [PATCH 14/24] 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 dddc69ec7f0e5b6bcac991275fdeda4ed873b210 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Jun 2019 11:40:41 -0400 Subject: [PATCH 15/24] 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 86c5086c54481f7572765b73c4a3fe93c79c08d5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Jun 2019 13:00:16 -0400 Subject: [PATCH 16/24] 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 `