diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 89c1e6b4f6f8..c4924697f81e 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -101,7 +101,8 @@ 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(); + globals: Map = new Map(); indirect_dependencies: Map> = new Map(); @@ -233,8 +234,15 @@ 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; + } + + global(name: string) { + const alias = this.alias(name); + this.globals.set(name, alias); + return alias; } generate(result: string) { @@ -251,23 +259,29 @@ 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 (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}'`); + } + + 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 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( result, @@ -276,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 54b891d4ac1b..79cb33549c48 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 0c585c5bf0ae..d7986e167b2d 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 9a49bc0c351c..e643b40a33e0 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); } `); } @@ -57,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");' ); } @@ -106,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 ''"); } `); } @@ -122,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 ''"); } `); } @@ -145,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}'"); }`)} `; } @@ -402,8 +404,8 @@ 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 => { - if (!writable_props.includes(key) && !key.startsWith('$$')) console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); + @_Object.keys($$props).forEach(key => { + if (!writable_props.includes(key) && !key.startsWith('$$')) @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); }); `; } @@ -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/AwaitBlock.ts b/src/compiler/compile/render-dom/wrappers/AwaitBlock.ts index 346dbe9ca0f0..321f7fd9ffc3 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/Body.ts b/src/compiler/compile/render-dom/wrappers/Body.ts index 04e677a09024..623625ce6d36 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 713113921dfe..6705b51cc557 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 c26b58c35610..9e4f0689fa49 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; @@ -498,7 +498,7 @@ export default class EachBlockWrapper extends Wrapper { if (this.block.has_outros) { block.builders.outro.add_block(deindent` - ${iterations} = ${iterations}.filter(Boolean); + ${iterations} = ${iterations}.filter(@_Boolean); for (let #i = 0; #i < ${view_length}; #i += 1) @transition_out(${iterations}[#i]);` ); } diff --git a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts index a96b44f7f621..1b58751e9f14 100644 --- a/src/compiler/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render-dom/wrappers/Element/Binding.ts @@ -144,7 +144,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 9db034092b43..842201381c1f 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 { @@ -381,7 +381,7 @@ export default class ElementWrapper extends Wrapper { } if (namespace) { - return `document.createElementNS("${namespace}", "${name}")`; + return `@_document.createElementNS("${namespace}", "${name}")`; } return `@element("${name}")`; @@ -467,7 +467,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 d942a69dc27b..a5226e7efa2d 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 aa0a7aef99ce..a783364d030b 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 15a7d41861b5..f85c48935e68 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 456a833a8f29..302c9f1aa960 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 585864664aed..c47f12593c3c 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 || '@_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}); + @_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/InlineComponent.ts b/src/compiler/compile/render-ssr/handlers/InlineComponent.ts index 94fdcfd434b9..320bf5e6a088 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 000000000000..664093d2e4fd --- /dev/null +++ b/src/runtime/internal/globals.ts @@ -0,0 +1,3 @@ +declare const global: any; + +export const globals = (typeof window !== 'undefined' ? window : global) as unknown as typeof globalThis; diff --git a/src/runtime/internal/index.ts b/src/runtime/internal/index.ts index d9d95541ebc3..54f871ad11a3 100644 --- a/src/runtime/internal/index.ts +++ b/src/runtime/internal/index.ts @@ -2,6 +2,7 @@ export * from './animations'; export * from './await-block'; export * from './dom'; export * from './environment'; +export * from './globals'; export * from './keyed-each'; export * from './lifecycle'; export * from './loop'; diff --git a/test/helpers.js b/test/helpers.js index e07d7c9b0676..514d0846980d 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,21 @@ 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'}; +global.requestAnimationFrame = null; // placeholder, filled in using set_raf + +// 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 +127,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/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 18f5210bea7e..fbe4596e2b1f 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); - window.scrollTo(window.pageXOffset, ctx.y); + scrollTo(window.pageXOffset, ctx.y); scrolling_timeout = setTimeout(clear_scrolling, 100); } diff --git a/test/runtime/index.js b/test/runtime/index.js index 77879edc1fb6..87be528a61a3 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,8 +115,6 @@ describe("runtime", () => { throw err; } - global.window = window; - if (config.before_test) config.before_test(); // Put things we need on window for testing @@ -221,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 000000000000..c29f022a37d5 --- /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 000000000000..d1928ea5335d --- /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 7759941dbb6c..cb89b3e9d1ad 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); });