diff --git a/src/Declaration.js b/src/Declaration.js index 515b17d3a37..f7ffb61b2fe 100644 --- a/src/Declaration.js +++ b/src/Declaration.js @@ -1,5 +1,5 @@ import { blank, forOwn, keys } from './utils/object.js'; -import makeLegalIdentifier, { reservedWords } from './utils/makeLegalIdentifier.js'; +import { makeLegal, reservedWords } from './utils/identifier-helpers.js'; import { UNKNOWN } from './ast/values.js'; export default class Declaration { @@ -25,7 +25,7 @@ export default class Declaration { reference.declaration = this; if ( reference.name !== this.name ) { - this.name = makeLegalIdentifier( reference.name ); // TODO handle differences of opinion + this.name = makeLegal( reference.name ); // TODO handle differences of opinion } if ( reference.isReassignment ) this.isReassigned = true; diff --git a/src/ExternalModule.js b/src/ExternalModule.js index 469d708be51..5538bb25286 100644 --- a/src/ExternalModule.js +++ b/src/ExternalModule.js @@ -1,5 +1,5 @@ import { blank } from './utils/object.js'; -import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; +import { makeLegal } from './utils/identifier-helpers.js'; import { ExternalDeclaration } from './Declaration.js'; export default class ExternalModule { @@ -7,7 +7,7 @@ export default class ExternalModule { this.id = id; this.path = relativePath; - this.name = makeLegalIdentifier( relativePath ); + this.name = makeLegal( relativePath ); this.nameSuggestions = blank(); this.mostCommonSuggestion = 0; diff --git a/src/Module.js b/src/Module.js index a1d5ec9f8f6..b533c900667 100644 --- a/src/Module.js +++ b/src/Module.js @@ -4,7 +4,7 @@ import { locate } from 'locate-character'; import { timeStart, timeEnd } from './utils/flushTime.js'; import { assign, blank, keys } from './utils/object.js'; import { basename, extname } from './utils/path.js'; -import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; +import { makeLegal } from './utils/identifier-helpers.js'; import getCodeFrame from './utils/getCodeFrame.js'; import { SOURCEMAPPING_URL_RE } from './utils/sourceMappingURL.js'; import error from './utils/error.js'; @@ -251,7 +251,7 @@ export default class Module { const base = basename( this.id ); const ext = extname( this.id ); - return makeLegalIdentifier( ext ? base.slice( 0, -ext.length ) : base ); + return makeLegal( ext ? base.slice( 0, -ext.length ) : base ); } bindImportSpecifiers () { diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 239cdb5b7e2..f39d00f5855 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -7,6 +7,7 @@ import getGlobalNameMaker from './shared/getGlobalNameMaker.js'; import { property, keypath } from './shared/sanitize.js'; import warnOnBuiltins from './shared/warnOnBuiltins.js'; import trimEmptyImports from './shared/trimEmptyImports.js'; +import { isLegal } from '../utils/identifier-helpers.js'; function setupNamespace ( keypath ) { const parts = keypath.split( '.' ); @@ -26,6 +27,14 @@ export default function iife ( bundle, magicString, { exportMode, indentString, const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle, 'null' ); const name = options.moduleName; + + if ( name && !isLegal(name) ) { + error({ + code: 'ILLEGAL_IDENTIFIER_AS_NAME', + message: `Given moduleName - ${ name } - is not legal JS identifier.` + }) + } + const isNamespaced = name && ~name.indexOf( '.' ); warnOnBuiltins( bundle ); @@ -41,9 +50,12 @@ export default function iife ( bundle, magicString, { exportMode, indentString, }); } - if ( exportMode === 'named' ) { + if ( isNamespaced ) { dependencies.unshift( `(${thisProp(name)} = ${thisProp(name)} || {})` ); args.unshift( 'exports' ); + } else if ( exportMode === 'named' ) { + dependencies.unshift( '{}' ); + args.unshift( 'exports' ); } const useStrict = options.useStrict !== false ? `${indentString}'use strict';\n\n` : ``; @@ -51,7 +63,7 @@ export default function iife ( bundle, magicString, { exportMode, indentString, let wrapperIntro = `(function (${args}) {\n${useStrict}`; const wrapperOutro = `\n\n}(${dependencies}));`; - if ( exportMode === 'default' ) { + if ( exportMode !== 'none' ) { wrapperIntro = ( isNamespaced ? thisProp(name) : `${bundle.varOrConst} ${name}` ) + ` = ${wrapperIntro}`; } diff --git a/src/utils/makeLegalIdentifier.js b/src/utils/identifier-helpers.js similarity index 72% rename from src/utils/makeLegalIdentifier.js rename to src/utils/identifier-helpers.js index 846c42e158f..654f0172047 100644 --- a/src/utils/makeLegalIdentifier.js +++ b/src/utils/identifier-helpers.js @@ -6,13 +6,26 @@ const builtins = 'Infinity NaN undefined null true false eval uneval isFinite is const blacklisted = blank(); reservedWords.concat( builtins ).forEach( word => blacklisted[ word ] = true ); +const illegalCharacters = /[^$_a-zA-Z0-9]/g; -export default function makeLegalIdentifier ( str ) { +const startsWithDigit = str => /\d/.test( str[0] ); + +export function isLegal ( str ) { + if ( startsWithDigit(str) || blacklisted[ str ] ) { + return false; + } + if ( illegalCharacters.test(str) ) { + return false; + } + return true; +} + +export function makeLegal ( str ) { str = str .replace( /-(\w)/g, ( _, letter ) => letter.toUpperCase() ) - .replace( /[^$_a-zA-Z0-9]/g, '_' ); + .replace( illegalCharacters, '_' ); - if ( /\d/.test( str[0] ) || blacklisted[ str ] ) str = `_${str}`; + if ( startsWithDigit(str) || blacklisted[ str ] ) str = `_${str}`; return str; }