diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index fbee4037c4..2eef65c5db 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -17,7 +17,7 @@ import getName from '../utils/getName'; import Stylesheet from '../css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; -import shared from './dom/shared'; // TODO move this file +import shared from './shared'; import { Node, GenerateOptions, ShorthandImport, Ast, CompileOptions, CustomElementOptions } from '../interfaces'; interface Computation { @@ -193,6 +193,8 @@ export default class Generator { this.fragment = new Fragment(this, ast.html); // this.walkTemplate(); if (!this.customElement) this.stylesheet.reify(); + + stylesheet.warnOnUnusedSelectors(options.onwarn); } addSourcemapLocations(node: Node) { @@ -212,9 +214,29 @@ export default class Generator { return this.aliases.get(name); } - generate(result: string, options: CompileOptions, { banner = '', helpers, name, format }: GenerateOptions ) { + generate(result: string, options: CompileOptions, { banner = '', name, format }: GenerateOptions ) { const pattern = /\[✂(\d+)-(\d+)$/; + const helpers = new Set(); + + // TODO use same regex for both + result = result.replace(options.generate === 'ssr' ? /(@+|#+|%+)(\w*(?:-\w*)?)/g : /(%+|@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => { + if (sigil === '@') { + if (name in shared) { + if (options.dev && `${name}Dev` in shared) name = `${name}Dev`; + helpers.add(name); + } + + return this.alias(name); + } + + if (sigil === '%') { + return this.templateVars.get(name); + } + + return sigil.slice(1) + name; + }); + let importedHelpers; if (options.shared) { @@ -233,8 +255,8 @@ export default class Generator { importedHelpers = []; - helpers.forEach(key => { - const str = shared[key]; + helpers.forEach(name => { + const str = shared[name]; const code = new MagicString(str); const expression = parseExpressionAt(str, 0); @@ -267,14 +289,14 @@ export default class Generator { }, }); - if (key === 'transitionManager') { + if (name === 'transitionManager') { // special case const global = `_svelteTransitionManager`; inlineHelpers += `\n\nvar ${this.alias('transitionManager')} = window.${global} || (window.${global} = ${code});\n\n`; - } else if (key === 'escaped' || key === 'missingComponent') { + } else if (name === 'escaped' || name === 'missingComponent') { // vars are an awkward special case... would be nice to avoid this - const alias = this.alias(key); + const alias = this.alias(name); inlineHelpers += `\n\nconst ${alias} = ${code};` } else { const alias = this.alias(expression.id.name); diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 30545d0a70..9ce31dcc54 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -3,7 +3,6 @@ import deindent from '../../utils/deindent'; import { escape } from '../../utils/stringify'; import { DomGenerator } from './index'; import { Node } from '../../interfaces'; -import shared from './shared'; export interface BlockOptions { name: string; diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index b3ae1581d8..e8d9355200 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -76,8 +76,6 @@ export default function dom( // prevent fragment being created twice (#1063) if (options.customElement) block.builders.create.addLine(`this.c = @noop;`); - generator.stylesheet.warnOnUnusedSelectors(options.onwarn); - const builder = new CodeBuilder(); const computationBuilder = new CodeBuilder(); const computationDeps = new Set(); @@ -361,26 +359,7 @@ export default function dom( ${immutable && `${name}.prototype._differs = @_differsImmutable;`} `); - const helpers = new Set(); - - let result = builder - .toString() - .replace(/(%+|@+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => { - if (sigil === '@') { - if (name in shared) { - if (options.dev && `${name}Dev` in shared) name = `${name}Dev`; - helpers.add(name); - } - - return generator.alias(name); - } - - if (sigil === '%') { - return generator.templateVars.get(name); - } - - return sigil.slice(1) + name; - }); + let result = builder.toString(); const filename = options.filename && ( typeof process !== 'undefined' ? options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : options.filename @@ -389,7 +368,6 @@ export default function dom( return generator.generate(result, options, { banner: `/* ${filename ? `${filename} ` : ``}generated by Svelte v${"__VERSION__"} */`, sharedPath, - helpers, name, format, }); diff --git a/src/generators/nodes/EachBlock.ts b/src/generators/nodes/EachBlock.ts index 41ba3a5251..9db8c7dc39 100644 --- a/src/generators/nodes/EachBlock.ts +++ b/src/generators/nodes/EachBlock.ts @@ -5,7 +5,7 @@ import Block from '../dom/Block'; import createDebuggingComment from '../../utils/createDebuggingComment'; import Expression from './shared/Expression'; import mapChildren from './shared/mapChildren'; -import TemplateScope from '../dom/TemplateScope'; +import TemplateScope from './shared/TemplateScope'; export default class EachBlock extends Node { type: 'EachBlock'; diff --git a/src/generators/nodes/Fragment.ts b/src/generators/nodes/Fragment.ts index 4b2025f8d6..b9710c2a7b 100644 --- a/src/generators/nodes/Fragment.ts +++ b/src/generators/nodes/Fragment.ts @@ -3,7 +3,7 @@ import { DomGenerator } from '../dom/index'; import Generator from '../Generator'; import mapChildren from './shared/mapChildren'; import Block from '../dom/Block'; -import TemplateScope from '../dom/TemplateScope'; +import TemplateScope from './shared/TemplateScope'; export default class Fragment extends Node { block: Block; diff --git a/src/generators/dom/TemplateScope.ts b/src/generators/nodes/shared/TemplateScope.ts similarity index 100% rename from src/generators/dom/TemplateScope.ts rename to src/generators/nodes/shared/TemplateScope.ts diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts index 04432cd4dc..fb378e05eb 100644 --- a/src/generators/server-side-rendering/index.ts +++ b/src/generators/server-side-rendering/index.ts @@ -27,8 +27,6 @@ export class SsrGenerator extends Generator { this.bindings = []; this.renderCode = ''; this.appendTargets = []; - - this.stylesheet.warnOnUnusedSelectors(options.onwarn); } append(code: string) { @@ -157,17 +155,9 @@ export default function ssr( var warned = false; ${templateProperties.preload && `${name}.preload = %preload;`} - `.replace(/(@+|#+|%+)(\w*(?:-\w*)?)/g, (match: string, sigil: string, name: string) => { - if (sigil === '@') { - helpers.add(name); - return generator.alias(name); - } - - if (sigil === '%') return generator.templateVars.get(name); - return sigil.slice(1) + name; - }); + `; - return generator.generate(result, options, { name, format, helpers }); + return generator.generate(result, options, { name, format }); } function trim(nodes) { diff --git a/src/generators/shared.ts b/src/generators/shared.ts new file mode 100644 index 0000000000..fc7b83ecdc --- /dev/null +++ b/src/generators/shared.ts @@ -0,0 +1,79 @@ +// this file is auto-generated, do not edit it +const shared: Record = { + "appendNode": "function appendNode(node, target) {\n\ttarget.appendChild(node);\n}", + "insertNode": "function insertNode(node, target, anchor) {\n\ttarget.insertBefore(node, anchor);\n}", + "detachNode": "function detachNode(node) {\n\tnode.parentNode.removeChild(node);\n}", + "detachBetween": "function detachBetween(before, after) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}", + "detachBefore": "function detachBefore(after) {\n\twhile (after.previousSibling) {\n\t\tafter.parentNode.removeChild(after.previousSibling);\n\t}\n}", + "detachAfter": "function detachAfter(before) {\n\twhile (before.nextSibling) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}", + "reinsertBetween": "function reinsertBetween(before, after, target) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\ttarget.appendChild(before.parentNode.removeChild(before.nextSibling));\n\t}\n}", + "reinsertChildren": "function reinsertChildren(parent, target) {\n\twhile (parent.firstChild) target.appendChild(parent.firstChild);\n}", + "reinsertAfter": "function reinsertAfter(before, target) {\n\twhile (before.nextSibling) target.appendChild(before.nextSibling);\n}", + "reinsertBefore": "function reinsertBefore(after, target) {\n\tvar parent = after.parentNode;\n\twhile (parent.firstChild !== after) target.appendChild(parent.firstChild);\n}", + "destroyEach": "function destroyEach(iterations) {\n\tfor (var i = 0; i < iterations.length; i += 1) {\n\t\tif (iterations[i]) iterations[i].d();\n\t}\n}", + "createFragment": "function createFragment() {\n\treturn document.createDocumentFragment();\n}", + "createElement": "function createElement(name) {\n\treturn document.createElement(name);\n}", + "createSvgElement": "function createSvgElement(name) {\n\treturn document.createElementNS('http://www.w3.org/2000/svg', name);\n}", + "createText": "function createText(data) {\n\treturn document.createTextNode(data);\n}", + "createComment": "function createComment() {\n\treturn document.createComment('');\n}", + "addListener": "function addListener(node, event, handler) {\n\tnode.addEventListener(event, handler, false);\n}", + "removeListener": "function removeListener(node, event, handler) {\n\tnode.removeEventListener(event, handler, false);\n}", + "setAttribute": "function setAttribute(node, attribute, value) {\n\tnode.setAttribute(attribute, value);\n}", + "setAttributes": "function setAttributes(node, attributes) {\n\tfor (var key in attributes) {\n\t\tif (key in node) {\n\t\t\tnode[key] = attributes[key];\n\t\t} else {\n\t\t\tif (attributes[key] === undefined) removeAttribute(node, key);\n\t\t\telse setAttribute(node, key, attributes[key]);\n\t\t}\n\t}\n}", + "removeAttribute": "function removeAttribute(node, attribute) {\n\tnode.removeAttribute(attribute);\n}", + "setXlinkAttribute": "function setXlinkAttribute(node, attribute, value) {\n\tnode.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);\n}", + "getBindingGroupValue": "function getBindingGroupValue(group) {\n\tvar value = [];\n\tfor (var i = 0; i < group.length; i += 1) {\n\t\tif (group[i].checked) value.push(group[i].__value);\n\t}\n\treturn value;\n}", + "toNumber": "function toNumber(value) {\n\treturn value === '' ? undefined : +value;\n}", + "timeRangesToArray": "function timeRangesToArray(ranges) {\n\tvar array = [];\n\tfor (var i = 0; i < ranges.length; i += 1) {\n\t\tarray.push({ start: ranges.start(i), end: ranges.end(i) });\n\t}\n\treturn array;\n}", + "children": "function children (element) {\n\treturn Array.from(element.childNodes);\n}", + "claimElement": "function claimElement (nodes, name, attributes, svg) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeName === name) {\n\t\t\tfor (var j = 0; j < node.attributes.length; j += 1) {\n\t\t\t\tvar attribute = node.attributes[j];\n\t\t\t\tif (!attributes[attribute.name]) node.removeAttribute(attribute.name);\n\t\t\t}\n\t\t\treturn nodes.splice(i, 1)[0]; // TODO strip unwanted attributes\n\t\t}\n\t}\n\n\treturn svg ? createSvgElement(name) : createElement(name);\n}", + "claimText": "function claimText (nodes, data) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeType === 3) {\n\t\t\tnode.data = data;\n\t\t\treturn nodes.splice(i, 1)[0];\n\t\t}\n\t}\n\n\treturn createText(data);\n}", + "setInputType": "function setInputType(input, type) {\n\ttry {\n\t\tinput.type = type;\n\t} catch (e) {}\n}", + "setStyle": "function setStyle(node, key, value) {\n\tnode.style.setProperty(key, value);\n}", + "selectOption": "function selectOption(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\n\t\tif (option.__value === value) {\n\t\t\toption.selected = true;\n\t\t\treturn;\n\t\t}\n\t}\n}", + "selectOptions": "function selectOptions(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\t\toption.selected = ~value.indexOf(option.__value);\n\t}\n}", + "selectValue": "function selectValue(select) {\n\tvar selectedOption = select.querySelector(':checked') || select.options[0];\n\treturn selectedOption && selectedOption.__value;\n}", + "selectMultipleValue": "function selectMultipleValue(select) {\n\treturn [].map.call(select.querySelectorAll(':checked'), function(option) {\n\t\treturn option.__value;\n\t});\n}", + "blankObject": "function blankObject() {\n\treturn Object.create(null);\n}", + "destroy": "function destroy(detach) {\n\tthis.destroy = noop;\n\tthis.fire('destroy');\n\tthis.set = noop;\n\n\tif (detach !== false) this._fragment.u();\n\tthis._fragment.d();\n\tthis._fragment = null;\n\tthis._state = {};\n}", + "destroyDev": "function destroyDev(detach) {\n\tdestroy.call(this, detach);\n\tthis.destroy = function() {\n\t\tconsole.warn('Component was already destroyed');\n\t};\n}", + "_differs": "function _differs(a, b) {\n\treturn a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}", + "_differsImmutable": "function _differsImmutable(a, b) {\n\treturn a != a ? b == b : a !== b;\n}", + "fire": "function fire(eventName, data) {\n\tvar handlers =\n\t\teventName in this._handlers && this._handlers[eventName].slice();\n\tif (!handlers) return;\n\n\tfor (var i = 0; i < handlers.length; i += 1) {\n\t\tvar handler = handlers[i];\n\n\t\tif (!handler.__calling) {\n\t\t\thandler.__calling = true;\n\t\t\thandler.call(this, data);\n\t\t\thandler.__calling = false;\n\t\t}\n\t}\n}", + "get": "function get() {\n\treturn this._state;\n}", + "init": "function init(component, options) {\n\tcomponent._handlers = blankObject();\n\tcomponent._bind = options._bind;\n\n\tcomponent.options = options;\n\tcomponent.root = options.root || component;\n\tcomponent.store = component.root.store || options.store;\n}", + "on": "function on(eventName, handler) {\n\tvar handlers = this._handlers[eventName] || (this._handlers[eventName] = []);\n\thandlers.push(handler);\n\n\treturn {\n\t\tcancel: function() {\n\t\t\tvar index = handlers.indexOf(handler);\n\t\t\tif (~index) handlers.splice(index, 1);\n\t\t}\n\t};\n}", + "run": "function run(fn) {\n\tfn();\n}", + "set": "function set(newState) {\n\tthis._set(assign({}, newState));\n\tif (this.root._lock) return;\n\tthis.root._lock = true;\n\tcallAll(this.root._beforecreate);\n\tcallAll(this.root._oncreate);\n\tcallAll(this.root._aftercreate);\n\tthis.root._lock = false;\n}", + "_set": "function _set(newState) {\n\tvar oldState = this._state,\n\t\tchanged = {},\n\t\tdirty = false;\n\n\tfor (var key in newState) {\n\t\tif (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;\n\t}\n\tif (!dirty) return;\n\n\tthis._state = assign(assign({}, oldState), newState);\n\tthis._recompute(changed, this._state);\n\tif (this._bind) this._bind(changed, this._state);\n\n\tif (this._fragment) {\n\t\tthis.fire(\"state\", { changed: changed, current: this._state, previous: oldState });\n\t\tthis._fragment.p(changed, this._state);\n\t\tthis.fire(\"update\", { changed: changed, current: this._state, previous: oldState });\n\t}\n}", + "setDev": "function setDev(newState) {\n\tif (typeof newState !== 'object') {\n\t\tthrow new Error(\n\t\t\tthis._debugName + '.set was called without an object of data key-values to update.'\n\t\t);\n\t}\n\n\tthis._checkReadOnly(newState);\n\tset.call(this, newState);\n}", + "callAll": "function callAll(fns) {\n\twhile (fns && fns.length) fns.shift()();\n}", + "_mount": "function _mount(target, anchor) {\n\tthis._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);\n}", + "_unmount": "function _unmount() {\n\tif (this._fragment) this._fragment.u();\n}", + "isPromise": "function isPromise(value) {\n\treturn value && typeof value.then === 'function';\n}", + "PENDING": "{}", + "SUCCESS": "{}", + "FAILURE": "{}", + "removeFromStore": "function removeFromStore() {\n\tthis.store._remove(this);\n}", + "proto": "{\n\tdestroy,\n\tget,\n\tfire,\n\ton,\n\tset,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}", + "protoDev": "{\n\tdestroy: destroyDev,\n\tget,\n\tfire,\n\ton,\n\tset: setDev,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}", + "destroyBlock": "function destroyBlock(block, lookup) {\n\tblock.u();\n\tblock.d();\n\tlookup[block.key] = null;\n}", + "outroAndDestroyBlock": "function outroAndDestroyBlock(block, lookup) {\n\tblock.o(function() {\n\t\tdestroyBlock(block, lookup);\n\t});\n}", + "updateKeyedEach": "function updateKeyedEach(old_blocks, component, changed, key_prop, dynamic, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) {\n\tvar o = old_blocks.length;\n\tvar n = list.length;\n\n\tvar i = o;\n\tvar old_indexes = {};\n\twhile (i--) old_indexes[old_blocks[i].key] = i;\n\n\tvar new_blocks = [];\n\tvar new_lookup = {};\n\tvar deltas = {};\n\n\tvar i = n;\n\twhile (i--) {\n\t\tvar key = list[i][key_prop];\n\t\tvar block = lookup[key];\n\n\t\tif (!block) {\n\t\t\tblock = create_each_block(component, key, get_context(i));\n\t\t\tblock.c();\n\t\t} else if (dynamic) {\n\t\t\tblock.p(changed, get_context(i));\n\t\t}\n\n\t\tnew_blocks[i] = new_lookup[key] = block;\n\n\t\tif (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]);\n\t}\n\n\tvar will_move = {};\n\tvar did_move = {};\n\n\tvar destroy = has_outro ? outroAndDestroyBlock : destroyBlock;\n\n\tfunction insert(block) {\n\t\tblock[intro_method](node, next);\n\t\tlookup[block.key] = block;\n\t\tnext = block.first;\n\t\tn--;\n\t}\n\n\twhile (o && n) {\n\t\tvar new_block = new_blocks[n - 1];\n\t\tvar old_block = old_blocks[o - 1];\n\t\tvar new_key = new_block.key;\n\t\tvar old_key = old_block.key;\n\n\t\tif (new_block === old_block) {\n\t\t\t// do nothing\n\t\t\tnext = new_block.first;\n\t\t\to--;\n\t\t\tn--;\n\t\t}\n\n\t\telse if (!new_lookup[old_key]) {\n\t\t\t// remove old block\n\t\t\tdestroy(old_block, lookup);\n\t\t\to--;\n\t\t}\n\n\t\telse if (!lookup[new_key] || will_move[new_key]) {\n\t\t\tinsert(new_block);\n\t\t}\n\n\t\telse if (did_move[old_key]) {\n\t\t\to--;\n\n\t\t} else if (deltas[new_key] > deltas[old_key]) {\n\t\t\tdid_move[new_key] = true;\n\t\t\tinsert(new_block);\n\n\t\t} else {\n\t\t\twill_move[old_key] = true;\n\t\t\to--;\n\t\t}\n\t}\n\n\twhile (o--) {\n\t\tvar old_block = old_blocks[o];\n\t\tif (!new_lookup[old_block.key]) destroy(old_block, lookup);\n\t}\n\n\twhile (n) insert(new_blocks[n - 1]);\n\n\treturn new_blocks;\n}", + "getSpreadUpdate": "function getSpreadUpdate(levels, updates) {\n\tvar update = {};\n\n\tvar to_null_out = {};\n\tvar accounted_for = {};\n\n\tvar i = levels.length;\n\twhile (i--) {\n\t\tvar o = levels[i];\n\t\tvar n = updates[i];\n\n\t\tif (n) {\n\t\t\tfor (var key in o) {\n\t\t\t\tif (!(key in n)) to_null_out[key] = 1;\n\t\t\t}\n\n\t\t\tfor (var key in n) {\n\t\t\t\tif (!accounted_for[key]) {\n\t\t\t\t\tupdate[key] = n[key];\n\t\t\t\t\taccounted_for[key] = 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlevels[i] = n;\n\t\t} else {\n\t\t\tfor (var key in o) {\n\t\t\t\taccounted_for[key] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (var key in to_null_out) {\n\t\tif (!(key in update)) update[key] = undefined;\n\t}\n\n\treturn update;\n}", + "spread": "function spread(args) {\n\tconst attributes = Object.assign({}, ...args);\n\tlet str = '';\n\n\tObject.keys(attributes).forEach(name => {\n\t\tconst value = attributes[name];\n\t\tif (value === undefined) return;\n\t\tif (value === true) str += \" \" + name;\n\t\tstr += \" \" + name + \"=\" + JSON.stringify(value);\n\t});\n\n\treturn str;\n}", + "escaped": "{\n\t'\"': '"',\n\t\"'\": ''',\n\t'&': '&',\n\t'<': '<',\n\t'>': '>'\n}", + "escape": "function escape(html) {\n\treturn String(html).replace(/[\"'&<>]/g, match => escaped[match]);\n}", + "each": "function each(items, assign, fn) {\n\tlet str = '';\n\tfor (let i = 0; i < items.length; i += 1) {\n\t\tstr += fn(assign(items[i], i));\n\t}\n\treturn str;\n}", + "missingComponent": "{\n\t_render: () => ''\n}", + "linear": "function linear(t) {\n\treturn t;\n}", + "generateRule": "function generateRule(\n\ta,\n\tb,\n\tdelta,\n\tduration,\n\tease,\n\tfn\n) {\n\tvar keyframes = '{\\n';\n\n\tfor (var p = 0; p <= 1; p += 16.666 / duration) {\n\t\tvar t = a + delta * ease(p);\n\t\tkeyframes += p * 100 + '%{' + fn(t) + '}\\n';\n\t}\n\n\treturn keyframes + '100% {' + fn(b) + '}\\n}';\n}", + "hash": "function hash(str) {\n\tvar hash = 5381;\n\tvar i = str.length;\n\n\twhile (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);\n\treturn hash >>> 0;\n}", + "wrapTransition": "function wrapTransition(component, node, fn, params, intro, outgroup) {\n\tvar obj = fn(node, params);\n\tvar duration = obj.duration || 300;\n\tvar ease = obj.easing || linear;\n\tvar cssText;\n\n\t// TODO share