Skip to content

Commit

Permalink
Merge pull request #1472 from Andarist/iife/extends
Browse files Browse the repository at this point in the history
[new feature] extend option
  • Loading branch information
Rich-Harris committed Jul 10, 2017
2 parents f9408a0 + 5306bab commit 875cd37
Show file tree
Hide file tree
Showing 74 changed files with 462 additions and 75 deletions.
4 changes: 2 additions & 2 deletions 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/identifierHelpers.js';
import { UNKNOWN } from './ast/values.js';

export default class Declaration {
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/ExternalModule.js
@@ -1,13 +1,13 @@
import { blank } from './utils/object.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import { makeLegal } from './utils/identifierHelpers.js';
import { ExternalDeclaration } from './Declaration.js';

export default class ExternalModule {
constructor ( id, relativePath ) {
this.id = id;
this.path = relativePath;

this.name = makeLegalIdentifier( relativePath );
this.name = makeLegal( relativePath );

this.nameSuggestions = blank();
this.mostCommonSuggestion = 0;
Expand Down
4 changes: 2 additions & 2 deletions src/Module.js
Expand Up @@ -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/identifierHelpers.js';
import getCodeFrame from './utils/getCodeFrame.js';
import { SOURCEMAPPING_URL_RE } from './utils/sourceMappingURL.js';
import error from './utils/error.js';
Expand Down Expand Up @@ -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 () {
Expand Down
2 changes: 1 addition & 1 deletion src/finalisers/amd.js
@@ -1,4 +1,4 @@
import { getName, quotePath } from '../utils/map-helpers.js';
import { getName, quotePath } from '../utils/mapHelpers.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import esModuleExport from './shared/esModuleExport.js';
Expand Down
29 changes: 23 additions & 6 deletions src/finalisers/iife.js
@@ -1,12 +1,13 @@
import { blank } from '../utils/object.js';
import { getName } from '../utils/map-helpers.js';
import { getName } from '../utils/mapHelpers.js';
import error from '../utils/error.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
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/identifierHelpers.js';

function setupNamespace ( keypath ) {
const parts = keypath.split( '.' );
Expand All @@ -25,8 +26,16 @@ const thisProp = name => `this${keypath( name )}`;
export default function iife ( bundle, magicString, { exportMode, indentString, intro, outro }, options ) {
const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle, 'null' );

const name = options.moduleName;
const isNamespaced = name && ~name.indexOf( '.' );
const { extend, moduleName: name } = options;
const isNamespaced = name && name.indexOf( '.' ) !== -1;
const possibleVariableAssignment = !extend && !isNamespaced;

if ( name && possibleVariableAssignment && !isLegal(name) ) {
error({
code: 'ILLEGAL_IDENTIFIER_AS_NAME',
message: `Given moduleName (${name}) is not legal JS identifier. If you need this you can try --extend option`
});
}

warnOnBuiltins( bundle );

Expand All @@ -41,24 +50,32 @@ export default function iife ( bundle, magicString, { exportMode, indentString,
});
}

if ( exportMode === 'named' ) {
if ( extend ) {
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` : ``;

let wrapperIntro = `(function (${args}) {\n${useStrict}`;
const wrapperOutro = `\n\n}(${dependencies}));`;

if ( exportMode === 'default' ) {
if ( exportMode !== 'none' && !extend) {
wrapperIntro = ( isNamespaced ? thisProp(name) : `${bundle.varOrConst} ${name}` ) + ` = ${wrapperIntro}`;
}

if ( isNamespaced ) {
wrapperIntro = setupNamespace( name ) + wrapperIntro;
}

let wrapperOutro = `\n\n}(${dependencies}));`;

if (possibleVariableAssignment && exportMode === 'named') {
wrapperOutro = `\n\n${indentString}return exports;${wrapperOutro}`;
}

// var foo__default = 'default' in foo ? foo['default'] : foo;
const interopBlock = getInteropBlock( bundle, options );
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' );
Expand Down
37 changes: 30 additions & 7 deletions src/finalisers/umd.js
@@ -1,5 +1,5 @@
import { blank } from '../utils/object.js';
import { getName, quotePath, req } from '../utils/map-helpers.js';
import { getName, quotePath, req } from '../utils/mapHelpers.js';
import error from '../utils/error.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
Expand All @@ -25,6 +25,15 @@ function setupNamespace ( name ) {
.join( ', ' );
}

function safeAccess ( name ) {
const parts = name.split( '.' );

let acc = 'global';
return parts
.map( part => ( acc += property( part ), acc ) )
.join( ` && ` );
}

const wrapperOutro = '\n\n})));';

export default function umd ( bundle, magicString, { exportMode, indentString, intro, outro }, options ) {
Expand All @@ -49,7 +58,7 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i
if ( exportMode === 'named' ) {
amdDeps.unshift( `'exports'` );
cjsDeps.unshift( `exports` );
globalDeps.unshift( `(${setupNamespace(options.moduleName)} = ${globalProp(options.moduleName)} || {})` );
globalDeps.unshift( `(${setupNamespace(options.moduleName)} = ${options.extend ? `${globalProp(options.moduleName)} || ` : '' }{})` );

args.unshift( 'exports' );
}
Expand All @@ -67,13 +76,27 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i

const useStrict = options.useStrict !== false ? ` 'use strict';` : ``;

const globalExport = options.noConflict === true ?
`(function() {
var current = ${globalProp(options.moduleName)};
var exports = factory(${globalDeps});
let globalExport;

if (options.noConflict === true) {
let factory;

if ( exportMode === 'default' ) {
factory = `var exports = factory(${globalDeps});`;
} else if ( exportMode === 'named' ) {
const module = globalDeps.shift();
factory = `var exports = ${module};
factory(${['exports'].concat(globalDeps)});`;
}
globalExport = `(function() {
var current = ${safeAccess(options.moduleName)};
${factory}
${globalProp(options.moduleName)} = exports;
exports.noConflict = function() { ${globalProp(options.moduleName)} = current; return exports; };
})()` : `(${defaultExport}factory(${globalDeps}))`;
})()`;
} else {
globalExport = `(${defaultExport}factory(${globalDeps}))`;
}

const wrapperIntro =
`(function (global, factory) {
Expand Down
1 change: 1 addition & 0 deletions src/rollup.js
Expand Up @@ -19,6 +19,7 @@ const ALLOWED_KEYS = [
'dest',
'entry',
'exports',
'extend',
'external',
'footer',
'format',
Expand Down
Expand Up @@ -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;
}
File renamed without changes.
@@ -1,7 +1,9 @@
(function (exports) {
var myModule = (function (exports) {
'use strict';

exports.Foo = class Foo {};
exports.Foo = lol( exports.Foo );

}((this.myModule = this.myModule || {})));
return exports;

}({}));
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.myModule = global.myModule || {})));
(factory((global.myModule = {})));
}(this, (function (exports) { 'use strict';

exports.Foo = class Foo {};
Expand Down
6 changes: 4 additions & 2 deletions test/form/assignment-to-exports/_expected/iife.js
@@ -1,6 +1,8 @@
(function (exports) {
var bundle = (function (exports) {
'use strict';

exports.foo = 1;

}((this.bundle = this.bundle || {})));
return exports;

}({}));
2 changes: 1 addition & 1 deletion test/form/assignment-to-exports/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.bundle = global.bundle || {})));
(factory((global.bundle = {})));
}(this, (function (exports) { 'use strict';

exports.foo = 1;
Expand Down
6 changes: 4 additions & 2 deletions test/form/computed-properties/_expected/iife.js
@@ -1,4 +1,4 @@
(function (exports) {
var computedProperties = (function (exports) {
'use strict';

var foo = 'foo';
Expand All @@ -17,4 +17,6 @@
exports.x = x;
exports.X = X;

}((this.computedProperties = this.computedProperties || {})));
return exports;

}({}));
2 changes: 1 addition & 1 deletion test/form/computed-properties/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.computedProperties = global.computedProperties || {})));
(factory((global.computedProperties = {})));
}(this, (function (exports) { 'use strict';

var foo = 'foo';
Expand Down
6 changes: 4 additions & 2 deletions test/form/dedupes-external-imports/_expected/iife.js
@@ -1,4 +1,4 @@
(function (exports,external) {
var myBundle = (function (exports,external) {
'use strict';

class Foo extends external.Component {
Expand Down Expand Up @@ -30,4 +30,6 @@
exports.bar = bar;
exports.baz = baz;

}((this.myBundle = this.myBundle || {}),external));
return exports;

}({},external));
2 changes: 1 addition & 1 deletion test/form/dedupes-external-imports/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('external')) :
typeof define === 'function' && define.amd ? define(['exports', 'external'], factory) :
(factory((global.myBundle = global.myBundle || {}),global.external));
(factory((global.myBundle = {}),global.external));
}(this, (function (exports,external) { 'use strict';

class Foo extends external.Component {
Expand Down
6 changes: 4 additions & 2 deletions test/form/export-all-from-internal/_expected/iife.js
@@ -1,4 +1,4 @@
(function (exports) {
var exposedInternals = (function (exports) {
'use strict';

const a = 1;
Expand All @@ -7,4 +7,6 @@
exports.a = a;
exports.b = b;

}((this.exposedInternals = this.exposedInternals || {})));
return exports;

}({}));
2 changes: 1 addition & 1 deletion test/form/export-all-from-internal/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.exposedInternals = global.exposedInternals || {})));
(factory((global.exposedInternals = {})));
}(this, (function (exports) { 'use strict';

const a = 1;
Expand Down
6 changes: 4 additions & 2 deletions test/form/export-all-multiple/_expected/iife.js
@@ -1,4 +1,4 @@
(function (exports,foo,bar,baz) {
var myBundle = (function (exports,foo,bar,baz) {
'use strict';


Expand All @@ -7,4 +7,6 @@
Object.keys(bar).forEach(function (key) { exports[key] = bar[key]; });
Object.keys(baz).forEach(function (key) { exports[key] = baz[key]; });

}((this.myBundle = this.myBundle || {}),foo,bar,baz));
return exports;

}({},foo,bar,baz));
2 changes: 1 addition & 1 deletion test/form/export-all-multiple/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('foo'), require('bar'), require('baz')) :
typeof define === 'function' && define.amd ? define(['exports', 'foo', 'bar', 'baz'], factory) :
(factory((global.myBundle = global.myBundle || {}),global.foo,global.bar,global.baz));
(factory((global.myBundle = {}),global.foo,global.bar,global.baz));
}(this, (function (exports,foo,bar,baz) { 'use strict';

Object.keys(foo).forEach(function (key) { exports[key] = foo[key]; });
Expand Down
6 changes: 4 additions & 2 deletions test/form/export-default-import/_expected/iife.js
@@ -1,4 +1,4 @@
(function (exports,x) {
var myBundle = (function (exports,x) {
'use strict';

x = x && 'default' in x ? x['default'] : x;
Expand All @@ -7,4 +7,6 @@

exports.x = x;

}((this.myBundle = this.myBundle || {}),x));
return exports;

}({},x));
2 changes: 1 addition & 1 deletion test/form/export-default-import/_expected/umd.js
@@ -1,7 +1,7 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('x')) :
typeof define === 'function' && define.amd ? define(['exports', 'x'], factory) :
(factory((global.myBundle = global.myBundle || {}),global.x));
(factory((global.myBundle = {}),global.x));
}(this, (function (exports,x) { 'use strict';

x = x && 'default' in x ? x['default'] : x;
Expand Down

0 comments on commit 875cd37

Please sign in to comment.