diff --git a/src/Bundle.js b/src/Bundle.js index 829847b..3de5c51 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -1,7 +1,7 @@ import { basename, dirname, extname, relative, resolve } from 'path'; import { readFile, Promise } from 'sander'; import MagicString from 'magic-string'; -import { keys, has } from './utils/object'; +import { blank, keys } from './utils/object'; import Module from './Module'; import ExternalModule from './ExternalModule'; import finalisers from './finalisers/index'; @@ -32,7 +32,7 @@ export default class Bundle { }; this.entryModule = null; - this.modulePromises = {}; + this.modulePromises = blank(); this.statements = []; this.externalModules = []; this.internalNamespaceModules = []; @@ -43,7 +43,7 @@ export default class Bundle { .then( path => { if ( !path ) { // external module - if ( !has( this.modulePromises, importee ) ) { + if ( !this.modulePromises[ importee ] ) { const module = new ExternalModule( importee ); this.externalModules.push( module ); this.modulePromises[ importee ] = Promise.resolve( module ); @@ -52,7 +52,7 @@ export default class Bundle { return this.modulePromises[ importee ]; } - if ( !has( this.modulePromises, path ) ) { + if ( !this.modulePromises[ path ] ) { this.modulePromises[ path ] = Promise.resolve( this.load( path, this.loadOptions ) ) .then( source => { const module = new Module({ @@ -99,8 +99,8 @@ export default class Bundle { } deconflict () { - let definers = {}; - let conflicts = {}; + let definers = blank(); + let conflicts = blank(); // Discover conflicts (i.e. two statements in separate modules both define `foo`) this.statements.forEach( statement => { @@ -122,7 +122,7 @@ export default class Bundle { } names.forEach( name => { - if ( has( definers, name ) ) { + if ( definers[ name ] ) { conflicts[ name ] = true; } else { definers[ name ] = []; @@ -139,7 +139,7 @@ export default class Bundle { // TODO is this right? let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id ); - if ( has( definers, name ) ) { + if ( definers[ name ] ) { conflicts[ name ] = true; } else { definers[ name ] = []; @@ -162,7 +162,7 @@ export default class Bundle { }); function getSafeName ( name ) { - while ( has( conflicts, name ) ) { + while ( conflicts[ name ] ) { name = `_${name}`; } @@ -181,7 +181,7 @@ export default class Bundle { // Apply new names and add to the output bundle this.statements.forEach( statement => { - let replacements = {}; + let replacements = blank(); keys( statement.dependsOn ) .concat( keys( statement.defines ) ) diff --git a/src/ExternalModule.js b/src/ExternalModule.js index 929312f..367996a 100644 --- a/src/ExternalModule.js +++ b/src/ExternalModule.js @@ -1,3 +1,5 @@ +import { blank } from './utils/object'; + export default class ExternalModule { constructor ( id ) { this.id = id; @@ -6,8 +8,8 @@ export default class ExternalModule { this.isExternal = true; this.importedByBundle = []; - this.canonicalNames = {}; - this.suggestedNames = {}; + this.canonicalNames = blank(); + this.suggestedNames = blank(); this.needsDefault = false; this.needsNamed = false; diff --git a/src/Module.js b/src/Module.js index 4103f33..588370a 100644 --- a/src/Module.js +++ b/src/Module.js @@ -4,7 +4,7 @@ import MagicString from 'magic-string'; import Statement from './Statement'; import walk from './ast/walk'; import analyse from './ast/analyse'; -import { has, keys } from './utils/object'; +import { blank, keys } from './utils/object'; import { sequence } from './utils/promise'; import { isImportDeclaration, isExportDeclaration } from './utils/map-helpers'; import getLocation from './utils/getLocation'; @@ -23,7 +23,7 @@ export default class Module { filename: path }); - this.suggestedNames = {}; + this.suggestedNames = blank(); this.comments = []; // Try to extract a list of top-level statements/declarations. If @@ -60,8 +60,8 @@ export default class Module { analyse () { // imports and exports, indexed by ID - this.imports = {}; - this.exports = {}; + this.imports = blank(); + this.exports = blank(); this.importDeclarations.forEach( statement => { const node = statement.node; @@ -74,7 +74,7 @@ export default class Module { const localName = specifier.local.name; const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name; - if ( has( this.imports, localName ) ) { + if ( this.imports[ localName ] ) { const err = new Error( `Duplicated import '${localName}'` ); err.file = this.path; err.loc = getLocation( this.source, specifier.start ); @@ -157,11 +157,11 @@ export default class Module { analyse( this.magicString, this ); - this.canonicalNames = {}; + this.canonicalNames = blank(); - this.definitions = {}; - this.definitionPromises = {}; - this.modifications = {}; + this.definitions = blank(); + this.definitionPromises = blank(); + this.modifications = blank(); this.statements.forEach( statement => { keys( statement.defines ).forEach( name => { @@ -169,7 +169,7 @@ export default class Module { }); keys( statement.modifies ).forEach( name => { - if ( !has( this.modifications, name ) ) { + if ( !this.modifications[ name ] ) { this.modifications[ name ] = []; } @@ -179,14 +179,14 @@ export default class Module { } getCanonicalName ( localName ) { - if ( has( this.suggestedNames, localName ) ) { + if ( this.suggestedNames[ localName ] ) { localName = this.suggestedNames[ localName ]; } - if ( !has( this.canonicalNames, localName ) ) { + if ( !this.canonicalNames[ localName ] ) { let canonicalName; - if ( has( this.imports, localName ) ) { + if ( this.imports[ localName ] ) { const importDeclaration = this.imports[ localName ]; const module = importDeclaration.module; @@ -218,14 +218,14 @@ export default class Module { define ( name ) { // shortcut cycles. TODO this won't work everywhere... - if ( has( this.definitionPromises, name ) ) { + if ( this.definitionPromises[ name ] ) { return emptyArrayPromise; } let promise; // The definition for this name is in a different module - if ( has( this.imports, name ) ) { + if ( this.imports[ name ] ) { const importDeclaration = this.imports[ name ]; promise = this.bundle.fetchModule( importDeclaration.source, this.path ) @@ -236,17 +236,17 @@ export default class Module { if ( importDeclaration.name === 'default' ) { // TODO this seems ropey const localName = importDeclaration.localName; - let suggestion = has( this.suggestedNames, localName ) ? this.suggestedNames[ localName ] : localName; + let suggestion = this.suggestedNames[ localName ] || localName; // special case - the module has its own import by this name - while ( !module.isExternal && has( module.imports, suggestion ) ) { + while ( !module.isExternal && module.imports[ suggestion ] ) { suggestion = `_${suggestion}`; } module.suggestName( 'default', suggestion ); } else if ( importDeclaration.name === '*' ) { const localName = importDeclaration.localName; - const suggestion = has( this.suggestedNames, localName ) ? this.suggestedNames[ localName ] : localName; + const suggestion = this.suggestedNames[ localName ] || localName; module.suggestName( '*', suggestion ); module.suggestName( 'default', `${suggestion}__default` ); } diff --git a/src/Statement.js b/src/Statement.js index 5d8fd24..9abccaa 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -1,4 +1,4 @@ -import { has, keys } from './utils/object'; +import { blank, keys } from './utils/object'; import { sequence } from './utils/promise'; import { getName } from './utils/map-helpers'; import getLocation from './utils/getLocation'; @@ -14,9 +14,9 @@ export default class Statement { this.magicString = magicString; this.scope = new Scope(); - this.defines = {}; - this.modifies = {}; - this.dependsOn = {}; + this.defines = blank(); + this.modifies = blank(); + this.dependsOn = blank(); this.isIncluded = false; @@ -226,7 +226,7 @@ export default class Statement { // thing(s) this statement defines .then( () => { return sequence( keys( this.defines ), name => { - const modifications = has( this.module.modifications, name ) && this.module.modifications[ name ]; + const modifications = this.module.modifications[ name ]; if ( modifications ) { return sequence( modifications, statement => { @@ -264,7 +264,7 @@ export default class Statement { const scope = node._scope; if ( scope ) { - let newNames = {}; + let newNames = blank(); let hasReplacements; keys( names ).forEach( key => { @@ -295,7 +295,7 @@ export default class Statement { if ( parent.type === 'Property' && node !== parent.value ) return; // TODO others...? - const name = has( names, node.name ) && names[ node.name ]; + const name = names[ node.name ]; if ( name && name !== node.name ) { magicString.overwrite( node.start, node.end, name ); diff --git a/src/ast/walk.js b/src/ast/walk.js index a6202a1..b91c68b 100644 --- a/src/ast/walk.js +++ b/src/ast/walk.js @@ -1,3 +1,5 @@ +import { blank } from '../utils/object'; + let shouldSkip; let shouldAbort; @@ -11,7 +13,7 @@ let context = { abort: () => shouldAbort = true }; -let childKeys = {}; +let childKeys = blank(); let toString = Object.prototype.toString; @@ -54,4 +56,4 @@ function visit ( node, parent, enter, leave ) { if ( leave && !shouldAbort ) { leave( node, parent ); } -} \ No newline at end of file +} diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index 0fae690..53f8d81 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -1,4 +1,3 @@ -import { has } from '../utils/object'; import { getName, quoteId } from '../utils/map-helpers'; export default function amd ( bundle, magicString, exportMode, options ) { @@ -11,7 +10,7 @@ export default function amd ( bundle, magicString, exportMode, options ) { } const params = - ( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) + + ( options.moduleId ? `['${options.moduleId}'], ` : `` ) + ( deps.length ? `[${deps.join( ', ' )}], ` : `` ); const intro = `define(${params}function (${args.join( ', ' )}) { 'use strict';\n\n`; diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 1e0665c..df32a66 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -1,11 +1,11 @@ -import { has } from '../utils/object'; +import { blank } from '../utils/object'; import { getName } from '../utils/map-helpers'; export default function iife ( bundle, magicString, exportMode, options ) { - const globalNames = options.globals || {}; + const globalNames = options.globals || blank(); let dependencies = bundle.externalModules.map( module => { - return has( globalNames, module.id ) ? globalNames[ module.id ] : module.name; + return globalNames[ module.id ] || module.name; }); let args = bundle.externalModules.map( getName ); diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index fa69822..125b0f4 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -1,4 +1,4 @@ -import { has } from '../utils/object'; +import { blank } from '../utils/object'; import { getName, quoteId, req } from '../utils/map-helpers'; export default function umd ( bundle, magicString, exportMode, options ) { @@ -8,12 +8,12 @@ export default function umd ( bundle, magicString, exportMode, options ) { const indentStr = magicString.getIndentString(); - const globalNames = options.globals || {}; + const globalNames = options.globals || blank(); let amdDeps = bundle.externalModules.map( quoteId ); let cjsDeps = bundle.externalModules.map( req ); let globalDeps = bundle.externalModules.map( module => { - return has( globalNames, module.id ) ? globalNames[ module.id ] : module.name; + return globalNames[ module.id ] || module.name; }); let args = bundle.externalModules.map( getName ); @@ -27,7 +27,7 @@ export default function umd ( bundle, magicString, exportMode, options ) { } const amdParams = - ( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) + + ( options.moduleId ? `['${options.moduleId}'], ` : `` ) + ( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` ); const cjsExport = exportMode === 'default' ? `module.exports = ` : ``; diff --git a/src/utils/makeLegalIdentifier.js b/src/utils/makeLegalIdentifier.js index d11482f..7e0a846 100644 --- a/src/utils/makeLegalIdentifier.js +++ b/src/utils/makeLegalIdentifier.js @@ -1,7 +1,9 @@ +import { blank } from './object'; + const reservedWords = 'break case class catch const continue debugger default delete do else export extends finally for function if import in instanceof let new return super switch this throw try typeof var void while with yield enum await implements package protected static interface private public'.split( ' ' ); const builtins = 'Infinity NaN undefined null true false eval uneval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Symbol Error EvalError InternalError RangeError ReferenceError SyntaxError TypeError URIError Number Math Date String RegExp Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Map Set WeakMap WeakSet SIMD ArrayBuffer DataView JSON Promise Generator GeneratorFunction Reflect Proxy Intl'.split( ' ' ); -let blacklisted = {}; +let blacklisted = blank(); reservedWords.concat( builtins ).forEach( word => blacklisted[ word ] = true ); diff --git a/src/utils/object.js b/src/utils/object.js index da70eef..a11ea25 100644 --- a/src/utils/object.js +++ b/src/utils/object.js @@ -1,11 +1,5 @@ export const keys = Object.keys; -export const hasOwnProp = Object.prototype.hasOwnProperty; - -export function has ( obj, prop ) { - return hasOwnProp.call( obj, prop ); -} - export function blank () { return Object.create( null ); } diff --git a/test/function/object-prototype-properties/_config.js b/test/function/object-prototype-properties/_config.js new file mode 100644 index 0000000..258727c --- /dev/null +++ b/test/function/object-prototype-properties/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'handles names conflicting with Object.prototype properties' +}; diff --git a/test/function/object-prototype-properties/foo.js b/test/function/object-prototype-properties/foo.js new file mode 100644 index 0000000..932f70a --- /dev/null +++ b/test/function/object-prototype-properties/foo.js @@ -0,0 +1,7 @@ +function valueOf() { + return 42; +} + +export default function() { + return valueOf(); +}; diff --git a/test/function/object-prototype-properties/main.js b/test/function/object-prototype-properties/main.js new file mode 100644 index 0000000..6f3d893 --- /dev/null +++ b/test/function/object-prototype-properties/main.js @@ -0,0 +1,3 @@ +import foo from './foo'; + +assert.equal( foo(), 42 );