From 7d03b74576f3407f85ce1151dcdff01937d95708 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 23 Mar 2017 08:58:52 -0400 Subject: [PATCH] use maps and sets in many places instead of plain objects --- src/generators/Generator.js | 47 ++++++++-------- src/generators/annotateWithScopes.js | 14 ++--- src/generators/dom/index.js | 30 +++++----- src/generators/dom/visitors/Component.js | 10 ++-- src/generators/dom/visitors/EachBlock.js | 28 +++++----- src/generators/dom/visitors/Element.js | 12 ++-- .../attributes/addComponentAttributes.js | 4 +- .../attributes/addComponentBinding.js | 6 +- .../attributes/addElementAttributes.js | 6 +- .../visitors/attributes/binding/getSetter.js | 12 ++-- src/generators/server-side-rendering/index.js | 4 +- .../visitors/Component.js | 2 +- .../visitors/EachBlock.js | 12 ++-- .../server-side-rendering/visitors/Element.js | 4 +- src/generators/shared/utils/counter.js | 13 +++-- src/parse/state/tag.js | 56 +++++++++---------- src/validate/js/propValidators/computed.js | 7 +-- src/validate/js/propValidators/data.js | 8 +-- src/validate/js/propValidators/methods.js | 11 +--- src/validate/js/utils/checkForDupes.js | 6 +- 20 files changed, 140 insertions(+), 152 deletions(-) diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 464b71d17e..8e3898a04b 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -18,15 +18,15 @@ export default class Generator { this.options = options; this.imports = []; - this.helpers = {}; - this.components = {}; - this.events = {}; + this.helpers = new Set(); + this.components = new Set(); + this.events = new Set(); this.bindingGroups = []; // track which properties are needed, so we can provide useful info // in dev mode - this.expectedProperties = {}; + this.expectedProperties = new Set(); this.elementDepth = 0; @@ -37,11 +37,11 @@ export default class Generator { // allow compiler to deconflict user's `import { get } from 'whatever'` and // Svelte's builtin `import { get, ... } from 'svelte/shared.js'`; - this.importedNames = {}; + this.importedNames = new Set(); - this.aliases = {}; + this.aliases = new Map(); - this._callbacks = {}; + this._callbacks = new Map(); } addSourcemapLocations ( node ) { @@ -54,17 +54,17 @@ export default class Generator { } alias ( name ) { - if ( !( name in this.aliases ) ) { + if ( !( this.aliases.has( name ) ) ) { let alias = name; let i = 1; while ( alias in this.importedNames ) { alias = `${name}$${i++}`; } - this.aliases[ name ] = alias; + this.aliases.set( name, alias ); } - return this.aliases[ name ]; + return this.aliases.get( name ); } contextualise ( expression, isEventHandler ) { @@ -91,7 +91,7 @@ export default class Generator { const { name } = flattenReference( node ); if ( scope.has( name ) ) return; - if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) { + if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers.has( name ) ) { code.prependRight( node.start, `${self.alias( 'template' )}.helpers.` ); } @@ -99,19 +99,19 @@ export default class Generator { // noop } - else if ( name in contexts ) { - const context = contexts[ name ]; + else if ( contexts.has( name ) ) { + const context = contexts.get( name ); if ( context !== name ) { // this is true for 'reserved' names like `root` and `component` code.overwrite( node.start, node.start + name.length, context, true ); } - dependencies.push( ...contextDependencies[ name ] ); + dependencies.push( ...contextDependencies.get( name ) ); if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name ); } - else if ( indexes[ name ] ) { - const context = indexes[ name ]; + else if ( indexes.has( name ) ) { + const context = indexes.get( name ); if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context ); } @@ -145,7 +145,7 @@ export default class Generator { }); dependencies.forEach( name => { - this.expectedProperties[ name ] = true; + this.expectedProperties.add( name ); }); return { @@ -157,7 +157,7 @@ export default class Generator { } fire ( eventName, data ) { - const handlers = eventName in this._callbacks && this._callbacks[ eventName ].slice(); + const handlers = this._callbacks.has( eventName ) && this._callbacks.get( eventName ).slice(); if ( !handlers ) return; for ( let i = 0; i < handlers.length; i += 1 ) { @@ -287,7 +287,7 @@ export default class Generator { } else { const { declarations } = annotateWithScopes( js ); let template = 'template'; - for ( let i = 1; template in declarations; template = `template$${i++}` ); + for ( let i = 1; declarations.has( template ); template = `template$${i++}` ); this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var ${template} = ` ); @@ -312,7 +312,7 @@ export default class Generator { [ 'helpers', 'events', 'components' ].forEach( key => { if ( templateProperties[ key ] ) { templateProperties[ key ].value.properties.forEach( prop => { - this[ key ][ prop.key.name ] = prop.value; + this[ key ].add( prop.key.name ); }); } }); @@ -354,8 +354,11 @@ export default class Generator { } on ( eventName, handler ) { - const handlers = this._callbacks[ eventName ] || ( this._callbacks[ eventName ] = [] ); - handlers.push( handler ); + if ( this._callbacks.has( eventName ) ) { + this._callbacks.get( eventName ).push( handler ); + } else { + this._callbacks.set( eventName, [ handler ] ); + } } pop () { diff --git a/src/generators/annotateWithScopes.js b/src/generators/annotateWithScopes.js index a598ef22d1..cc7926e6b9 100644 --- a/src/generators/annotateWithScopes.js +++ b/src/generators/annotateWithScopes.js @@ -7,15 +7,15 @@ export default function annotateWithScopes ( expression ) { enter ( node ) { if ( /Function/.test( node.type ) ) { if ( node.type === 'FunctionDeclaration' ) { - scope.declarations[ node.id.name ] = true; + scope.declarations.add( node.id.name ); } else { node._scope = scope = new Scope( scope, false ); - if ( node.id ) scope.declarations[ node.id.name ] = true; + if ( node.id ) scope.declarations.add( node.id.name ); } node.params.forEach( param => { extractNames( param ).forEach( name => { - scope.declarations[ name ] = true; + scope.declarations.add( name ); }); }); } @@ -47,7 +47,7 @@ class Scope { constructor ( parent, block ) { this.parent = parent; this.block = block; - this.declarations = Object.create( null ); + this.declarations = new Set(); } addDeclaration ( node ) { @@ -56,16 +56,16 @@ class Scope { } else if ( node.type === 'VariableDeclaration' ) { node.declarations.forEach( declarator => { extractNames( declarator.id ).forEach( name => { - this.declarations[ name ] = true; + this.declarations.add( name ); }); }); } else { - this.declarations[ node.id.name ] = true; + this.declarations.add( node.id.name ); } } has ( name ) { - return name in this.declarations || this.parent && this.parent.has( name ); + return this.declarations.has( name ) || this.parent && this.parent.has( name ); } } diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 4d386affd3..24a8753476 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -12,9 +12,9 @@ class DomGenerator extends Generator { constructor ( parsed, source, name, names, visitors, options ) { super( parsed, source, name, names, visitors, options ); this.renderers = []; - this.uses = {}; + this.uses = new Set(); - this.importedComponents = {}; + this.importedComponents = new Map(); } addElement ( name, renderStatement, needsIdentifier = false ) { @@ -134,7 +134,7 @@ class DomGenerator extends Generator { name = `${name}Dev`; } - this.uses[ name ] = true; + this.uses.add( name ); return this.alias( name ); } @@ -174,15 +174,15 @@ export default function dom ( parsed, source, options, names ) { templateProperties.components.value.properties.forEach( property => { const key = property.key.name; const value = source.slice( property.value.start, property.value.end ); - if ( generator.importedNames[ value ] ) { - generator.importedComponents[ key ] = value; + if ( generator.importedNames.has( value ) ) { + generator.importedComponents.set( key, value ); } else { hasNonImportedComponent = true; } }); if ( hasNonImportedComponent ) { // remove the specific components that were imported, as we'll refer to them directly - Object.keys( generator.importedComponents ).forEach( key => { + Array.from( generator.importedComponents.keys() ).forEach( key => { removeObjectKey( generator, templateProperties.components.value, key ); }); } else { @@ -198,12 +198,12 @@ export default function dom ( parsed, source, options, names ) { localElementDepth: 0, key: null, - contexts: {}, - indexes: {}, + contexts: new Map(), + indexes: new Map(), params: [ 'root' ], - indexNames: {}, - listNames: {}, + indexNames: new Map(), + listNames: new Map(), builders: getBuilders(), getUniqueName: generator.getUniqueNameMaker() @@ -336,7 +336,7 @@ export default function dom ( parsed, source, options, names ) { } if ( options.dev ) { - Object.keys( generator.expectedProperties ).forEach( prop => { + generator.expectedProperties.forEach( prop => { constructorBlock.addLine( `if ( !( '${prop}' in this._state ) ) throw new Error( "Component was created without expected data property '${prop}'" );` ); @@ -408,17 +408,17 @@ export default function dom ( parsed, source, options, names ) { throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` ); } - const names = Object.keys( generator.uses ).map( name => { - return name !== generator.aliases[ name ] ? `${name} as ${generator.aliases[ name ]}` : name; + const names = Array.from( generator.uses ).map( name => { + return name !== generator.aliases.get( name ) ? `${name} as ${generator.aliases.get( name )}` : name; }); builders.main.addLineAtStart( `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` ); } else { - Object.keys( generator.uses ).forEach( key => { + generator.uses.forEach( key => { const fn = shared[ key ]; // eslint-disable-line import/namespace - builders.main.addBlock( fn.toString().replace( /^function [^(]*/, 'function ' + generator.aliases[ key ] ) ); + builders.main.addBlock( fn.toString().replace( /^function [^(]*/, 'function ' + generator.aliases.get( key ) ) ); }); } diff --git a/src/generators/dom/visitors/Component.js b/src/generators/dom/visitors/Component.js index f6fae1392e..01b0621e03 100644 --- a/src/generators/dom/visitors/Component.js +++ b/src/generators/dom/visitors/Component.js @@ -32,8 +32,8 @@ export default { const initialProps = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `root: root`; - const listName = generator.current.listNames[ contextName ]; - const indexName = generator.current.indexNames[ contextName ]; + const listName = generator.current.listNames.get( contextName ); + const indexName = generator.current.indexNames.get( contextName ); return `${listName}: ${listName},\n${indexName}: ${indexName}`; }).join( ',\n' ); @@ -41,8 +41,8 @@ export default { const updates = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `${name}._context.root = root;`; - const listName = generator.current.listNames[ contextName ]; - const indexName = generator.current.indexNames[ contextName ]; + const listName = generator.current.listNames.get( contextName ); + const indexName = generator.current.indexNames.get( contextName ); return `${name}._context.${listName} = ${listName};\n${name}._context.${indexName} = ${indexName};`; }).join( '\n' ); @@ -106,7 +106,7 @@ export default { componentInitProperties.push(`data: ${name}_initialData`); } - const expression = node.name === ':Self' ? generator.name : generator.importedComponents[ node.name ] || `${generator.alias( 'template' )}.components.${node.name}`; + const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get( node.name ) || `${generator.alias( 'template' )}.components.${node.name}`; local.init.addBlockAtStart( deindent` ${statements.join( '\n\n' )} diff --git a/src/generators/dom/visitors/EachBlock.js b/src/generators/dom/visitors/EachBlock.js index 8113e93803..a6819059e3 100644 --- a/src/generators/dom/visitors/EachBlock.js +++ b/src/generators/dom/visitors/EachBlock.js @@ -2,10 +2,7 @@ import CodeBuilder from '../../../utils/CodeBuilder.js'; import deindent from '../../../utils/deindent.js'; import getBuilders from '../utils/getBuilders.js'; -const reserved = { - component: true, - root: true -}; +const reserved = new Set( [ 'component', 'root' ] ); export default { enter ( generator, node ) { @@ -171,28 +168,29 @@ export default { generator.generateBlock( node.else, renderElse ); } - const indexNames = Object.assign( {}, generator.current.indexNames ); - const indexName = indexNames[ node.context ] = ( node.index || `${node.context}__index` ); + const indexNames = new Map( generator.current.indexNames ); + const indexName = node.index || `${node.context}__index`; + indexNames.set( node.context, indexName ); - const listNames = Object.assign( {}, generator.current.listNames ); - listNames[ node.context ] = listName; + const listNames = new Map( generator.current.listNames ); + listNames.set( node.context, listName ); // ensure that contexts like `root` or `component` don't blow up the whole show let context = node.context; let c = 1; - while ( context in reserved || ~generator.current.params.indexOf( context ) ) { + while ( reserved.has( context ) || ~generator.current.params.indexOf( context ) ) { context = `${node.context}$${c++}`; } - const contexts = Object.assign( {}, generator.current.contexts ); - contexts[ node.context ] = context; + const contexts = new Map( generator.current.contexts ); + contexts.set( node.context, context ); - const indexes = Object.assign( {}, generator.current.indexes ); - if ( node.index ) indexes[ indexName ] = node.context; + const indexes = new Map( generator.current.indexes ); + if ( node.index ) indexes.set( indexName, node.context ); - const contextDependencies = Object.assign( {}, generator.current.contextDependencies ); - contextDependencies[ node.context ] = dependencies; + const contextDependencies = new Map( generator.current.contextDependencies ); + contextDependencies.set( node.context, dependencies ); const blockParams = generator.current.params.concat( listName, context, indexName ); diff --git a/src/generators/dom/visitors/Element.js b/src/generators/dom/visitors/Element.js index 0e76e0d708..0526e5fc71 100644 --- a/src/generators/dom/visitors/Element.js +++ b/src/generators/dom/visitors/Element.js @@ -5,7 +5,7 @@ import Component from './Component.js'; export default { enter ( generator, node ) { - const isComponent = node.name in generator.components || node.name === ':Self'; + const isComponent = generator.components.has( node.name ) || node.name === ':Self'; if ( isComponent ) { return Component.enter( generator, node ); } @@ -32,8 +32,8 @@ export default { const initialProps = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `root: root`; - const listName = generator.current.listNames[ contextName ]; - const indexName = generator.current.indexNames[ contextName ]; + const listName = generator.current.listNames.get( contextName ); + const indexName = generator.current.indexNames.get( contextName ); return `${listName}: ${listName},\n${indexName}: ${indexName}`; }).join( ',\n' ); @@ -41,8 +41,8 @@ export default { const updates = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `${name}.__svelte.root = root;`; - const listName = generator.current.listNames[ contextName ]; - const indexName = generator.current.indexNames[ contextName ]; + const listName = generator.current.listNames.get( contextName ); + const indexName = generator.current.indexNames.get( contextName ); return `${name}.__svelte.${listName} = ${listName};\n${name}.__svelte.${indexName} = ${indexName};`; }).join( '\n' ); @@ -100,7 +100,7 @@ export default { }, leave ( generator, node ) { - const isComponent = node.name in generator.components; + const isComponent = generator.components.has( node.name ); if ( isComponent ) { return Component.leave( generator, node ); } diff --git a/src/generators/dom/visitors/attributes/addComponentAttributes.js b/src/generators/dom/visitors/attributes/addComponentAttributes.js index a5da73babc..8e5c5c92a6 100644 --- a/src/generators/dom/visitors/attributes/addComponentAttributes.js +++ b/src/generators/dom/visitors/attributes/addComponentAttributes.js @@ -94,8 +94,8 @@ export default function addComponentAttributes ( generator, node, local ) { const declarations = usedContexts.map( name => { if ( name === 'root' ) return 'var root = this._context.root;'; - const listName = generator.current.listNames[ name ]; - const indexName = generator.current.indexNames[ name ]; + const listName = generator.current.listNames.get( name ); + const indexName = generator.current.indexNames.get( name ); return `var ${listName} = this._context.${listName}, ${indexName} = this._context.${indexName}, ${name} = ${listName}[${indexName}]`; }); diff --git a/src/generators/dom/visitors/attributes/addComponentBinding.js b/src/generators/dom/visitors/attributes/addComponentBinding.js index f7ca07a106..baee355619 100644 --- a/src/generators/dom/visitors/attributes/addComponentBinding.js +++ b/src/generators/dom/visitors/attributes/addComponentBinding.js @@ -12,14 +12,14 @@ export default function createBinding ( generator, node, attribute, current, loc if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context ); }); - const contextual = name in current.contexts; + const contextual = current.contexts.has( name ); let obj; let prop; if ( contextual ) { - obj = current.listNames[ name ]; - prop = current.indexNames[ name ]; + obj = current.listNames.get( name ); + prop = current.indexNames.get( name ); } else if ( attribute.value.type === 'MemberExpression' ) { prop = `'[✂${attribute.value.property.start}-${attribute.value.property.end}✂]}'`; obj = `root.[✂${attribute.value.object.start}-${attribute.value.object.end}✂]}`; diff --git a/src/generators/dom/visitors/attributes/addElementAttributes.js b/src/generators/dom/visitors/attributes/addElementAttributes.js index 191e612a0e..c30c5d0cc2 100644 --- a/src/generators/dom/visitors/attributes/addElementAttributes.js +++ b/src/generators/dom/visitors/attributes/addElementAttributes.js @@ -171,8 +171,8 @@ export default function addElementAttributes ( generator, node, local ) { const declarations = usedContexts.map( name => { if ( name === 'root' ) return 'var root = this.__svelte.root;'; - const listName = generator.current.listNames[ name ]; - const indexName = generator.current.indexNames[ name ]; + const listName = generator.current.listNames.get( name ); + const indexName = generator.current.indexNames.get( name ); return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`; }); @@ -180,7 +180,7 @@ export default function addElementAttributes ( generator, node, local ) { const handlerName = generator.current.getUniqueName( `${name}Handler` ); const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`; - if ( name in generator.events ) { + if ( generator.events.has( name ) ) { local.init.addBlock( deindent` var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( component, ${local.name}, function ( event ) { ${handlerBody} diff --git a/src/generators/dom/visitors/attributes/binding/getSetter.js b/src/generators/dom/visitors/attributes/binding/getSetter.js index 64c822df7c..a81fc9277a 100644 --- a/src/generators/dom/visitors/attributes/binding/getSetter.js +++ b/src/generators/dom/visitors/attributes/binding/getSetter.js @@ -1,19 +1,19 @@ import deindent from '../../../../../utils/deindent.js'; export default function getSetter ({ current, name, context, attribute, dependencies, snippet, value }) { - if ( name in current.contexts ) { + if ( current.contexts.has( name ) ) { const prop = dependencies[0]; const tail = attribute.value.type === 'MemberExpression' ? getTailSnippet( attribute.value ) : ''; return deindent` - var list = this.${context}.${current.listNames[ name ]}; - var index = this.${context}.${current.indexNames[ name ]}; + var list = this.${context}.${current.listNames.get( name )}; + var index = this.${context}.${current.indexNames.get( name )}; list[index]${tail} = ${value}; component._set({ ${prop}: component.get( '${prop}' ) }); `; } - + if ( attribute.value.type === 'MemberExpression' ) { return deindent` var ${name} = component.get( '${name}' ); @@ -21,7 +21,7 @@ export default function getSetter ({ current, name, context, attribute, dependen component._set({ ${name}: ${name} }); `; } - + return `component._set({ ${name}: ${value} });`; } @@ -31,4 +31,4 @@ function getTailSnippet ( node ) { const start = node.end; return `[✂${start}-${end}✂]`; -} \ No newline at end of file +} diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index 0a4fd2afa0..a2bb1bebb3 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -52,8 +52,8 @@ export default function ssr ( parsed, source, options, names ) { // create main render() function generator.push({ - contexts: {}, - indexes: {}, + contexts: new Map(), + indexes: new Map(), conditions: [] }); diff --git a/src/generators/server-side-rendering/visitors/Component.js b/src/generators/server-side-rendering/visitors/Component.js index 0a13ade27c..a2bb11d3bc 100644 --- a/src/generators/server-side-rendering/visitors/Component.js +++ b/src/generators/server-side-rendering/visitors/Component.js @@ -45,7 +45,7 @@ export default { }) .concat( bindings.map( binding => { const { name, keypath } = flattenReference( binding.value ); - const value = name in generator.current.contexts ? keypath : `root.${keypath}`; + const value = generator.current.contexts.has( name ) ? keypath : `root.${keypath}`; return `${binding.name}: ${value}`; })) .join( ', ' ); diff --git a/src/generators/server-side-rendering/visitors/EachBlock.js b/src/generators/server-side-rendering/visitors/EachBlock.js index 70a9527a1a..0fbe3ee995 100644 --- a/src/generators/server-side-rendering/visitors/EachBlock.js +++ b/src/generators/server-side-rendering/visitors/EachBlock.js @@ -7,14 +7,14 @@ export default { // TODO should this be the generator's job? It's duplicated between // here and the equivalent DOM compiler visitor - const contexts = Object.assign( {}, generator.current.contexts ); - contexts[ node.context ] = node.context; + const contexts = new Map( generator.current.contexts ); + contexts.set( node.context, node.context ); - const indexes = Object.assign( {}, generator.current.indexes ); - if ( node.index ) indexes[ node.index ] = node.context; + const indexes = new Map( generator.current.indexes ); + if ( node.index ) indexes.set( node.index, node.context ); - const contextDependencies = Object.assign( {}, generator.current.contextDependencies ); - contextDependencies[ node.context ] = dependencies; + const contextDependencies = new Map( generator.current.contextDependencies ); + contextDependencies.set( node.context, dependencies ); generator.push({ contexts, diff --git a/src/generators/server-side-rendering/visitors/Element.js b/src/generators/server-side-rendering/visitors/Element.js index 4b750bf7b1..971b54d658 100644 --- a/src/generators/server-side-rendering/visitors/Element.js +++ b/src/generators/server-side-rendering/visitors/Element.js @@ -3,7 +3,7 @@ import isVoidElementName from '../../../utils/isVoidElementName.js'; export default { enter ( generator, node ) { - if ( node.name in generator.components || node.name === ':Self' ) { + if ( generator.components.has( node.name ) || node.name === ':Self' ) { Component.enter( generator, node ); return; } @@ -39,7 +39,7 @@ export default { }, leave ( generator, node ) { - if ( node.name in generator.components || node.name === ':Self' ) { + if ( generator.components.has( node.name ) || node.name === ':Self' ) { Component.leave( generator, node ); return; } diff --git a/src/generators/shared/utils/counter.js b/src/generators/shared/utils/counter.js index 7a954ccf59..da00544ca5 100644 --- a/src/generators/shared/utils/counter.js +++ b/src/generators/shared/utils/counter.js @@ -1,14 +1,17 @@ export default function counter ( used ) { - const counts = {}; + const counts = new Map(); - used.forEach( name => counts[ name ] = 1 ); + used.forEach( name => counts.set( name, 1 ) ); return function ( name ) { - if ( name in counts ) { - return `${name}${counts[ name ]++}`; + if ( counts.has( name ) ) { + const count = counts.get( name ); + const newName = `${name}${count}`; + counts.set( name, count + 1 ); + return newName; } - counts[ name ] = 1; + counts.set( name, 1 ); return name; }; } diff --git a/src/parse/state/tag.js b/src/parse/state/tag.js index f315ef69e4..361f6f49f0 100644 --- a/src/parse/state/tag.js +++ b/src/parse/state/tag.js @@ -11,35 +11,34 @@ const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/; const SELF = ':Self'; -const specials = { - script: { +const specials = new Map( [ + [ 'script', { read: readScript, property: 'js' - }, - - style: { + } ], + [ 'style', { read: readStyle, property: 'css' - } -}; + } ] +] ); // based on http://developers.whatwg.org/syntax.html#syntax-tag-omission -const disallowedContents = { - li: [ 'li' ], - dt: [ 'dt', 'dd' ], - dd: [ 'dt', 'dd' ], - p: 'address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split( ' ' ), - rt: [ 'rt', 'rp' ], - rp: [ 'rt', 'rp' ], - optgroup: [ 'optgroup' ], - option: [ 'option', 'optgroup' ], - thead: [ 'tbody', 'tfoot' ], - tbody: [ 'tbody', 'tfoot' ], - tfoot: [ 'tbody' ], - tr: [ 'tr', 'tbody' ], - td: [ 'td', 'th', 'tr' ], - th: [ 'td', 'th', 'tr' ] -}; +const disallowedContents = new Map( [ + [ 'li', new Set( [ 'li' ] ) ], + [ 'dt', new Set( [ 'dt', 'dd' ] ) ], + [ 'dd', new Set( [ 'dt', 'dd' ] ) ], + [ 'p', new Set( 'address article aside blockquote div dl fieldset footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav ol p pre section table ul'.split( ' ' ) ) ], + [ 'rt', new Set( [ 'rt', 'rp' ] ) ], + [ 'rp', new Set( [ 'rt', 'rp' ] ) ], + [ 'optgroup', new Set( [ 'optgroup' ] ) ], + [ 'option', new Set( [ 'option', 'optgroup' ] ) ], + [ 'thead', new Set( [ 'tbody', 'tfoot' ] ) ], + [ 'tbody', new Set( [ 'tbody', 'tfoot' ] ) ], + [ 'tfoot', new Set( [ 'tbody' ] ) ], + [ 'tr', new Set( [ 'tr', 'tbody' ] ) ], + [ 'td', new Set( [ 'td', 'th', 'tr' ] ) ], + [ 'th', new Set( [ 'td', 'th', 'tr' ] ) ], +] ); function stripWhitespace ( element ) { if ( element.children.length ) { @@ -107,11 +106,10 @@ export default function tag ( parser ) { parser.stack.pop(); return null; - } else if ( parent.name in disallowedContents ) { + } else if ( disallowedContents.has( parent.name ) ) { // can this be a child of the parent element, or does it implicitly // close it, like `
  • one
  • two`? - const disallowed = disallowedContents[ parent.name ]; - if ( ~disallowed.indexOf( name ) ) { + if ( disallowedContents.get( parent.name ).has( name ) ) { stripWhitespace( parent ); parent.end = start; @@ -131,8 +129,8 @@ export default function tag ( parser ) { parser.allowWhitespace(); // special cases – top-level