From 84595fb38192ef00dcad44676e64155ba85f26af Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 2 Jun 2017 21:57:25 -0400 Subject: [PATCH] run prettier on src, update tests --- src/generators/Generator.ts | 490 +++++++++++------- src/generators/dom/Block.ts | 208 ++++---- src/generators/dom/index.ts | 298 +++++++---- src/generators/dom/interfaces.ts | 2 +- src/generators/dom/preprocess.ts | 314 ++++++----- src/generators/dom/visit.ts | 13 +- .../dom/visitors/Component/Attribute.ts | 68 +-- .../dom/visitors/Component/Binding.ts | 70 ++- .../dom/visitors/Component/Component.ts | 164 +++--- .../dom/visitors/Component/EventHandler.ts | 45 +- src/generators/dom/visitors/Component/Ref.ts | 15 +- src/generators/dom/visitors/EachBlock.ts | 281 ++++++---- .../dom/visitors/Element/Attribute.ts | 154 +++--- .../dom/visitors/Element/Binding.ts | 231 ++++++--- .../dom/visitors/Element/Element.ts | 133 +++-- .../dom/visitors/Element/EventHandler.ts | 96 ++-- src/generators/dom/visitors/Element/Ref.ts | 14 +- .../dom/visitors/Element/addTransitions.ts | 79 +-- .../Element/getStaticAttributeValue.ts | 14 +- src/generators/dom/visitors/Element/lookup.ts | 313 +++++++---- .../dom/visitors/Element/meta/Window.ts | 131 +++-- src/generators/dom/visitors/IfBlock.ts | 308 ++++++----- src/generators/dom/visitors/MustacheTag.ts | 26 +- src/generators/dom/visitors/RawMustacheTag.ts | 47 +- src/generators/dom/visitors/Text.ts | 18 +- src/generators/dom/visitors/YieldTag.ts | 10 +- .../dom/visitors/shared/binding/getSetter.ts | 40 +- src/generators/server-side-rendering/Block.ts | 34 +- src/generators/server-side-rendering/index.ts | 75 ++- src/generators/server-side-rendering/visit.ts | 12 +- .../server-side-rendering/visitors/Comment.ts | 2 +- .../visitors/Component.ts | 73 +-- .../visitors/EachBlock.ts | 36 +- .../server-side-rendering/visitors/Element.ts | 62 ++- .../server-side-rendering/visitors/IfBlock.ts | 28 +- .../visitors/MustacheTag.ts | 12 +- .../visitors/RawMustacheTag.ts | 12 +- .../server-side-rendering/visitors/Text.ts | 10 +- .../visitors/YieldTag.ts | 6 +- .../visitors/meta/Window.ts | 4 +- src/generators/shared/processCss.ts | 113 ++-- src/generators/shared/utils/getGlobals.ts | 48 +- src/generators/shared/utils/getIntro.ts | 101 ++-- src/generators/shared/utils/getOutro.ts | 29 +- src/generators/shared/utils/walkHtml.ts | 20 +- src/index.ts | 67 +-- src/interfaces.ts | 14 +- src/parse/index.ts | 124 +++-- src/parse/read/directives.ts | 109 ++-- src/parse/read/expression.ts | 24 +- src/parse/read/script.ts | 19 +- src/parse/read/style.ts | 20 +- src/parse/state/fragment.ts | 6 +- src/parse/state/mustache.ts | 152 +++--- src/parse/state/tag.ts | 334 ++++++------ src/parse/state/text.ts | 12 +- src/parse/utils/entities.ts | 2 +- src/parse/utils/hash.ts | 4 +- src/parse/utils/html.ts | 78 ++- src/server-side-rendering/register.js | 12 +- src/shared/_build.js | 41 +- src/shared/dom.js | 66 +-- src/shared/index.js | 111 ++-- src/shared/transitions.js | 133 +++-- src/shared/utils.js | 13 +- src/utils/CodeBuilder.ts | 58 ++- src/utils/CompileError.ts | 28 +- src/utils/__test__.ts | 132 +++-- src/utils/annotateWithScopes.ts | 106 ++-- src/utils/deindent.js | 45 +- src/utils/flattenReference.ts | 16 +- src/utils/getCodeFrame.ts | 37 +- src/utils/globalWhitelist.ts | 27 +- src/utils/isReference.ts | 22 +- src/utils/isVoidElementName.ts | 4 +- src/utils/namespaces.ts | 24 +- src/utils/removeNode.ts | 42 +- src/utils/reservedNames.ts | 55 +- src/utils/spaces.js | 4 +- src/utils/trim.ts | 12 +- src/validate/html/index.ts | 52 +- src/validate/html/validateElement.ts | 166 +++--- src/validate/html/validateEventHandler.ts | 43 +- src/validate/html/validateWindow.ts | 46 +- src/validate/index.ts | 53 +- src/validate/js/index.ts | 86 +-- src/validate/js/propValidators/components.ts | 26 +- src/validate/js/propValidators/computed.ts | 52 +- src/validate/js/propValidators/data.ts | 10 +- src/validate/js/propValidators/events.ts | 13 +- src/validate/js/propValidators/helpers.ts | 62 ++- src/validate/js/propValidators/methods.ts | 36 +- src/validate/js/propValidators/namespace.ts | 24 +- src/validate/js/propValidators/oncreate.ts | 11 +- src/validate/js/propValidators/ondestroy.ts | 11 +- src/validate/js/propValidators/onrender.ts | 9 +- src/validate/js/propValidators/onteardown.ts | 9 +- src/validate/js/propValidators/transitions.ts | 15 +- src/validate/js/utils/checkForAccessors.ts | 12 +- src/validate/js/utils/checkForComputedKeys.ts | 11 +- src/validate/js/utils/checkForDupes.ts | 13 +- src/validate/js/utils/usesThisOrArguments.ts | 20 +- src/validate/utils/FuzzySet.ts | 72 ++- src/validate/utils/fuzzymatch.ts | 12 +- src/validate/utils/list.ts | 10 +- .../expected-bundle.js | 122 ++--- .../computed-collapsed-if/expected-bundle.js | 98 ++-- .../expected-bundle.js | 130 ++--- .../each-block-changed-check/expected.js | 2 +- .../event-handlers-custom/expected-bundle.js | 114 ++-- .../if-block-no-update/expected-bundle.js | 122 ++--- .../if-block-simple/expected-bundle.js | 122 ++--- .../non-imported-component/expected-bundle.js | 106 ++-- .../expected-bundle.js | 98 ++-- .../expected-bundle.js | 122 ++--- test/js/update.js | 10 + 116 files changed, 4787 insertions(+), 3345 deletions(-) create mode 100644 test/js/update.js diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 6ee4171cf13d..09b7e3409a85 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -42,7 +42,12 @@ export default class Generator { aliases: Map; usedNames: Set; - constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) { + constructor( + parsed: Parsed, + source: string, + name: string, + options: CompileOptions + ) { this.parsed = parsed; this.source = source; this.name = name; @@ -61,9 +66,9 @@ export default class Generator { // in dev mode this.expectedProperties = new Set(); - this.code = new MagicString( source ); + this.code = new MagicString(source); this.cascade = options.cascade !== false; // TODO remove this option in v2 - this.css = parsed.css ? processCss( parsed, this.code, this.cascade ) : null; + this.css = parsed.css ? processCss(parsed, this.code, this.cascade) : null; this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; this.usesRefs = false; @@ -71,105 +76,112 @@ export default class Generator { // Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`; this.importedNames = new Set(); this.aliases = new Map(); - this.usedNames = new Set( [ name ] ); + this.usedNames = new Set([name]); } - addSourcemapLocations ( node: Node ) { - walk( node, { - enter: ( node: Node ) => { - this.code.addSourcemapLocation( node.start ); - this.code.addSourcemapLocation( node.end ); + addSourcemapLocations(node: Node) { + walk(node, { + enter: (node: Node) => { + this.code.addSourcemapLocation(node.start); + this.code.addSourcemapLocation(node.end); } }); } - alias ( name: string ) { - if ( !this.aliases.has( name ) ) { - this.aliases.set( name, this.getUniqueName( name ) ); + alias(name: string) { + if (!this.aliases.has(name)) { + this.aliases.set(name, this.getUniqueName(name)); } - return this.aliases.get( name ); + return this.aliases.get(name); } - contextualise ( block: DomBlock | SsrBlock, expression: Node, context: string, isEventHandler: boolean ) { - this.addSourcemapLocations( expression ); + contextualise( + block: DomBlock | SsrBlock, + expression: Node, + context: string, + isEventHandler: boolean + ) { + this.addSourcemapLocations(expression); const usedContexts: string[] = []; const { code, helpers } = this; const { contexts, indexes } = block; - let scope = annotateWithScopes( expression ); // TODO this already happens in findDependencies + let scope = annotateWithScopes(expression); // TODO this already happens in findDependencies let lexicalDepth = 0; const self = this; - walk( expression, { - enter ( node: Node, parent: Node, key: string ) { - if ( /^Function/.test( node.type ) ) lexicalDepth += 1; + walk(expression, { + enter(node: Node, parent: Node, key: string) { + if (/^Function/.test(node.type)) lexicalDepth += 1; - if ( node._scope ) { + if (node._scope) { scope = node._scope; return; } - if ( node.type === 'ThisExpression' ) { - if ( lexicalDepth === 0 && context ) code.overwrite( node.start, node.end, context, { storeName: true, contentOnly: false } ); - } - - else if ( isReference( node, parent ) ) { - const { name } = flattenReference( node ); - if ( scope.has( name ) ) return; - - if ( name === 'event' && isEventHandler ) { + if (node.type === 'ThisExpression') { + if (lexicalDepth === 0 && context) + code.overwrite(node.start, node.end, context, { + storeName: true, + contentOnly: false + }); + } else if (isReference(node, parent)) { + const { name } = flattenReference(node); + if (scope.has(name)) return; + + if (name === 'event' && isEventHandler) { // noop - } - - else if ( contexts.has( name ) ) { - const contextName = contexts.get( name ); - if ( contextName !== name ) { + } else if (contexts.has(name)) { + const contextName = contexts.get(name); + if (contextName !== name) { // this is true for 'reserved' names like `state` and `component` - code.overwrite( node.start, node.start + name.length, contextName, { storeName: true, contentOnly: false } ); + code.overwrite( + node.start, + node.start + name.length, + contextName, + { storeName: true, contentOnly: false } + ); } - if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name ); - } - - else if ( helpers.has( name ) ) { - code.prependRight( node.start, `${self.alias( 'template' )}.helpers.` ); - } - - else if ( indexes.has( name ) ) { - const context = indexes.get( name ); - if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context ); - } - - else { + if (!~usedContexts.indexOf(name)) usedContexts.push(name); + } else if (helpers.has(name)) { + code.prependRight(node.start, `${self.alias('template')}.helpers.`); + } else if (indexes.has(name)) { + const context = indexes.get(name); + if (!~usedContexts.indexOf(context)) usedContexts.push(context); + } else { // handle shorthand properties - if ( parent && parent.type === 'Property' && parent.shorthand ) { - if ( key === 'key' ) { - code.appendLeft( node.start, `${name}: ` ); + if (parent && parent.type === 'Property' && parent.shorthand) { + if (key === 'key') { + code.appendLeft(node.start, `${name}: `); return; } } - if ( globalWhitelist.has( name ) ) { - code.prependRight( node.start, `( '${name}' in state ? state.` ); - code.appendLeft( node.object ? node.object.end : node.end, ` : ${name} )` ); + if (globalWhitelist.has(name)) { + code.prependRight(node.start, `( '${name}' in state ? state.`); + code.appendLeft( + node.object ? node.object.end : node.end, + ` : ${name} )` + ); } else { - code.prependRight( node.start, `state.` ); + code.prependRight(node.start, `state.`); } - if ( !~usedContexts.indexOf( 'state' ) ) usedContexts.push( 'state' ); + if (!~usedContexts.indexOf('state')) usedContexts.push('state'); } this.skip(); } }, - leave ( node: Node ) { - if ( /^Function/.test( node.type ) ) lexicalDepth -= 1; - if ( node._scope ) scope = scope.parent; + leave(node: Node) { + if (/^Function/.test(node.type)) lexicalDepth -= 1; + if (node._scope) scope = scope.parent; } }); @@ -180,112 +192,133 @@ export default class Generator { }; } - findDependencies ( contextDependencies: Map, indexes: Map, expression: Node ) { - if ( expression._dependencies ) return expression._dependencies; + findDependencies( + contextDependencies: Map, + indexes: Map, + expression: Node + ) { + if (expression._dependencies) return expression._dependencies; - let scope = annotateWithScopes( expression ); + let scope = annotateWithScopes(expression); const dependencies: string[] = []; const generator = this; // can't use arrow functions, because of this.skip() - walk( expression, { - enter ( node: Node, parent: Node ) { - if ( node._scope ) { + walk(expression, { + enter(node: Node, parent: Node) { + if (node._scope) { scope = node._scope; return; } - if ( isReference( node, parent ) ) { - const { name } = flattenReference( node ); - if ( scope.has( name ) || generator.helpers.has( name ) ) return; + if (isReference(node, parent)) { + const { name } = flattenReference(node); + if (scope.has(name) || generator.helpers.has(name)) return; - if ( contextDependencies.has( name ) ) { - dependencies.push( ...contextDependencies.get( name ) ); - } else if ( !indexes.has( name ) ) { - dependencies.push( name ); + if (contextDependencies.has(name)) { + dependencies.push(...contextDependencies.get(name)); + } else if (!indexes.has(name)) { + dependencies.push(name); } this.skip(); } }, - leave ( node: Node ) { - if ( node._scope ) scope = scope.parent; + leave(node: Node) { + if (node._scope) scope = scope.parent; } }); - dependencies.forEach( name => { - if ( !globalWhitelist.has( name ) ) { - this.expectedProperties.add( name ); + dependencies.forEach(name => { + if (!globalWhitelist.has(name)) { + this.expectedProperties.add(name); } }); - return ( expression._dependencies = dependencies ); + return (expression._dependencies = dependencies); } - generate ( result, options, { name, format } ) { - if ( this.imports.length ) { + generate(result, options, { name, format }) { + if (this.imports.length) { const statements: string[] = []; - this.imports.forEach( ( declaration, i ) => { - if ( format === 'es' ) { - statements.push( this.source.slice( declaration.start, declaration.end ) ); + this.imports.forEach((declaration, i) => { + if (format === 'es') { + statements.push( + this.source.slice(declaration.start, declaration.end) + ); return; } - const defaultImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' ); - const namespaceImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportNamespaceSpecifier' ); - const namedImports = declaration.specifiers.filter( ( x: Node ) => x.type === 'ImportSpecifier' && x.imported.name !== 'default' ); - - const name = ( defaultImport || namespaceImport ) ? ( defaultImport || namespaceImport ).local.name : `__import${i}`; + const defaultImport = declaration.specifiers.find( + (x: Node) => + x.type === 'ImportDefaultSpecifier' || + (x.type === 'ImportSpecifier' && x.imported.name === 'default') + ); + const namespaceImport = declaration.specifiers.find( + (x: Node) => x.type === 'ImportNamespaceSpecifier' + ); + const namedImports = declaration.specifiers.filter( + (x: Node) => + x.type === 'ImportSpecifier' && x.imported.name !== 'default' + ); + + const name = defaultImport || namespaceImport + ? (defaultImport || namespaceImport).local.name + : `__import${i}`; declaration.name = name; // hacky but makes life a bit easier later - namedImports.forEach( ( specifier: Node ) => { - statements.push( `var ${specifier.local.name} = ${name}.${specifier.imported.name}` ); + namedImports.forEach((specifier: Node) => { + statements.push( + `var ${specifier.local.name} = ${name}.${specifier.imported.name}` + ); }); - if ( defaultImport ) { - statements.push( `${name} = ( ${name} && ${name}.__esModule ) ? ${name}['default'] : ${name};` ); + if (defaultImport) { + statements.push( + `${name} = ( ${name} && ${name}.__esModule ) ? ${name}['default'] : ${name};` + ); } }); - result = `${statements.join( '\n' )}\n\n${result}`; + result = `${statements.join('\n')}\n\n${result}`; } const pattern = /\[✂(\d+)-(\d+)$/; - const parts = result.split( '✂]' ); + const parts = result.split('✂]'); const finalChunk = parts.pop(); const compiled = new Bundle({ separator: '' }); - function addString ( str: string ) { + function addString(str: string) { compiled.addSource({ - content: new MagicString( str ) + content: new MagicString(str) }); } - const intro = getIntro( format, options, this.imports ); - if ( intro ) addString( intro ); + const intro = getIntro(format, options, this.imports); + if (intro) addString(intro); const { filename } = options; // special case — the source file doesn't actually get used anywhere. we need // to add an empty file to populate map.sources and map.sourcesContent - if ( !parts.length ) { + if (!parts.length) { compiled.addSource({ filename, - content: new MagicString( this.source ).remove( 0, this.source.length ) + content: new MagicString(this.source).remove(0, this.source.length) }); } - parts.forEach( ( str: string ) => { - const chunk = str.replace( pattern, '' ); - if ( chunk ) addString( chunk ); + parts.forEach((str: string) => { + const chunk = str.replace(pattern, ''); + if (chunk) addString(chunk); - const match = pattern.exec( str ); + const match = pattern.exec(str); - const snippet = this.code.snip( +match[1], +match[2] ); + const snippet = this.code.snip(+match[1], +match[2]); compiled.addSource({ filename, @@ -293,36 +326,52 @@ export default class Generator { }); }); - addString( finalChunk ); - addString( '\n\n' + getOutro( format, name, options, this.imports ) ); + addString(finalChunk); + addString('\n\n' + getOutro(format, name, options, this.imports)); return { code: compiled.toString(), - map: compiled.generateMap({ includeContent: true, file: options.outputFilename }), + map: compiled.generateMap({ + includeContent: true, + file: options.outputFilename + }), css: this.css }; } - getUniqueName ( name: string ) { - if ( test ) name = `${name}$`; + getUniqueName(name: string) { + if (test) name = `${name}$`; let alias = name; - for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this.usedNames.has( alias ); alias = `${name}_${i++}` ); - this.usedNames.add( alias ); + for ( + let i = 1; + reservedNames.has(alias) || + this.importedNames.has(alias) || + this.usedNames.has(alias); + alias = `${name}_${i++}` + ); + this.usedNames.add(alias); return alias; } - getUniqueNameMaker ( params ) { - const localUsedNames = new Set( params ); + getUniqueNameMaker(params) { + const localUsedNames = new Set(params); return name => { - if ( test ) name = `${name}$`; + if (test) name = `${name}$`; let alias = name; - for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this.usedNames.has( alias ) || localUsedNames.has( alias ); alias = `${name}_${i++}` ); - localUsedNames.add( alias ); + for ( + let i = 1; + reservedNames.has(alias) || + this.importedNames.has(alias) || + this.usedNames.has(alias) || + localUsedNames.has(alias); + alias = `${name}_${i++}` + ); + localUsedNames.add(alias); return alias; }; } - parseJs ( ssr: boolean = false ) { + parseJs(ssr: boolean = false) { const { source } = this; const { js } = this.parsed; @@ -333,154 +382,213 @@ export default class Generator { let namespace = null; let hasJs = !!js; - if ( js ) { - this.addSourcemapLocations( js.content ); + if (js) { + this.addSourcemapLocations(js.content); const body = js.content.body.slice(); // slice, because we're going to be mutating the original // imports need to be hoisted out of the IIFE - for ( let i = 0; i < body.length; i += 1 ) { + for (let i = 0; i < body.length; i += 1) { const node = body[i]; - if ( node.type === 'ImportDeclaration' ) { - removeNode( this.code, js.content, node ); - imports.push( node ); + if (node.type === 'ImportDeclaration') { + removeNode(this.code, js.content, node); + imports.push(node); - node.specifiers.forEach( ( specifier: Node ) => { - this.importedNames.add( specifier.local.name ); + node.specifiers.forEach((specifier: Node) => { + this.importedNames.add(specifier.local.name); }); } } - const defaultExport = body.find( ( node: Node ) => node.type === 'ExportDefaultDeclaration' ); + const defaultExport = body.find( + (node: Node) => node.type === 'ExportDefaultDeclaration' + ); - if ( defaultExport ) { - defaultExport.declaration.properties.forEach( ( prop: Node ) => { - templateProperties[ prop.key.name ] = prop; + if (defaultExport) { + defaultExport.declaration.properties.forEach((prop: Node) => { + templateProperties[prop.key.name] = prop; }); } - [ 'helpers', 'events', 'components', 'transitions' ].forEach( key => { - if ( templateProperties[ key ] ) { - templateProperties[ key ].value.properties.forEach( ( prop: node ) => { - this[ key ].add( prop.key.name ); + ['helpers', 'events', 'components', 'transitions'].forEach(key => { + if (templateProperties[key]) { + templateProperties[key].value.properties.forEach((prop: node) => { + this[key].add(prop.key.name); }); } }); - if ( templateProperties.computed ) { + if (templateProperties.computed) { const dependencies = new Map(); - templateProperties.computed.value.properties.forEach( ( prop: Node ) => { + templateProperties.computed.value.properties.forEach((prop: Node) => { const key = prop.key.name; const value = prop.value; - const deps = value.params.map( ( param: Node ) => param.type === 'AssignmentPattern' ? param.left.name : param.name ); - dependencies.set( key, deps ); + const deps = value.params.map( + (param: Node) => + param.type === 'AssignmentPattern' ? param.left.name : param.name + ); + dependencies.set(key, deps); }); const visited = new Set(); - function visit ( key ) { - if ( !dependencies.has( key ) ) return; // not a computation + function visit(key) { + if (!dependencies.has(key)) return; // not a computation - if ( visited.has( key ) ) return; - visited.add( key ); + if (visited.has(key)) return; + visited.add(key); - const deps = dependencies.get( key ); - deps.forEach( visit ); + const deps = dependencies.get(key); + deps.forEach(visit); computations.push({ key, deps }); } - templateProperties.computed.value.properties.forEach( ( prop: Node ) => visit( prop.key.name ) ); + templateProperties.computed.value.properties.forEach((prop: Node) => + visit(prop.key.name) + ); } - if ( templateProperties.namespace ) { + if (templateProperties.namespace) { const ns = templateProperties.namespace.value.value; - namespace = namespaces[ ns ] || ns; + namespace = namespaces[ns] || ns; - removeObjectKey( this.code, defaultExport.declaration, 'namespace' ); + removeObjectKey(this.code, defaultExport.declaration, 'namespace'); } - if ( templateProperties.components ) { + if (templateProperties.components) { let hasNonImportedComponent = false; - templateProperties.components.value.properties.forEach( ( property: Node ) => { - const key = property.key.name; - const value = source.slice( property.value.start, property.value.end ); - if ( this.importedNames.has( value ) ) { - this.importedComponents.set( key, value ); - } else { - hasNonImportedComponent = true; + templateProperties.components.value.properties.forEach( + (property: Node) => { + const key = property.key.name; + const value = source.slice( + property.value.start, + property.value.end + ); + if (this.importedNames.has(value)) { + this.importedComponents.set(key, value); + } else { + hasNonImportedComponent = true; + } } - }); - if ( hasNonImportedComponent ) { + ); + if (hasNonImportedComponent) { // remove the specific components that were imported, as we'll refer to them directly - Array.from( this.importedComponents.keys() ).forEach( key => { - removeObjectKey( this.code, templateProperties.components.value, key ); + Array.from(this.importedComponents.keys()).forEach(key => { + removeObjectKey( + this.code, + templateProperties.components.value, + key + ); }); } else { // remove the entire components portion of the export - removeObjectKey( this.code, defaultExport.declaration, 'components' ); + removeObjectKey(this.code, defaultExport.declaration, 'components'); } } // Remove these after version 2 - if ( templateProperties.onrender ) { + if (templateProperties.onrender) { const { key } = templateProperties.onrender; - this.code.overwrite( key.start, key.end, 'oncreate', { storeName: true, contentOnly: false } ); + this.code.overwrite(key.start, key.end, 'oncreate', { + storeName: true, + contentOnly: false + }); templateProperties.oncreate = templateProperties.onrender; } - if ( templateProperties.onteardown ) { + if (templateProperties.onteardown) { const { key } = templateProperties.onteardown; - this.code.overwrite( key.start, key.end, 'ondestroy', { storeName: true, contentOnly: false } ); + this.code.overwrite(key.start, key.end, 'ondestroy', { + storeName: true, + contentOnly: false + }); templateProperties.ondestroy = templateProperties.onteardown; } // in an SSR context, we don't need to include events, methods, oncreate or ondestroy - if ( ssr ) { - if ( templateProperties.oncreate ) removeNode( this.code, defaultExport.declaration, templateProperties.oncreate ); - if ( templateProperties.ondestroy ) removeNode( this.code, defaultExport.declaration, templateProperties.ondestroy ); - if ( templateProperties.methods ) removeNode( this.code, defaultExport.declaration, templateProperties.methods ); - if ( templateProperties.events ) removeNode( this.code, defaultExport.declaration, templateProperties.events ); + if (ssr) { + if (templateProperties.oncreate) + removeNode( + this.code, + defaultExport.declaration, + templateProperties.oncreate + ); + if (templateProperties.ondestroy) + removeNode( + this.code, + defaultExport.declaration, + templateProperties.ondestroy + ); + if (templateProperties.methods) + removeNode( + this.code, + defaultExport.declaration, + templateProperties.methods + ); + if (templateProperties.events) + removeNode( + this.code, + defaultExport.declaration, + templateProperties.events + ); } // now that we've analysed the default export, we can determine whether or not we need to keep it let hasDefaultExport = !!defaultExport; - if ( defaultExport && defaultExport.declaration.properties.length === 0 ) { + if (defaultExport && defaultExport.declaration.properties.length === 0) { hasDefaultExport = false; - removeNode( this.code, js.content, defaultExport ); + removeNode(this.code, js.content, defaultExport); } // if we do need to keep it, then we need to generate a return statement - if ( hasDefaultExport ) { - const finalNode = body[ body.length - 1 ]; - if ( defaultExport === finalNode ) { + if (hasDefaultExport) { + const finalNode = body[body.length - 1]; + if (defaultExport === finalNode) { // export is last property, we can just return it - this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); + this.code.overwrite( + defaultExport.start, + defaultExport.declaration.start, + `return ` + ); } else { - const { declarations } = annotateWithScopes( js ); + const { declarations } = annotateWithScopes(js); let template = 'template'; - for ( let i = 1; declarations.has( template ); template = `template_${i++}` ); - - this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var ${template} = ` ); + for ( + let i = 1; + declarations.has(template); + template = `template_${i++}` + ); + + this.code.overwrite( + defaultExport.start, + defaultExport.declaration.start, + `var ${template} = ` + ); let i = defaultExport.start; - while ( /\s/.test( source[ i - 1 ] ) ) i--; + while (/\s/.test(source[i - 1])) i--; - const indentation = source.slice( i, defaultExport.start ); - this.code.appendLeft( finalNode.end, `\n\n${indentation}return ${template};` ); + const indentation = source.slice(i, defaultExport.start); + this.code.appendLeft( + finalNode.end, + `\n\n${indentation}return ${template};` + ); } } // user code gets wrapped in an IIFE - if ( js.content.body.length ) { - const prefix = hasDefaultExport ? `var ${this.alias( 'template' )} = (function () {` : `(function () {`; - this.code.prependRight( js.content.start, prefix ).appendLeft( js.content.end, '}());' ); - } - - // if there's no need to include user code, remove it altogether - else { - this.code.remove( js.content.start, js.content.end ); + if (js.content.body.length) { + const prefix = hasDefaultExport + ? `var ${this.alias('template')} = (function () {` + : `(function () {`; + this.code + .prependRight(js.content.start, prefix) + .appendLeft(js.content.end, '}());'); + } else { + // if there's no need to include user code, remove it altogether + this.code.remove(js.content.start, js.content.end); hasJs = false; } } diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 0131ca1e12be..f99c1d52c92b 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -48,7 +48,7 @@ export default class Block { unmount: CodeBuilder; detachRaw: CodeBuilder; destroy: CodeBuilder; - } + }; hasIntroMethod: boolean; hasOutroMethod: boolean; @@ -64,7 +64,7 @@ export default class Block { hasUpdateMethod: boolean; autofocus: string; - constructor ( options: BlockOptions ) { + constructor(options: BlockOptions) { this.generator = options.generator; this.name = options.name; this.expression = options.expression; @@ -102,144 +102,169 @@ export default class Block { this.aliases = new Map(); this.variables = new Map(); - this.getUniqueName = this.generator.getUniqueNameMaker( options.params ); + this.getUniqueName = this.generator.getUniqueNameMaker(options.params); // unique names - this.component = this.getUniqueName( 'component' ); - this.target = this.getUniqueName( 'target' ); + this.component = this.getUniqueName('component'); + this.target = this.getUniqueName('target'); this.hasUpdateMethod = false; // determined later } - addDependencies ( dependencies ) { - dependencies.forEach( dependency => { - this.dependencies.add( dependency ); + addDependencies(dependencies) { + dependencies.forEach(dependency => { + this.dependencies.add(dependency); }); } - addElement ( name: string, renderStatement: string, parentNode: string, needsIdentifier = false ) { + addElement( + name: string, + renderStatement: string, + parentNode: string, + needsIdentifier = false + ) { const isToplevel = !parentNode; - if ( needsIdentifier || isToplevel ) { - this.builders.create.addLine( - `var ${name} = ${renderStatement};` - ); + if (needsIdentifier || isToplevel) { + this.builders.create.addLine(`var ${name} = ${renderStatement};`); - this.mount( name, parentNode ); + this.mount(name, parentNode); } else { - this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${renderStatement}, ${parentNode} );` ); + this.builders.create.addLine( + `${this.generator.helper( + 'appendNode' + )}( ${renderStatement}, ${parentNode} );` + ); } - if ( isToplevel ) { - this.builders.unmount.addLine( `${this.generator.helper( 'detachNode' )}( ${name} );` ); + if (isToplevel) { + this.builders.unmount.addLine( + `${this.generator.helper('detachNode')}( ${name} );` + ); } } - addVariable ( name: string, init?: string ) { - if ( this.variables.has( name ) && this.variables.get( name ) !== init ) { - throw new Error( `Variable '${name}' already initialised with a different value` ); + addVariable(name: string, init?: string) { + if (this.variables.has(name) && this.variables.get(name) !== init) { + throw new Error( + `Variable '${name}' already initialised with a different value` + ); } - this.variables.set( name, init ); + this.variables.set(name, init); } - alias ( name: string ) { - if ( !this.aliases.has( name ) ) { - this.aliases.set( name, this.getUniqueName( name ) ); + alias(name: string) { + if (!this.aliases.has(name)) { + this.aliases.set(name, this.getUniqueName(name)); } - return this.aliases.get( name ); + return this.aliases.get(name); } - child ( options: BlockOptions ) { - return new Block( Object.assign( {}, this, options, { parent: this } ) ); + child(options: BlockOptions) { + return new Block(Object.assign({}, this, options, { parent: this })); } - contextualise ( expression: Node, context?: string, isEventHandler?: boolean ) { - return this.generator.contextualise( this, expression, context, isEventHandler ); + contextualise(expression: Node, context?: string, isEventHandler?: boolean) { + return this.generator.contextualise( + this, + expression, + context, + isEventHandler + ); } - findDependencies ( expression ) { - return this.generator.findDependencies( this.contextDependencies, this.indexes, expression ); + findDependencies(expression) { + return this.generator.findDependencies( + this.contextDependencies, + this.indexes, + expression + ); } - mount ( name: string, parentNode: string ) { - if ( parentNode ) { - this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${parentNode} );` ); + mount(name: string, parentNode: string) { + if (parentNode) { + this.builders.create.addLine( + `${this.generator.helper('appendNode')}( ${name}, ${parentNode} );` + ); } else { - this.builders.mount.addLine( `${this.generator.helper( 'insertNode' )}( ${name}, ${this.target}, anchor );` ); + this.builders.mount.addLine( + `${this.generator.helper('insertNode')}( ${name}, ${this + .target}, anchor );` + ); } } - render () { + render() { let introing; const hasIntros = !this.builders.intro.isEmpty(); - if ( hasIntros ) { - introing = this.getUniqueName( 'introing' ); - this.addVariable( introing ); + if (hasIntros) { + introing = this.getUniqueName('introing'); + this.addVariable(introing); } let outroing; const hasOutros = !this.builders.outro.isEmpty(); - if ( hasOutros ) { - outroing = this.getUniqueName( 'outroing' ); - this.addVariable( outroing ); + if (hasOutros) { + outroing = this.getUniqueName('outroing'); + this.addVariable(outroing); } - if ( this.variables.size ) { - const variables = Array.from( this.variables.keys() ) - .map( key => { - const init = this.variables.get( key ); + if (this.variables.size) { + const variables = Array.from(this.variables.keys()) + .map(key => { + const init = this.variables.get(key); return init !== undefined ? `${key} = ${init}` : key; }) - .join( ', ' ); + .join(', '); - this.builders.create.addBlockAtStart( `var ${variables};` ); + this.builders.create.addBlockAtStart(`var ${variables};`); } - if ( this.autofocus ) { - this.builders.create.addLine( `${this.autofocus}.focus();` ); + if (this.autofocus) { + this.builders.create.addLine(`${this.autofocus}.focus();`); } // minor hack – we need to ensure that any {{{triples}}} are detached first - this.builders.unmount.addBlockAtStart( this.builders.detachRaw ); + this.builders.unmount.addBlockAtStart(this.builders.detachRaw); const properties = new CodeBuilder(); let localKey; - if ( this.key ) { - localKey = this.getUniqueName( 'key' ); - properties.addBlock( `key: ${localKey},` ); + if (this.key) { + localKey = this.getUniqueName('key'); + properties.addBlock(`key: ${localKey},`); } - if ( this.first ) { - properties.addBlock( `first: ${this.first},` ); + if (this.first) { + properties.addBlock(`first: ${this.first},`); } - if ( this.builders.mount.isEmpty() ) { - properties.addBlock( `mount: ${this.generator.helper( 'noop' )},` ); + if (this.builders.mount.isEmpty()) { + properties.addBlock(`mount: ${this.generator.helper('noop')},`); } else { - properties.addBlock( deindent` + properties.addBlock(deindent` mount: function ( ${this.target}, anchor ) { ${this.builders.mount} }, - ` ); + `); } - if ( this.hasUpdateMethod ) { - if ( this.builders.update.isEmpty() ) { - properties.addBlock( `update: ${this.generator.helper( 'noop' )},` ); + if (this.hasUpdateMethod) { + if (this.builders.update.isEmpty()) { + properties.addBlock(`update: ${this.generator.helper('noop')},`); } else { - properties.addBlock( deindent` - update: function ( changed, ${this.params.join( ', ' )} ) { + properties.addBlock(deindent` + update: function ( changed, ${this.params.join(', ')} ) { ${this.builders.update} }, - ` ); + `); } } - if ( this.hasIntroMethod ) { - if ( hasIntros ) { - properties.addBlock( deindent` + if (this.hasIntroMethod) { + if (hasIntros) { + properties.addBlock(deindent` intro: function ( ${this.target}, anchor ) { if ( ${introing} ) return; ${introing} = true; @@ -249,60 +274,63 @@ export default class Block { this.mount( ${this.target}, anchor ); }, - ` ); + `); } else { - properties.addBlock( deindent` + properties.addBlock(deindent` intro: function ( ${this.target}, anchor ) { this.mount( ${this.target}, anchor ); }, - ` ); + `); } } - if ( this.hasOutroMethod ) { - if ( hasOutros ) { - properties.addBlock( deindent` - outro: function ( ${this.alias( 'outrocallback' )} ) { + if (this.hasOutroMethod) { + if (hasOutros) { + properties.addBlock(deindent` + outro: function ( ${this.alias('outrocallback')} ) { if ( ${outroing} ) return; ${outroing} = true; ${hasIntros && `${introing} = false;`} - var ${this.alias( 'outros' )} = ${this.outros}; + var ${this.alias('outros')} = ${this.outros}; ${this.builders.outro} }, - ` ); + `); } else { - properties.addBlock( deindent` + properties.addBlock(deindent` outro: function ( outrocallback ) { outrocallback(); }, - ` ); + `); } } - if ( this.builders.unmount.isEmpty() ) { - properties.addBlock( `unmount: ${this.generator.helper('noop')},`); + if (this.builders.unmount.isEmpty()) { + properties.addBlock(`unmount: ${this.generator.helper('noop')},`); } else { - properties.addBlock( deindent` + properties.addBlock(deindent` unmount: function () { ${this.builders.unmount} }, - ` ); + `); } - if ( this.builders.destroy.isEmpty() ) { - properties.addBlock( `destroy: ${this.generator.helper( 'noop' )}` ); + if (this.builders.destroy.isEmpty()) { + properties.addBlock(`destroy: ${this.generator.helper('noop')}`); } else { - properties.addBlock( deindent` + properties.addBlock(deindent` destroy: function () { ${this.builders.destroy} } - ` ); + `); } return deindent` - function ${this.name} ( ${this.params.join( ', ' )}, ${this.component}${this.key ? `, ${localKey}` : ''} ) { + function ${this.name} ( ${this.params.join(', ')}, ${this.component}${this + .key + ? `, ${localKey}` + : ''} ) { ${this.builders.create} return { @@ -311,4 +339,4 @@ export default class Block { } `; } -} \ No newline at end of file +} diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index 341608d42818..cfb3bacdcaf6 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -13,7 +13,7 @@ import Block from './Block'; import { Parsed, CompileOptions, Node } from '../../interfaces'; export class DomGenerator extends Generator { - blocks: Block[] + blocks: Block[]; uses: Set; readonly: Set; metaBindings: string[]; @@ -22,8 +22,13 @@ export class DomGenerator extends Generator { hasOutroTransitions: boolean; hasComplexBindings: boolean; - constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) { - super( parsed, source, name, options ); + constructor( + parsed: Parsed, + source: string, + name: string, + options: CompileOptions + ) { + super(parsed, source, name, options); this.blocks = []; this.uses = new Set(); @@ -33,29 +38,38 @@ export class DomGenerator extends Generator { this.metaBindings = []; } - helper ( name: string ) { - if ( this.options.dev && `${name}Dev` in shared ) { + helper(name: string) { + if (this.options.dev && `${name}Dev` in shared) { name = `${name}Dev`; } - this.uses.add( name ); + this.uses.add(name); - return this.alias( name ); + return this.alias(name); } } -export default function dom ( parsed: Parsed, source: string, options: CompileOptions ) { +export default function dom( + parsed: Parsed, + source: string, + options: CompileOptions +) { const format = options.format || 'es'; const name = options.name || 'SvelteComponent'; - const generator = new DomGenerator( parsed, source, name, options ); + const generator = new DomGenerator(parsed, source, name, options); - const { computations, hasJs, templateProperties, namespace } = generator.parseJs(); + const { + computations, + hasJs, + templateProperties, + namespace + } = generator.parseJs(); - const { block, state } = preprocess( generator, namespace, parsed.html ); + const { block, state } = preprocess(generator, namespace, parsed.html); - parsed.html.children.forEach( ( node: Node ) => { - visit( generator, block, state, node ); + parsed.html.children.forEach((node: Node) => { + visit(generator, block, state, node); }); const builders = { @@ -63,94 +77,139 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp _set: new CodeBuilder() }; - if ( computations.length ) { + if (computations.length) { const builder = new CodeBuilder(); - const differs = generator.helper( 'differs' ); + const differs = generator.helper('differs'); - computations.forEach( ({ key, deps }) => { - if ( generator.readonly.has( key ) ) { + computations.forEach(({ key, deps }) => { + if (generator.readonly.has(key)) { // <:Window> bindings - throw new Error( `Cannot have a computed value '${key}' that clashes with a read-only property` ); + throw new Error( + `Cannot have a computed value '${key}' that clashes with a read-only property` + ); } - generator.readonly.add( key ); + generator.readonly.add(key); - const condition = `isInitial || ${deps.map( 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 condition = `isInitial || ${deps + .map( + 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(', ')} );`; - builder.addConditionalLine( condition, statement ); + builder.addConditionalLine(condition, statement); }); - builders.main.addBlock( deindent` - function ${generator.alias( 'recompute' )} ( state, newState, oldState, isInitial ) { + builders.main.addBlock(deindent` + function ${generator.alias( + 'recompute' + )} ( state, newState, oldState, isInitial ) { ${builder} } - ` ); + `); } - builders._set.addBlock( deindent` - ${options.dev && deindent` + builders._set.addBlock(deindent` + ${options.dev && + deindent` if ( typeof newState !== 'object' ) { throw new Error( 'Component .set was called without an object of data key-values to update.' ); } - ${Array.from( generator.readonly ).map( prop => - `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` + ${Array.from(generator.readonly).map( + prop => + `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` )} `} var oldState = this._state; - this._state = ${generator.helper( 'assign' )}( {}, oldState, newState ); - ${computations.length && `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )`} - ${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState ); + this._state = ${generator.helper('assign')}( {}, oldState, newState ); + ${computations.length && + `${generator.alias( + 'recompute' + )}( this._state, newState, oldState, false )`} + ${generator.helper( + 'dispatchObservers' + )}( this, this._observers.pre, newState, oldState ); ${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`} - ${generator.helper( '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}✂]` ); + ${generator.helper( + '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}✂]` + ); } - 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 ); + 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 ); } - ` ); + `); } - generator.blocks.forEach( block => { - builders.main.addBlock( block.render() ); + generator.blocks.forEach(block => { + builders.main.addBlock(block.render()); }); - const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared; - - const prototypeBase = `${name}.prototype` + ( templateProperties.methods ? `, ${generator.alias( 'template' )}.methods` : '' ); - const proto = sharedPath ? `${generator.helper( 'proto' )} ` : deindent` + const sharedPath = options.shared === true + ? 'svelte/shared.js' + : options.shared; + + const prototypeBase = + `${name}.prototype` + + (templateProperties.methods + ? `, ${generator.alias('template')}.methods` + : ''); + const proto = sharedPath + ? `${generator.helper('proto')} ` + : deindent` { - ${ - [ 'get', 'fire', 'observe', 'on', 'set', '_flush' ] - .map( n => `${n}: ${generator.helper( n )}` ) - .join( ',\n' ) - } + ${['get', 'fire', 'observe', 'on', 'set', '_flush'] + .map(n => `${n}: ${generator.helper(n)}`) + .join(',\n')} }`; // TODO deprecate component.teardown() - builders.main.addBlock( deindent` + builders.main.addBlock(deindent` function ${name} ( options ) { options = options || {}; - ${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`} + ${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 )` : `options.data || {}`}; + this._state = ${templateProperties.data + ? `${generator.helper('assign')}( ${generator.alias( + 'template' + )}.data(), options.data )` + : `options.data || {}`}; ${generator.metaBindings} - ${computations.length && `${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`} - ${options.dev && Array.from( generator.expectedProperties ).map( prop => `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`)} - ${generator.bindingGroups.length && `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];`} + ${computations.length && + `${generator.alias( + 'recompute' + )}( this._state, this._state, {}, true );`} + ${options.dev && + Array.from(generator.expectedProperties).map( + prop => + `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );` + )} + ${generator.bindingGroups.length && + `this._bindingGroups = [ ${Array(generator.bindingGroups.length) + .fill('[]') + .join(', ')} ];`} this._observers = { pre: Object.create( null ), @@ -163,25 +222,37 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp this._yield = options._yield; this._torndown = false; - ${parsed.css && options.css !== false && `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();`} - ${( generator.hasComponents || generator.hasIntroTransitions ) && `this._renderHooks = [];`} + ${parsed.css && + options.css !== false && + `if ( !document.getElementById( ${JSON.stringify( + generator.cssId + '-style' + )} ) ) ${generator.alias('add_css')}();`} + ${(generator.hasComponents || generator.hasIntroTransitions) && + `this._renderHooks = [];`} ${generator.hasComplexBindings && `this._bindings = [];`} - this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this ); + this._fragment = ${generator.alias( + 'create_main_fragment' + )}( this._state, this ); if ( options.target ) this._fragment.mount( options.target, null ); - ${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`} - ${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`} + ${generator.hasComplexBindings && + `while ( this._bindings.length ) this._bindings.pop()();`} + ${(generator.hasComponents || generator.hasIntroTransitions) && + `this._flush();`} - ${templateProperties.oncreate && deindent` + ${templateProperties.oncreate && + deindent` if ( options._root ) { - options._root._renderHooks.push( ${generator.alias( 'template' )}.oncreate.bind( this ) ); + options._root._renderHooks.push( ${generator.alias( + 'template' + )}.oncreate.bind( this ) ); } else { - ${generator.alias( 'template' )}.oncreate.call( this ); + ${generator.alias('template')}.oncreate.call( this ); } `} } - ${generator.helper( 'assign' )}( ${prototypeBase}, ${proto}); + ${generator.helper('assign')}( ${prototypeBase}, ${proto}); ${name}.prototype._set = function _set ( newState ) { ${builders._set} @@ -189,7 +260,8 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp ${name}.prototype.teardown = ${name}.prototype.destroy = function destroy ( detach ) { this.fire( 'destroy' ); - ${templateProperties.ondestroy && `${generator.alias( 'template' )}.ondestroy.call( this );`} + ${templateProperties.ondestroy && + `${generator.alias('template')}.ondestroy.call( this );`} if ( detach !== false ) this._fragment.unmount(); this._fragment.destroy(); @@ -198,63 +270,79 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp this._state = {}; this._torndown = true; }; - ` ); + `); - if ( sharedPath ) { - if ( format !== 'es' ) { - throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` ); + if (sharedPath) { + if (format !== 'es') { + throw new Error( + `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` + ); } - const names = Array.from( generator.uses ).sort().map( name => { - return name !== generator.alias( name ) ? `${name} as ${generator.alias( name )}` : name; + const names = Array.from(generator.uses).sort().map(name => { + return name !== generator.alias(name) + ? `${name} as ${generator.alias(name)}` + : name; }); builders.main.addLineAtStart( - `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )};` + `import { ${names.join(', ')} } from ${JSON.stringify(sharedPath)};` ); } else { - generator.uses.forEach( key => { - const str = shared[ key ]; - const code = new MagicString( str ); - const expression = parseExpressionAt( str, 0 ); - - let scope = annotateWithScopes( expression ); - - walk( expression, { - enter ( node, parent ) { - if ( node._scope ) scope = node._scope; - - if ( node.type === 'Identifier' && isReference( node, parent ) && !scope.has( node.name ) ) { - if ( node.name in shared ) { + generator.uses.forEach(key => { + const str = shared[key]; + const code = new MagicString(str); + const expression = parseExpressionAt(str, 0); + + let scope = annotateWithScopes(expression); + + walk(expression, { + enter(node, parent) { + if (node._scope) scope = node._scope; + + if ( + node.type === 'Identifier' && + isReference(node, parent) && + !scope.has(node.name) + ) { + if (node.name in shared) { // this helper function depends on another one const dependency = node.name; - generator.uses.add( dependency ); + generator.uses.add(dependency); - const alias = generator.alias( dependency ); - if ( alias !== node.name ) code.overwrite( node.start, node.end, alias ); + const alias = generator.alias(dependency); + if (alias !== node.name) + code.overwrite(node.start, node.end, alias); } } }, - leave ( node ) { - if ( node._scope ) scope = scope.parent; + leave(node) { + if (node._scope) scope = scope.parent; } }); - if ( key === 'transitionManager' ) { // special case + if (key === 'transitionManager') { + // special case const global = `_svelteTransitionManager`; builders.main.addBlock( - `var ${generator.alias( 'transitionManager' )} = window.${global} || ( window.${global} = ${code});` + `var ${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 ); + 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() ); + builders.main.addBlock(code.toString()); } }); } - return generator.generate( builders.main.toString(), options, { name, format } ); + return generator.generate(builders.main.toString(), options, { + name, + format + }); } diff --git a/src/generators/dom/interfaces.ts b/src/generators/dom/interfaces.ts index a58069da9006..8c460a47336f 100644 --- a/src/generators/dom/interfaces.ts +++ b/src/generators/dom/interfaces.ts @@ -2,7 +2,7 @@ export interface State { name: string; namespace: string; parentNode: string; - isTopLevel: boolean + isTopLevel: boolean; parentNodeName?: string; basename?: string; inEachBlock?: boolean; diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 3a96aca3f2b5..595b753d57e4 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -5,12 +5,14 @@ import { DomGenerator } from './index'; import { Node } from '../../interfaces'; import { State } from './interfaces'; -function isElseIf ( node: Node ) { - return node && node.children.length === 1 && node.children[0].type === 'IfBlock'; +function isElseIf(node: Node) { + return ( + node && node.children.length === 1 && node.children[0].type === 'IfBlock' + ); } -function getChildState ( parent: State, child = {} ) { - return assign( {}, parent, { name: null, parentNode: null }, child || {} ); +function getChildState(parent: State, child = {}) { + return assign({}, parent, { name: null, parentNode: null }, child || {}); } // Whitespace inside one of these elements will not result in @@ -28,118 +30,144 @@ const elementsWithoutText = new Set([ ]); const preprocessors = { - MustacheTag: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { - const dependencies = block.findDependencies( node.expression ); - block.addDependencies( dependencies ); - - node._state = getChildState( state, { - name: block.getUniqueName( 'text' ) + MustacheTag: ( + generator: DomGenerator, + block: Block, + state: State, + node: Node + ) => { + const dependencies = block.findDependencies(node.expression); + block.addDependencies(dependencies); + + node._state = getChildState(state, { + name: block.getUniqueName('text') }); }, - RawMustacheTag: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { - const dependencies = block.findDependencies( node.expression ); - block.addDependencies( dependencies ); + RawMustacheTag: ( + generator: DomGenerator, + block: Block, + state: State, + node: Node + ) => { + const dependencies = block.findDependencies(node.expression); + block.addDependencies(dependencies); - const basename = block.getUniqueName( 'raw' ); - const name = block.getUniqueName( `${basename}_before` ); + const basename = block.getUniqueName('raw'); + const name = block.getUniqueName(`${basename}_before`); - node._state = getChildState( state, { basename, name }); + node._state = getChildState(state, { basename, name }); }, - Text: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { - node._state = getChildState( state ); + Text: (generator: DomGenerator, block: Block, state: State, node: Node) => { + node._state = getChildState(state); - if ( !/\S/.test( node.data ) ) { - if ( state.namespace ) return; - if ( elementsWithoutText.has( state.parentNodeName ) ) return; + if (!/\S/.test(node.data)) { + if (state.namespace) return; + if (elementsWithoutText.has(state.parentNodeName)) return; } node._state.shouldCreate = true; - node._state.name = block.getUniqueName( `text` ); + node._state.name = block.getUniqueName(`text`); }, - IfBlock: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { + IfBlock: ( + generator: DomGenerator, + block: Block, + state: State, + node: Node + ) => { const blocks: Block[] = []; let dynamic = false; let hasIntros = false; let hasOutros = false; - function attachBlocks ( node: Node ) { - const dependencies = block.findDependencies( node.expression ); - block.addDependencies( dependencies ); + function attachBlocks(node: Node) { + const dependencies = block.findDependencies(node.expression); + block.addDependencies(dependencies); node._block = block.child({ - name: generator.getUniqueName( `create_if_block` ) + name: generator.getUniqueName(`create_if_block`) }); - node._state = getChildState( state ); + node._state = getChildState(state); - blocks.push( node._block ); - preprocessChildren( generator, node._block, node._state, node ); + blocks.push(node._block); + preprocessChildren(generator, node._block, node._state, node); - if ( node._block.dependencies.size > 0 ) { + if (node._block.dependencies.size > 0) { dynamic = true; - block.addDependencies( node._block.dependencies ); + block.addDependencies(node._block.dependencies); } - if ( node._block.hasIntroMethod ) hasIntros = true; - if ( node._block.hasOutroMethod ) hasOutros = true; + if (node._block.hasIntroMethod) hasIntros = true; + if (node._block.hasOutroMethod) hasOutros = true; - if ( isElseIf( node.else ) ) { - attachBlocks( node.else.children[0] ); - } else if ( node.else ) { + if (isElseIf(node.else)) { + attachBlocks(node.else.children[0]); + } else if (node.else) { node.else._block = block.child({ - name: generator.getUniqueName( `create_if_block` ) + name: generator.getUniqueName(`create_if_block`) }); - node.else._state = getChildState( state ); + node.else._state = getChildState(state); - blocks.push( node.else._block ); - preprocessChildren( generator, node.else._block, node.else._state, node.else ); + blocks.push(node.else._block); + preprocessChildren( + generator, + node.else._block, + node.else._state, + node.else + ); - if ( node.else._block.dependencies.size > 0 ) { + if (node.else._block.dependencies.size > 0) { dynamic = true; - block.addDependencies( node.else._block.dependencies ); + block.addDependencies(node.else._block.dependencies); } } } - attachBlocks( node ); + attachBlocks(node); - blocks.forEach( block => { + blocks.forEach(block => { block.hasUpdateMethod = dynamic; block.hasIntroMethod = hasIntros; block.hasOutroMethod = hasOutros; }); - generator.blocks.push( ...blocks ); + generator.blocks.push(...blocks); }, - EachBlock: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { - const dependencies = block.findDependencies( node.expression ); - block.addDependencies( dependencies ); + EachBlock: ( + generator: DomGenerator, + block: Block, + state: State, + node: Node + ) => { + const dependencies = block.findDependencies(node.expression); + block.addDependencies(dependencies); - const indexNames = new Map( block.indexNames ); - const indexName = node.index || block.getUniqueName( `${node.context}_index` ); - indexNames.set( node.context, indexName ); + const indexNames = new Map(block.indexNames); + const indexName = + node.index || block.getUniqueName(`${node.context}_index`); + indexNames.set(node.context, indexName); - const listNames = new Map( block.listNames ); - const listName = block.getUniqueName( `each_block_value` ); - listNames.set( node.context, listName ); + const listNames = new Map(block.listNames); + const listName = block.getUniqueName(`each_block_value`); + listNames.set(node.context, listName); - const context = generator.getUniqueName( node.context ); - const contexts = new Map( block.contexts ); - contexts.set( node.context, context ); + const context = generator.getUniqueName(node.context); + const contexts = new Map(block.contexts); + contexts.set(node.context, context); - const indexes = new Map( block.indexes ); - if ( node.index ) indexes.set( indexName, node.context ); + const indexes = new Map(block.indexes); + if (node.index) indexes.set(indexName, node.context); - const contextDependencies = new Map( block.contextDependencies ); - contextDependencies.set( node.context, dependencies ); + const contextDependencies = new Map(block.contextDependencies); + contextDependencies.set(node.context, dependencies); node._block = block.child({ - name: generator.getUniqueName( 'create_each_block' ), + name: generator.getUniqueName('create_each_block'), expression: node.expression, context: node.context, key: node.key, @@ -153,134 +181,152 @@ const preprocessors = { indexNames, listNames, - params: block.params.concat( listName, context, indexName ) + params: block.params.concat(listName, context, indexName) }); - node._state = getChildState( state, { + node._state = getChildState(state, { inEachBlock: true }); - generator.blocks.push( node._block ); - preprocessChildren( generator, node._block, node._state, node ); - block.addDependencies( node._block.dependencies ); + generator.blocks.push(node._block); + preprocessChildren(generator, node._block, node._state, node); + block.addDependencies(node._block.dependencies); node._block.hasUpdateMethod = node._block.dependencies.size > 0; - if ( node.else ) { + if (node.else) { node.else._block = block.child({ - name: generator.getUniqueName( `${node._block.name}_else` ) + name: generator.getUniqueName(`${node._block.name}_else`) }); - node.else._state = getChildState( state ); + node.else._state = getChildState(state); - generator.blocks.push( node.else._block ); - preprocessChildren( generator, node.else._block, node.else._state, node.else ); + generator.blocks.push(node.else._block); + preprocessChildren( + generator, + node.else._block, + node.else._state, + node.else + ); node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0; } }, - Element: ( generator: DomGenerator, block: Block, state: State, node: Node ) => { - const isComponent = generator.components.has( node.name ) || node.name === ':Self'; - - if ( isComponent ) { - node._state = getChildState( state ); + Element: ( + generator: DomGenerator, + block: Block, + state: State, + node: Node + ) => { + const isComponent = + generator.components.has(node.name) || node.name === ':Self'; + + if (isComponent) { + node._state = getChildState(state); } else { - const name = block.getUniqueName( node.name.replace( /[^a-zA-Z0-9_$]/g, '_' ) ); + const name = block.getUniqueName( + node.name.replace(/[^a-zA-Z0-9_$]/g, '_') + ); - node._state = getChildState( state, { + node._state = getChildState(state, { isTopLevel: false, name, parentNode: name, parentNodeName: node.name, - namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace, + namespace: node.name === 'svg' + ? 'http://www.w3.org/2000/svg' + : state.namespace, allUsedContexts: [] }); } - node.attributes.forEach( ( attribute: Node ) => { - if ( attribute.type === 'Attribute' && attribute.value !== true ) { - attribute.value.forEach( ( chunk: Node ) => { - if ( chunk.type !== 'Text' ) { - const dependencies = block.findDependencies( chunk.expression ); - block.addDependencies( dependencies ); + node.attributes.forEach((attribute: Node) => { + if (attribute.type === 'Attribute' && attribute.value !== true) { + attribute.value.forEach((chunk: Node) => { + if (chunk.type !== 'Text') { + const dependencies = block.findDependencies(chunk.expression); + block.addDependencies(dependencies); } }); - } - - else if ( attribute.type === 'Binding' ) { - const dependencies = block.findDependencies( attribute.value ); - block.addDependencies( dependencies ); - } - - else if ( attribute.type === 'Transition' ) { - if ( attribute.intro ) generator.hasIntroTransitions = block.hasIntroMethod = true; - if ( attribute.outro ) { + } else if (attribute.type === 'Binding') { + const dependencies = block.findDependencies(attribute.value); + block.addDependencies(dependencies); + } else if (attribute.type === 'Transition') { + if (attribute.intro) + generator.hasIntroTransitions = block.hasIntroMethod = true; + if (attribute.outro) { generator.hasOutroTransitions = block.hasOutroMethod = true; block.outros += 1; } } }); - if ( node.children.length ) { - if ( isComponent ) { - const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() ); + if (node.children.length) { + if (isComponent) { + const name = block.getUniqueName( + (node.name === ':Self' ? generator.name : node.name).toLowerCase() + ); node._block = block.child({ - name: generator.getUniqueName( `create_${name}_yield_fragment` ) + name: generator.getUniqueName(`create_${name}_yield_fragment`) }); - generator.blocks.push( node._block ); - preprocessChildren( generator, node._block, node._state, node ); - block.addDependencies( node._block.dependencies ); + generator.blocks.push(node._block); + preprocessChildren(generator, node._block, node._state, node); + block.addDependencies(node._block.dependencies); node._block.hasUpdateMethod = node._block.dependencies.size > 0; - } - - else { - preprocessChildren( generator, block, node._state, node ); + } else { + preprocessChildren(generator, block, node._state, node); } } } }; -function preprocessChildren ( generator: DomGenerator, block: Block, state: State, node: Node, isTopLevel: boolean = false ) { +function preprocessChildren( + generator: DomGenerator, + block: Block, + state: State, + node: Node, + isTopLevel: boolean = false +) { // glue text nodes together const cleaned: Node[] = []; let lastChild: Node; - node.children.forEach( ( child: Node ) => { - if ( child.type === 'Comment' ) return; + node.children.forEach((child: Node) => { + if (child.type === 'Comment') return; - if ( child.type === 'Text' && lastChild && lastChild.type === 'Text' ) { + if (child.type === 'Text' && lastChild && lastChild.type === 'Text') { lastChild.data += child.data; lastChild.end = child.end; } else { - cleaned.push( child ); + cleaned.push(child); } lastChild = child; }); - if ( isTopLevel ) { + if (isTopLevel) { // trim leading and trailing whitespace from the top level const firstChild = cleaned[0]; - if ( firstChild && firstChild.type === 'Text' ) { - firstChild.data = trimStart( firstChild.data ); - if ( !firstChild.data ) cleaned.shift(); + if (firstChild && firstChild.type === 'Text') { + firstChild.data = trimStart(firstChild.data); + if (!firstChild.data) cleaned.shift(); } - const lastChild = cleaned[ cleaned.length - 1 ]; - if ( lastChild && lastChild.type === 'Text' ) { - lastChild.data = trimEnd( lastChild.data ); - if ( !lastChild.data ) cleaned.pop(); + const lastChild = cleaned[cleaned.length - 1]; + if (lastChild && lastChild.type === 'Text') { + lastChild.data = trimEnd(lastChild.data); + if (!lastChild.data) cleaned.pop(); } } lastChild = null; - cleaned.forEach( ( child: Node ) => { - const preprocess = preprocessors[ child.type ]; - if ( preprocess ) preprocess( generator, block, state, child ); + cleaned.forEach((child: Node) => { + const preprocess = preprocessors[child.type]; + if (preprocess) preprocess(generator, block, state, child); - if ( lastChild ) { + if (lastChild) { lastChild.next = child; lastChild.needsAnchor = !child._state || !child._state.name; } @@ -288,24 +334,28 @@ function preprocessChildren ( generator: DomGenerator, block: Block, state: Stat lastChild = child; }); - if ( lastChild ) { + if (lastChild) { lastChild.needsAnchor = !state.parentNode; } node.children = cleaned; } -export default function preprocess ( generator: DomGenerator, namespace: string, node: Node ) { +export default function preprocess( + generator: DomGenerator, + namespace: string, + node: Node +) { const block = new Block({ generator, - name: generator.alias( 'create_main_fragment' ), + name: generator.alias('create_main_fragment'), key: null, contexts: new Map(), indexes: new Map(), contextDependencies: new Map(), - params: [ 'state' ], + params: ['state'], indexNames: new Map(), listNames: new Map(), @@ -318,9 +368,9 @@ export default function preprocess ( generator: DomGenerator, namespace: string, isTopLevel: true }; - generator.blocks.push( block ); - preprocessChildren( generator, block, state, node, true ); + generator.blocks.push(block); + preprocessChildren(generator, block, state, node, true); block.hasUpdateMethod = block.dependencies.size > 0; return { block, state }; -} \ No newline at end of file +} diff --git a/src/generators/dom/visit.ts b/src/generators/dom/visit.ts index 1443a32049bd..91601fa8fd91 100644 --- a/src/generators/dom/visit.ts +++ b/src/generators/dom/visit.ts @@ -3,7 +3,12 @@ import { DomGenerator } from './index'; import Block from './Block'; import { Node } from '../../interfaces'; -export default function visit ( generator: DomGenerator, block: Block, state, node: Node ) { - const visitor = visitors[ node.type ]; - visitor( generator, block, state, node ); -} \ No newline at end of file +export default function visit( + generator: DomGenerator, + block: Block, + state, + node: Node +) { + const visitor = visitors[node.type]; + visitor(generator, block, state, node); +} diff --git a/src/generators/dom/visitors/Component/Attribute.ts b/src/generators/dom/visitors/Component/Attribute.ts index 690cb886394b..1bd477a3960c 100644 --- a/src/generators/dom/visitors/Component/Attribute.ts +++ b/src/generators/dom/visitors/Component/Attribute.ts @@ -3,37 +3,40 @@ import Block from '../../Block'; import { Node } from '../../../../interfaces'; import { State } from '../../interfaces'; -export default function visitAttribute ( generator: DomGenerator, block: Block, state: State, node: Node, attribute, local ) { - if ( attribute.value === true ) { +export default function visitAttribute( + generator: DomGenerator, + block: Block, + state: State, + node: Node, + attribute, + local +) { + if (attribute.value === true) { // attributes without values, e.g. ' ); - parser.read( /<\/textarea>/ ); + element.children = readSequence( + parser, + () => + parser.template.slice(parser.index, parser.index + 11) === '' + ); + parser.read(/<\/textarea>/); element.end = parser.index; } else { // don't push self-closing elements onto the stack - parser.stack.push( element ); + parser.stack.push(element); } return null; } -function readTagName ( parser: Parser ) { +function readTagName(parser: Parser) { const start = parser.index; - if ( parser.eat( SELF ) ) { + if (parser.eat(SELF)) { // check we're inside a block, otherwise this // will cause infinite recursion let i = parser.stack.length; let legal = false; - while ( i-- ) { + while (i--) { const fragment = parser.stack[i]; - if ( fragment.type === 'IfBlock' || fragment.type === 'EachBlock' ) { + if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock') { legal = true; break; } } - if ( !legal ) { - parser.error( `<${SELF}> components can only exist inside if-blocks or each-blocks`, start ); + if (!legal) { + parser.error( + `<${SELF}> components can only exist inside if-blocks or each-blocks`, + start + ); } return SELF; } - const name = parser.readUntil( /(\s|\/|>)/ ); + const name = parser.readUntil(/(\s|\/|>)/); - if ( name in metaTags ) return name; + if (name in metaTags) return name; - if ( !validTagName.test( name ) ) { - parser.error( `Expected valid tag name`, start ); + if (!validTagName.test(name)) { + parser.error(`Expected valid tag name`, start); } return name; } -function readAttribute ( parser: Parser, uniqueNames ) { +function readAttribute(parser: Parser, uniqueNames) { const start = parser.index; - let name = parser.readUntil( /(\s|=|\/|>)/ ); - if ( !name ) return null; - if ( uniqueNames.has( name ) ) { - parser.error( 'Attributes need to be unique', start ); + let name = parser.readUntil(/(\s|=|\/|>)/); + if (!name) return null; + if (uniqueNames.has(name)) { + parser.error('Attributes need to be unique', start); } - uniqueNames.add( name ); + uniqueNames.add(name); parser.allowWhitespace(); - if ( /^on:/.test( name ) ) { - parser.eat( '=', true ); - return readEventHandlerDirective( parser, start, name.slice( 3 ) ); + if (/^on:/.test(name)) { + parser.eat('=', true); + return readEventHandlerDirective(parser, start, name.slice(3)); } - if ( /^bind:/.test( name ) ) { - return readBindingDirective( parser, start, name.slice( 5 ) ); + if (/^bind:/.test(name)) { + return readBindingDirective(parser, start, name.slice(5)); } - if ( /^ref:/.test( name ) ) { + if (/^ref:/.test(name)) { return { start, end: parser.index, type: 'Ref', - name: name.slice( 4 ) + name: name.slice(4) }; } - const match = /^(in|out|transition):/.exec( name ); - if ( match ) { - return readTransitionDirective( parser, start, name.slice( match[0].length ), match[1] ); + const match = /^(in|out|transition):/.exec(name); + if (match) { + return readTransitionDirective( + parser, + start, + name.slice(match[0].length), + match[1] + ); } let value; // :foo is shorthand for foo='{{foo}}' - if ( /^:\w+$/.test( name ) ) { - name = name.slice( 1 ); - value = getShorthandValue( start + 1, name ); + if (/^:\w+$/.test(name)) { + name = name.slice(1); + value = getShorthandValue(start + 1, name); } else { - value = parser.eat( '=' ) ? readAttributeValue( parser ) : true; + value = parser.eat('=') ? readAttributeValue(parser) : true; } return { @@ -283,42 +324,40 @@ function readAttribute ( parser: Parser, uniqueNames ) { }; } -function readAttributeValue ( parser: Parser ) { - const quoteMark = ( - parser.eat( `'` ) ? `'` : - parser.eat( `"` ) ? `"` : - null - ); +function readAttributeValue(parser: Parser) { + const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null; - const regex = ( - quoteMark === `'` ? /'/ : - quoteMark === `"` ? /"/ : - /[\s"'=<>\/`]/ - ); + const regex = quoteMark === `'` + ? /'/ + : quoteMark === `"` ? /"/ : /[\s"'=<>\/`]/; - const value = readSequence( parser, () => regex.test( parser.template[ parser.index ] ) ); + const value = readSequence(parser, () => + regex.test(parser.template[parser.index]) + ); - if ( quoteMark ) parser.index += 1; + if (quoteMark) parser.index += 1; return value; } -function getShorthandValue ( start: number, name: string ) { +function getShorthandValue(start: number, name: string) { const end = start + name.length; - return [{ - type: 'AttributeShorthand', - start, - end, - expression: { - type: 'Identifier', + return [ + { + type: 'AttributeShorthand', start, end, - name + expression: { + type: 'Identifier', + start, + end, + name + } } - }]; + ]; } -function readSequence ( parser: Parser, done: () => boolean ) { +function readSequence(parser: Parser, done: () => boolean) { let currentChunk: Node = { start: parser.index, end: null, @@ -328,31 +367,30 @@ function readSequence ( parser: Parser, done: () => boolean ) { const chunks = []; - while ( parser.index < parser.template.length ) { + while (parser.index < parser.template.length) { const index = parser.index; - if ( done() ) { + if (done()) { currentChunk.end = parser.index; - if ( currentChunk.data ) chunks.push( currentChunk ); + if (currentChunk.data) chunks.push(currentChunk); - chunks.forEach( chunk => { - if ( chunk.type === 'Text' ) chunk.data = decodeCharacterReferences( chunk.data ); + chunks.forEach(chunk => { + if (chunk.type === 'Text') + chunk.data = decodeCharacterReferences(chunk.data); }); return chunks; - } - - else if ( parser.eat( '{{' ) ) { - if ( currentChunk.data ) { + } else if (parser.eat('{{')) { + if (currentChunk.data) { currentChunk.end = index; - chunks.push( currentChunk ); + chunks.push(currentChunk); } - const expression = readExpression( parser ); + const expression = readExpression(parser); parser.allowWhitespace(); - if ( !parser.eat( '}}' ) ) { - parser.error( `Expected }}` ); + if (!parser.eat('}}')) { + parser.error(`Expected }}`); } chunks.push({ @@ -368,12 +406,10 @@ function readSequence ( parser: Parser, done: () => boolean ) { type: 'Text', data: '' }; - } - - else { - currentChunk.data += parser.template[ parser.index++ ]; + } else { + currentChunk.data += parser.template[parser.index++]; } } - parser.error( `Unexpected end of input` ); -} \ No newline at end of file + parser.error(`Unexpected end of input`); +} diff --git a/src/parse/state/text.ts b/src/parse/state/text.ts index cc46772556f7..a59e00e15ff7 100644 --- a/src/parse/state/text.ts +++ b/src/parse/state/text.ts @@ -1,20 +1,24 @@ import { decodeCharacterReferences } from '../utils/html'; import { Parser } from '../index'; -export default function text ( parser: Parser ) { +export default function text(parser: Parser) { const start = parser.index; let data = ''; - while ( parser.index < parser.template.length && !parser.match( '<' ) && !parser.match( '{{' ) ) { - data += parser.template[ parser.index++ ]; + while ( + parser.index < parser.template.length && + !parser.match('<') && + !parser.match('{{') + ) { + data += parser.template[parser.index++]; } parser.current().children.push({ start, end: parser.index, type: 'Text', - data: decodeCharacterReferences( data ) + data: decodeCharacterReferences(data) }); return null; diff --git a/src/parse/utils/entities.ts b/src/parse/utils/entities.ts index 4b3c924affac..7b43ced2c1d5 100644 --- a/src/parse/utils/entities.ts +++ b/src/parse/utils/entities.ts @@ -2031,4 +2031,4 @@ export default { wp: 8472, wr: 8768, xi: 958 -}; \ No newline at end of file +}; diff --git a/src/parse/utils/hash.ts b/src/parse/utils/hash.ts index 02f98dd20ae7..8f2221ee49b9 100644 --- a/src/parse/utils/hash.ts +++ b/src/parse/utils/hash.ts @@ -1,8 +1,8 @@ // https://github.com/darkskyapp/string-hash/blob/master/index.js -export default function hash ( str: string ) :number { +export default function hash(str: string): number { let hash = 5381; let i = str.length; - while ( i-- ) hash = ( hash * 33 ) ^ str.charCodeAt( i ); + while (i--) hash = (hash * 33) ^ str.charCodeAt(i); return hash >>> 0; } diff --git a/src/parse/utils/html.ts b/src/parse/utils/html.ts index 4b06db07234e..3b26f3bc7318 100644 --- a/src/parse/utils/html.ts +++ b/src/parse/utils/html.ts @@ -1,26 +1,62 @@ import htmlEntities from './entities'; -const windows1252 = [ 8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376 ]; -const entityPattern = new RegExp( `&(#?(?:x[\\w\\d]+|\\d+|${Object.keys( htmlEntities ).join( '|' )}));?`, 'g' ); +const windows1252 = [ + 8364, + 129, + 8218, + 402, + 8222, + 8230, + 8224, + 8225, + 710, + 8240, + 352, + 8249, + 338, + 141, + 381, + 143, + 144, + 8216, + 8217, + 8220, + 8221, + 8226, + 8211, + 8212, + 732, + 8482, + 353, + 8250, + 339, + 157, + 382, + 376 +]; +const entityPattern = new RegExp( + `&(#?(?:x[\\w\\d]+|\\d+|${Object.keys(htmlEntities).join('|')}));?`, + 'g' +); -export function decodeCharacterReferences ( html: string ) { - return html.replace( entityPattern, ( match, entity ) => { +export function decodeCharacterReferences(html: string) { + return html.replace(entityPattern, (match, entity) => { let code; // Handle named entities - if ( entity[0] !== '#' ) { - code = htmlEntities[ entity ]; - } else if ( entity[1] === 'x' ) { - code = parseInt( entity.substring( 2 ), 16 ); + if (entity[0] !== '#') { + code = htmlEntities[entity]; + } else if (entity[1] === 'x') { + code = parseInt(entity.substring(2), 16); } else { - code = parseInt( entity.substring( 1 ), 10 ); + code = parseInt(entity.substring(1), 10); } - if ( !code ) { + if (!code) { return match; } - return String.fromCodePoint( validateCode( code ) ); + return String.fromCodePoint(validateCode(code)); }); } @@ -31,45 +67,45 @@ const NUL = 0; // to replace them ourselves // // Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters -function validateCode ( code: number ) { +function validateCode(code: number) { // line feed becomes generic whitespace - if ( code === 10 ) { + if (code === 10) { return 32; } // ASCII range. (Why someone would use HTML entities for ASCII characters I don't know, but...) - if ( code < 128 ) { + if (code < 128) { return code; } // code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need // to correct the mistake or we'll end up with missing € signs and so on - if ( code <= 159 ) { - return windows1252[ code - 128 ]; + if (code <= 159) { + return windows1252[code - 128]; } // basic multilingual plane - if ( code < 55296 ) { + if (code < 55296) { return code; } // UTF-16 surrogate halves - if ( code <= 57343 ) { + if (code <= 57343) { return NUL; } // rest of the basic multilingual plane - if ( code <= 65535 ) { + if (code <= 65535) { return code; } // supplementary multilingual plane 0x10000 - 0x1ffff - if ( code >= 65536 && code <= 131071 ) { + if (code >= 65536 && code <= 131071) { return code; } // supplementary ideographic plane 0x20000 - 0x2ffff - if ( code >= 131072 && code <= 196607 ) { + if (code >= 131072 && code <= 196607) { return code; } diff --git a/src/server-side-rendering/register.js b/src/server-side-rendering/register.js index 3a1ca580301c..442888e99e5d 100644 --- a/src/server-side-rendering/register.js +++ b/src/server-side-rendering/register.js @@ -2,16 +2,16 @@ import * as fs from 'fs'; import * as path from 'path'; import { compile } from '../index.ts'; -function capitalise ( name ) { - return name[0].toUpperCase() + name.slice( 1 ); +function capitalise(name) { + return name[0].toUpperCase() + name.slice(1); } -require.extensions[ '.html' ] = function ( module, filename ) { - const { code } = compile( fs.readFileSync( filename, 'utf-8' ), { +require.extensions['.html'] = function(module, filename) { + const { code } = compile(fs.readFileSync(filename, 'utf-8'), { filename, - name: capitalise( path.basename( filename ).replace( /\.html$/, '' ) ), + name: capitalise(path.basename(filename).replace(/\.html$/, '')), generate: 'ssr' }); - return module._compile( code, filename ); + return module._compile(code, filename); }; diff --git a/src/shared/_build.js b/src/shared/_build.js index 3d8f1f3ae60e..3bbf0298ff31 100644 --- a/src/shared/_build.js +++ b/src/shared/_build.js @@ -1,35 +1,38 @@ -const fs = require( 'fs' ); -const path = require( 'path' ); -const acorn = require( 'acorn' ); +const fs = require('fs'); +const path = require('path'); +const acorn = require('acorn'); const declarations = {}; -fs.readdirSync( __dirname ).forEach( file => { - if ( !/^[a-z]+\.js$/.test( file ) ) return; +fs.readdirSync(__dirname).forEach(file => { + if (!/^[a-z]+\.js$/.test(file)) return; - const source = fs.readFileSync( path.join( __dirname, file ), 'utf-8' ); - const ast = acorn.parse( source, { + const source = fs.readFileSync(path.join(__dirname, file), 'utf-8'); + const ast = acorn.parse(source, { ecmaVersion: 6, sourceType: 'module' }); - ast.body.forEach( node => { - if ( node.type !== 'ExportNamedDeclaration' ) return; + ast.body.forEach(node => { + if (node.type !== 'ExportNamedDeclaration') return; const declaration = node.declaration; - if ( !declaration ) return; + if (!declaration) return; - const name = declaration.type === 'VariableDeclaration' ? - declaration.declarations[0].id.name : - declaration.id.name; + const name = declaration.type === 'VariableDeclaration' + ? declaration.declarations[0].id.name + : declaration.id.name; - const value = declaration.type === 'VariableDeclaration' ? - declaration.declarations[0].init : - declaration; + const value = declaration.type === 'VariableDeclaration' + ? declaration.declarations[0].init + : declaration; - declarations[ name ] = source.slice( value.start, value.end ); + declarations[name] = source.slice(value.start, value.end); }); }); -fs.writeFileSync( 'src/generators/dom/shared.ts', `// this file is auto-generated, do not edit it -export default ${JSON.stringify( declarations, null, '\t' )};` ); \ No newline at end of file +fs.writeFileSync( + 'src/generators/dom/shared.ts', + `// this file is auto-generated, do not edit it +export default ${JSON.stringify(declarations, null, '\t')};` +); diff --git a/src/shared/dom.js b/src/shared/dom.js index 97c227e7f7b2..11f178317b4b 100644 --- a/src/shared/dom.js +++ b/src/shared/dom.js @@ -1,68 +1,68 @@ -export function appendNode ( node, target ) { - target.appendChild( node ); +export function appendNode(node, target) { + target.appendChild(node); } -export function insertNode ( node, target, anchor ) { - target.insertBefore( node, anchor ); +export function insertNode(node, target, anchor) { + target.insertBefore(node, anchor); } -export function detachNode ( node ) { - node.parentNode.removeChild( node ); +export function detachNode(node) { + node.parentNode.removeChild(node); } -export function detachBetween ( before, after ) { - while ( before.nextSibling && before.nextSibling !== after ) { - before.parentNode.removeChild( before.nextSibling ); +export function detachBetween(before, after) { + while (before.nextSibling && before.nextSibling !== after) { + before.parentNode.removeChild(before.nextSibling); } } // TODO this is out of date -export function destroyEach ( iterations, detach, start ) { - for ( var i = start; i < iterations.length; i += 1 ) { - if ( iterations[i] ) iterations[i].destroy( detach ); +export function destroyEach(iterations, detach, start) { + for (var i = start; i < iterations.length; i += 1) { + if (iterations[i]) iterations[i].destroy(detach); } } -export function createElement ( name ) { - return document.createElement( name ); +export function createElement(name) { + return document.createElement(name); } -export function createSvgElement ( name ) { - return document.createElementNS( 'http://www.w3.org/2000/svg', name ); +export function createSvgElement(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); } -export function createText ( data ) { - return document.createTextNode( data ); +export function createText(data) { + return document.createTextNode(data); } -export function createComment () { - return document.createComment( '' ); +export function createComment() { + return document.createComment(''); } -export function addEventListener ( node, event, handler ) { - node.addEventListener( event, handler, false ); +export function addEventListener(node, event, handler) { + node.addEventListener(event, handler, false); } -export function removeEventListener ( node, event, handler ) { - node.removeEventListener( event, handler, false ); +export function removeEventListener(node, event, handler) { + node.removeEventListener(event, handler, false); } -export function setAttribute ( node, attribute, value ) { - node.setAttribute( attribute, value ); +export function setAttribute(node, attribute, value) { + node.setAttribute(attribute, value); } -export function setXlinkAttribute ( node, attribute, value ) { - node.setAttributeNS( 'http://www.w3.org/1999/xlink', attribute, value ); +export function setXlinkAttribute(node, attribute, value) { + node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value); } -export function getBindingGroupValue ( group ) { +export function getBindingGroupValue(group) { var value = []; - for ( var i = 0; i < group.length; i += 1 ) { - if ( group[i].checked ) value.push( group[i].__value ); + for (var i = 0; i < group.length; i += 1) { + if (group[i].checked) value.push(group[i].__value); } return value; } -export function toNumber ( value ) { +export function toNumber(value) { return value === '' ? undefined : +value; -} \ No newline at end of file +} diff --git a/src/shared/index.js b/src/shared/index.js index d31bbffc1784..b7b5ff7b64b2 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -3,109 +3,116 @@ export * from './dom.js'; export * from './transitions.js'; export * from './utils.js'; -export function differs ( a, b ) { - return ( a !== b ) || ( a && ( typeof a === 'object' ) || ( typeof a === 'function' ) ); +export function differs(a, b) { + return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } -export function dispatchObservers ( component, group, newState, oldState ) { - for ( var key in group ) { - if ( !( key in newState ) ) continue; +export function dispatchObservers(component, group, newState, oldState) { + for (var key in group) { + if (!(key in newState)) continue; - var newValue = newState[ key ]; - var oldValue = oldState[ key ]; + var newValue = newState[key]; + var oldValue = oldState[key]; - if ( differs( newValue, oldValue ) ) { - var callbacks = group[ key ]; - if ( !callbacks ) continue; + if (differs(newValue, oldValue)) { + var callbacks = group[key]; + if (!callbacks) continue; - for ( var i = 0; i < callbacks.length; i += 1 ) { + for (var i = 0; i < callbacks.length; i += 1) { var callback = callbacks[i]; - if ( callback.__calling ) continue; + if (callback.__calling) continue; callback.__calling = true; - callback.call( component, newValue, oldValue ); + callback.call(component, newValue, oldValue); callback.__calling = false; } } } } -export function get ( key ) { - return key ? this._state[ key ] : this._state; +export function get(key) { + return key ? this._state[key] : this._state; } -export function fire ( eventName, data ) { - var handlers = eventName in this._handlers && this._handlers[ eventName ].slice(); - if ( !handlers ) return; +export function fire(eventName, data) { + var handlers = + eventName in this._handlers && this._handlers[eventName].slice(); + if (!handlers) return; - for ( var i = 0; i < handlers.length; i += 1 ) { - handlers[i].call( this, data ); + for (var i = 0; i < handlers.length; i += 1) { + handlers[i].call(this, data); } } -export function observe ( key, callback, options ) { - var group = ( options && options.defer ) ? this._observers.post : this._observers.pre; +export function observe(key, callback, options) { + var group = options && options.defer + ? this._observers.post + : this._observers.pre; - ( group[ key ] || ( group[ key ] = [] ) ).push( callback ); + (group[key] || (group[key] = [])).push(callback); - if ( !options || options.init !== false ) { + if (!options || options.init !== false) { callback.__calling = true; - callback.call( this, this._state[ key ] ); + callback.call(this, this._state[key]); callback.__calling = false; } return { - cancel: function () { - var index = group[ key ].indexOf( callback ); - if ( ~index ) group[ key ].splice( index, 1 ); + cancel: function() { + var index = group[key].indexOf(callback); + if (~index) group[key].splice(index, 1); } }; } -export function observeDev ( key, callback, options ) { - var c = ( key = '' + key ).search( /[^\w]/ ); - if ( c > -1 ) { - var message = "The first argument to component.observe(...) must be the name of a top-level property"; - if ( c > 0 ) message += ", i.e. '" + key.slice( 0, c ) + "' rather than '" + key + "'"; +export function observeDev(key, callback, options) { + var c = (key = '' + key).search(/[^\w]/); + if (c > -1) { + var message = + 'The first argument to component.observe(...) must be the name of a top-level property'; + if (c > 0) + message += ", i.e. '" + key.slice(0, c) + "' rather than '" + key + "'"; - throw new Error( message ); + throw new Error(message); } - return observe.call( this, key, callback, options ); + return observe.call(this, key, callback, options); } -export function on ( eventName, handler ) { - if ( eventName === 'teardown' ) return this.on( 'destroy', handler ); +export function on(eventName, handler) { + if (eventName === 'teardown') return this.on('destroy', handler); - var handlers = this._handlers[ eventName ] || ( this._handlers[ eventName ] = [] ); - handlers.push( handler ); + var handlers = this._handlers[eventName] || (this._handlers[eventName] = []); + handlers.push(handler); return { - cancel: function () { - var index = handlers.indexOf( handler ); - if ( ~index ) handlers.splice( index, 1 ); + cancel: function() { + var index = handlers.indexOf(handler); + if (~index) handlers.splice(index, 1); } }; } -export function onDev ( eventName, handler ) { - if ( eventName === 'teardown' ) { - console.warn( "Use component.on('destroy', ...) instead of component.on('teardown', ...) which has been deprecated and will be unsupported in Svelte 2" ); - return this.on( 'destroy', handler ); +export function onDev(eventName, handler) { + if (eventName === 'teardown') { + console.warn( + "Use component.on('destroy', ...) instead of component.on('teardown', ...) which has been deprecated and will be unsupported in Svelte 2" + ); + return this.on('destroy', handler); } - return on.call( this, eventName, handler ); + return on.call(this, eventName, handler); } -export function set ( newState ) { - this._set( assign( {}, newState ) ); +export function set(newState) { + this._set(assign({}, newState)); this._root._flush(); } -export function _flush () { - if ( !this._renderHooks ) return; +export function _flush() { + if (!this._renderHooks) return; - while ( this._renderHooks.length ) { + while (this._renderHooks.length) { this._renderHooks.pop()(); } } diff --git a/src/shared/transitions.js b/src/shared/transitions.js index 1fe7aa28e63d..ded3a35b4d09 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -1,50 +1,60 @@ import { assign, noop } from './utils.js'; -export function linear ( t ) { +export function linear(t) { return t; } -export function generateKeyframes ( a, b, delta, duration, ease, fn, node, style ) { - var id = '__svelte' + ~~( Math.random() * 1e9 ); // TODO make this more robust +export function generateKeyframes( + a, + b, + delta, + duration, + ease, + fn, + node, + style +) { + var id = '__svelte' + ~~(Math.random() * 1e9); // TODO make this more robust var keyframes = '@keyframes ' + id + '{\n'; - for ( var p = 0; p <= 1; p += 16.666 / duration ) { - var t = a + delta * ease( p ); - keyframes += ( p * 100 ) + '%{' + fn( t ) + '}\n'; + for (var p = 0; p <= 1; p += 16.666 / duration) { + var t = a + delta * ease(p); + keyframes += p * 100 + '%{' + fn(t) + '}\n'; } - keyframes += '100% {' + fn( b ) + '}\n}'; + keyframes += '100% {' + fn(b) + '}\n}'; style.textContent += keyframes; - document.head.appendChild( style ); + document.head.appendChild(style); - node.style.animation = ( node.style.animation || '' ).split( ',' ) - .filter( function ( anim ) { + node.style.animation = (node.style.animation || '') + .split(',') + .filter(function(anim) { // when introing, discard old animations if there are any - return anim && ( delta < 0 || !/__svelte/.test( anim ) ); + return anim && (delta < 0 || !/__svelte/.test(anim)); }) - .concat( id + ' ' + duration + 'ms linear 1 forwards' ) - .join( ', ' ); + .concat(id + ' ' + duration + 'ms linear 1 forwards') + .join(', '); } -export function wrapTransition ( node, fn, params, intro, outgroup ) { - var obj = fn( node, params ); +export function wrapTransition(node, fn, params, intro, outgroup) { + var obj = fn(node, params); var duration = obj.duration || 300; var ease = obj.easing || linear; var cssText; // TODO share