diff --git a/mocha.opts b/mocha.opts index 4e8a550a4c..af6b17a845 100644 --- a/mocha.opts +++ b/mocha.opts @@ -1 +1,2 @@ -test/test.js +--bail +test/test.js \ No newline at end of file diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index e7187e309d..ff58a4e947 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -2,6 +2,7 @@ import CodeBuilder from '../../utils/CodeBuilder'; import deindent from '../../utils/deindent'; import { DomGenerator } from './index'; import { Node } from '../../interfaces'; +import shared from './shared'; export interface BlockOptions { name: string; @@ -61,9 +62,6 @@ export default class Block { variables: Map; getUniqueName: (name: string) => string; - component: string; - target: string; - hasUpdateMethod: boolean; autofocus: string; @@ -110,10 +108,6 @@ export default class Block { this.variables = new Map(); this.getUniqueName = this.generator.getUniqueNameMaker(options.params); - // unique names - this.component = this.getUniqueName('component'); - this.target = this.getUniqueName('target'); - this.hasUpdateMethod = false; // determined later } @@ -134,14 +128,12 @@ export default class Block { this.addVariable(name); this.builders.create.addLine(`${name} = ${renderStatement};`); - this.builders.claim.addLine(`${name} = ${claimStatement};`) + this.builders.claim.addLine(`${name} = ${claimStatement};`); this.mount(name, parentNode); if (isToplevel) { - this.builders.unmount.addLine( - `${this.generator.helper('detachNode')}( ${name} );` - ); + this.builders.unmount.addLine(`@detachNode( ${name} );`); } } @@ -186,14 +178,9 @@ export default class Block { mount(name: string, parentNode: string) { if (parentNode) { - this.builders.mount.addLine( - `${this.generator.helper('appendNode')}( ${name}, ${parentNode} );` - ); + this.builders.mount.addLine(`@appendNode( ${name}, ${parentNode} );`); } else { - this.builders.mount.addLine( - `${this.generator.helper('insertNode')}( ${name}, ${this - .target}, anchor );` - ); + this.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`); } } @@ -229,11 +216,11 @@ export default class Block { if (this.first) { properties.addBlock(`first: null,`); - this.builders.hydrate.addLine( `this.first = ${this.first};` ); + this.builders.hydrate.addLine(`this.first = ${this.first};`); } if (this.builders.create.isEmpty()) { - properties.addBlock(`create: ${this.generator.helper('noop')},`); + properties.addBlock(`create: @noop,`); } else { properties.addBlock(deindent` create: function () { @@ -245,7 +232,7 @@ export default class Block { if (this.generator.hydratable) { if (this.builders.claim.isEmpty()) { - properties.addBlock(`claim: ${this.generator.helper('noop')},`); + properties.addBlock(`claim: @noop,`); } else { properties.addBlock(deindent` claim: function ( nodes ) { @@ -265,10 +252,10 @@ export default class Block { } if (this.builders.mount.isEmpty()) { - properties.addBlock(`mount: ${this.generator.helper('noop')},`); + properties.addBlock(`mount: @noop,`); } else { properties.addBlock(deindent` - mount: function ( ${this.target}, anchor ) { + mount: function ( #target, anchor ) { ${this.builders.mount} }, `); @@ -276,7 +263,7 @@ export default class Block { if (this.hasUpdateMethod) { if (this.builders.update.isEmpty()) { - properties.addBlock(`update: ${this.generator.helper('noop')},`); + properties.addBlock(`update: @noop,`); } else { properties.addBlock(deindent` update: function ( changed, ${this.params.join(', ')} ) { @@ -289,20 +276,20 @@ export default class Block { if (this.hasIntroMethod) { if (hasIntros) { properties.addBlock(deindent` - intro: function ( ${this.target}, anchor ) { + intro: function ( #target, anchor ) { if ( ${introing} ) return; ${introing} = true; ${hasOutros && `${outroing} = false;`} ${this.builders.intro} - this.mount( ${this.target}, anchor ); + this.mount( #target, anchor ); }, `); } else { properties.addBlock(deindent` - intro: function ( ${this.target}, anchor ) { - this.mount( ${this.target}, anchor ); + intro: function ( #target, anchor ) { + this.mount( #target, anchor ); }, `); } @@ -331,7 +318,7 @@ export default class Block { } if (this.builders.unmount.isEmpty()) { - properties.addBlock(`unmount: ${this.generator.helper('noop')},`); + properties.addBlock(`unmount: @noop,`); } else { properties.addBlock(deindent` unmount: function () { @@ -341,7 +328,7 @@ export default class Block { } if (this.builders.destroy.isEmpty()) { - properties.addBlock(`destroy: ${this.generator.helper('noop')}`); + properties.addBlock(`destroy: @noop`); } else { properties.addBlock(deindent` destroy: function () { @@ -351,15 +338,16 @@ export default class Block { } return deindent` - function ${this.name} ( ${this.params.join(', ')}, ${this.component}${this - .key + function ${this.name} ( ${this.params.join(', ')}, #component${this.key ? `, ${localKey}` : ''} ) { - ${this.variables.size > 0 && ( - `var ${Array.from(this.variables.keys()).map(key => { - const init = this.variables.get(key); - return init !== undefined ? `${key} = ${init}` : key; - }).join(', ')};`)} + ${this.variables.size > 0 && + `var ${Array.from(this.variables.keys()) + .map(key => { + const init = this.variables.get(key); + return init !== undefined ? `${key} = ${init}` : key; + }) + .join(', ')};`} ${!this.builders.init.isEmpty() && this.builders.init} @@ -367,6 +355,8 @@ export default class Block { ${properties} }; } - `; + `.replace(/(\\)?#(\w*)/g, (match, escaped, name) => { + return escaped ? match.slice(1) : this.alias(name); + }); } } diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index 8921c6cabe..fe0b3857f2 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -4,6 +4,7 @@ import annotateWithScopes from '../../utils/annotateWithScopes'; import isReference from '../../utils/isReference'; import { walk } from 'estree-walker'; import deindent from '../../utils/deindent'; +import stringify from '../../utils/stringify'; import CodeBuilder from '../../utils/CodeBuilder'; import visit from './visit'; import shared from './shared'; @@ -14,7 +15,6 @@ import { Parsed, CompileOptions, Node } from '../../interfaces'; export class DomGenerator extends Generator { blocks: Block[]; - uses: Set; readonly: Set; metaBindings: string[]; @@ -32,7 +32,6 @@ export class DomGenerator extends Generator { ) { super(parsed, source, name, options); this.blocks = []; - this.uses = new Set(); this.readonly = new Set(); @@ -41,16 +40,6 @@ export class DomGenerator extends Generator { // initial values for e.g. window.innerWidth, if there's a <:Window> meta tag this.metaBindings = []; } - - helper(name: string) { - if (this.options.dev && `${name}Dev` in shared) { - name = `${name}Dev`; - } - - this.uses.add(name); - - return this.alias(name); - } } export default function dom( @@ -76,14 +65,10 @@ export default function dom( visit(generator, block, state, node); }); - const builders = { - main: new CodeBuilder(), - _set: new CodeBuilder(), - }; + const builder = new CodeBuilder(); if (computations.length) { - const builder = new CodeBuilder(); - const differs = generator.helper('differs'); + const computationBuilder = new CodeBuilder(); computations.forEach(({ key, deps }) => { if (generator.readonly.has(key)) { @@ -98,26 +83,24 @@ export default function dom( const condition = `isInitial || ${deps .map( dep => - `( '${dep}' in newState && ${differs}( state.${dep}, oldState.${dep} ) )` + `( '${dep}' in newState && @differs( state.${dep}, oldState.${dep} ) )` ) .join(' || ')}`; - const statement = `state.${key} = newState.${key} = ${generator.alias( - 'template' - )}.computed.${key}( ${deps.map(dep => `state.${dep}`).join(', ')} );`; + const statement = `state.${key} = newState.${key} = @template.computed.${key}( ${deps + .map(dep => `state.${dep}`) + .join(', ')} );`; - builder.addConditionalLine(condition, statement); + computationBuilder.addConditionalLine(condition, statement); }); - builders.main.addBlock(deindent` - function ${generator.alias( - 'recompute' - )} ( state, newState, oldState, isInitial ) { - ${builder} + builder.addBlock(deindent` + function @recompute ( state, newState, oldState, isInitial ) { + ${computationBuilder} } `); } - builders._set.addBlock(deindent` + const _set = deindent` ${options.dev && deindent` if ( typeof newState !== 'object' ) { @@ -131,43 +114,35 @@ export default function dom( `} var oldState = this._state; - this._state = ${generator.helper('assign')}( {}, oldState, newState ); + this._state = @assign( {}, oldState, newState ); ${computations.length && - `${generator.alias( - 'recompute' - )}( this._state, newState, oldState, false )`} - ${generator.helper( - 'dispatchObservers' - )}( this, this._observers.pre, newState, oldState ); + `@recompute( this._state, newState, oldState, false )`} + @dispatchObservers( this, this._observers.pre, newState, oldState ); ${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`} - ${generator.helper( - 'dispatchObservers' - )}( this, this._observers.post, newState, oldState ); + @dispatchObservers( this, this._observers.post, newState, oldState ); ${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`} ${(generator.hasComponents || generator.hasIntroTransitions) && `this._flush();`} - `); + `; if (hasJs) { - builders.main.addBlock( - `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` - ); + builder.addBlock(`[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`); } if (generator.css && options.css !== false) { - builders.main.addBlock(deindent` - function ${generator.alias('add_css')} () { - var style = ${generator.helper('createElement')}( 'style' ); - style.id = ${JSON.stringify(generator.cssId + '-style')}; - style.textContent = ${JSON.stringify(generator.css)}; - ${generator.helper('appendNode')}( style, document.head ); + builder.addBlock(deindent` + function @add_css () { + var style = @createElement( 'style' ); + style.id = '${generator.cssId}-style'; + style.textContent = ${stringify(generator.css)}; + @appendNode( style, document.head ); } `); } generator.blocks.forEach(block => { - builders.main.addBlock(block.render()); + builder.addBlock(block.render()); }); const sharedPath = options.shared === true @@ -176,35 +151,28 @@ export default function dom( const prototypeBase = `${name}.prototype` + - (templateProperties.methods - ? `, ${generator.alias('template')}.methods` - : ''); + (templateProperties.methods ? `, @template.methods` : ''); const proto = sharedPath - ? `${generator.helper('proto')} ` + ? `@proto ` : deindent` { ${['get', 'fire', 'observe', 'on', 'set', '_flush'] - .map(n => `${n}: ${generator.helper(n)}`) + .map(n => `${n}: @${n}`) .join(',\n')} }`; // TODO deprecate component.teardown() - builders.main.addBlock(deindent` + builder.addBlock(deindent` function ${name} ( options ) { options = options || {}; ${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`} ${generator.usesRefs && `this.refs = {};`} this._state = ${templateProperties.data - ? `${generator.helper('assign')}( ${generator.alias( - 'template' - )}.data(), options.data )` + ? `@assign( @template.data(), options.data )` : `options.data || {}`}; ${generator.metaBindings} - ${computations.length && - `${generator.alias( - 'recompute' - )}( this._state, this._state, {}, true );`} + ${computations.length && `@recompute( this._state, this._state, {}, true );`} ${options.dev && Array.from(generator.expectedProperties).map( prop => @@ -228,23 +196,19 @@ export default function dom( this._torndown = false; ${generator.css && options.css !== false && - `if ( !document.getElementById( ${JSON.stringify( - generator.cssId + '-style' - )} ) ) ${generator.alias('add_css')}();`} + `if ( !document.getElementById( '${generator.cssId}-style' ) ) @add_css();`} ${(generator.hasComponents || generator.hasIntroTransitions) && `this._renderHooks = [];`} ${generator.hasComplexBindings && `this._bindings = [];`} - this._fragment = ${generator.alias( - 'create_main_fragment' - )}( this._state, this ); + this._fragment = @create_main_fragment( this._state, this ); if ( options.target ) { - ${generator.hydratable ? - deindent` - var nodes = ${generator.helper('children')}( options.target ); + ${generator.hydratable + ? deindent` + var nodes = @children( options.target ); options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create(); - nodes.forEach( ${generator.helper('detachNode')} ); + nodes.forEach( @detachNode ); ` : deindent` ${options.dev && `if ( options.hydrate ) throw new Error( 'options.hydrate only works if the component was compiled with the \`hydratable: true\` option' );`} @@ -261,25 +225,22 @@ export default function dom( ${templateProperties.oncreate && deindent` if ( options._root ) { - options._root._renderHooks.push( ${generator.alias( - 'template' - )}.oncreate.bind( this ) ); + options._root._renderHooks.push( @template.oncreate.bind( this ) ); } else { - ${generator.alias('template')}.oncreate.call( this ); + @template.oncreate.call( this ); } `} } - ${generator.helper('assign')}( ${prototypeBase}, ${proto}); + @assign( ${prototypeBase}, ${proto}); ${name}.prototype._set = function _set ( newState ) { - ${builders._set} + ${_set} }; ${name}.prototype.teardown = ${name}.prototype.destroy = function destroy ( detach ) { this.fire( 'destroy' ); - ${templateProperties.ondestroy && - `${generator.alias('template')}.ondestroy.call( this );`} + ${templateProperties.ondestroy && `@template.ondestroy.call( this );`} if ( detach !== false ) this._fragment.unmount(); this._fragment.destroy(); @@ -290,6 +251,21 @@ export default function dom( }; `); + const usedHelpers = new Set(); + + let result = builder + .toString() + .replace(/(\\)?@(\w*)/g, (match: string, escaped: string, name: string) => { + if (escaped) return match.slice(1); + + if (name in shared) { + if (options.dev && `${name}Dev` in shared) name = `${name}Dev`; + usedHelpers.add(name); + } + + return generator.alias(name); + }); + if (sharedPath) { if (format !== 'es') { throw new Error( @@ -297,17 +273,17 @@ export default function dom( ); } - const names = Array.from(generator.uses).sort().map(name => { + const names = Array.from(usedHelpers).sort().map(name => { return name !== generator.alias(name) ? `${name} as ${generator.alias(name)}` : name; }); - builders.main.addLineAtStart( - `import { ${names.join(', ')} } from ${JSON.stringify(sharedPath)};` - ); + result = + `import { ${names.join(', ')} } from ${stringify(sharedPath)};\n\n` + + result; } else { - generator.uses.forEach(key => { + usedHelpers.forEach(key => { const str = shared[key]; const code = new MagicString(str); const expression = parseExpressionAt(str, 0); @@ -326,7 +302,7 @@ export default function dom( if (node.name in shared) { // this helper function depends on another one const dependency = node.name; - generator.uses.add(dependency); + usedHelpers.add(dependency); const alias = generator.alias(dependency); if (alias !== node.name) @@ -344,22 +320,20 @@ export default function dom( // special case const global = `_svelteTransitionManager`; - builders.main.addBlock( - `var ${generator.alias( - 'transitionManager' - )} = window.${global} || ( window.${global} = ${code});` - ); + result += `\n\nvar ${generator.alias( + 'transitionManager' + )} = window.${global} || ( window.${global} = ${code});`; } else { const alias = generator.alias(expression.id.name); if (alias !== expression.id.name) code.overwrite(expression.id.start, expression.id.end, alias); - builders.main.addBlock(code.toString()); + result += `\n\n${code}`; } }); } - return generator.generate(builders.main.toString(), options, { + return generator.generate(result, options, { name, format, }); diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index dc3ff59717..9e46a8869b 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -12,7 +12,12 @@ function isElseIf(node: Node) { } function getChildState(parent: State, child = {}) { - return assign({}, parent, { name: null, parentNode: null, parentNodes: 'nodes' }, child || {}); + return assign( + {}, + parent, + { name: null, parentNode: null, parentNodes: 'nodes' }, + child || {} + ); } // Whitespace inside one of these elements will not result in @@ -225,7 +230,11 @@ const preprocessors = { block.addDependencies(dependencies); // special case —