Skip to content

Commit

Permalink
Fix UMD wrapper issues and refine wrappers (#2600)
Browse files Browse the repository at this point in the history
* Fix missing parentheses when creating a namespace via a comma expression,
only check for self in the UMD-IIFE case

* Add comprehensive UMD wrapper tests and fix more issues

* Shorten variable names in UMD; remove unnecessary UMD function tests

* Test and refine iife wrapper
  • Loading branch information
lukastaegert committed Dec 19, 2018
1 parent 7708970 commit 8f71071
Show file tree
Hide file tree
Showing 133 changed files with 723 additions and 359 deletions.
32 changes: 17 additions & 15 deletions src/finalisers/iife.ts
Expand Up @@ -31,9 +31,9 @@ export default function iife(

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

if (name && possibleVariableAssignment && !isLegal(name)) {
if (name && useVariableAssignment && !isLegal(name)) {
error({
code: 'ILLEGAL_IDENTIFIER_AS_NAME',
message: `Given name (${name}) is not legal JS identifier. If you need this you can try --extend option`
Expand All @@ -53,29 +53,31 @@ export default function iife(
});
}

if (extend) {
deps.unshift(`${thisProp(name)}${_}=${_}${thisProp(name)}${_}||${_}{}`);
args.unshift('exports');
} else if (namedExportsMode && hasExports) {
deps.unshift('{}');
args.unshift('exports');
if (namedExportsMode && hasExports) {
if (extend) {
deps.unshift(`${thisProp(name)}${_}=${_}${thisProp(name)}${_}||${_}{}`);
args.unshift('exports');
} else {
deps.unshift('{}');
args.unshift('exports');
}
}

const useStrict = options.strict !== false ? `${t}'use strict';${n}${n}` : ``;

let wrapperIntro = `(function${_}(${args})${_}{${n}${useStrict}`;
let wrapperIntro = `(function${_}(${args.join(`,${_}`)})${_}{${n}${useStrict}`;

if (hasExports && !extend) {
if (hasExports && (!extend || !namedExportsMode)) {
wrapperIntro =
(isNamespaced ? thisProp(name) : `${graph.varOrConst} ${name}`) + `${_}=${_}${wrapperIntro}`;
(useVariableAssignment ? `${graph.varOrConst} ${name}` : thisProp(name)) +
`${_}=${_}${wrapperIntro}`;
}

if (isNamespaced) {
wrapperIntro =
setupNamespace(name, 'this', false, options.globals, options.compact) + wrapperIntro;
if (isNamespaced && hasExports) {
wrapperIntro = setupNamespace(name, 'this', options.globals, options.compact) + wrapperIntro;
}

let wrapperOutro = `${n}${n}}(${deps}));`;
let wrapperOutro = `${n}${n}}(${deps.join(`,${_}`)}));`;

if (!extend && namedExportsMode && hasExports) {
wrapperOutro = `${n}${n}${t}return exports;${wrapperOutro}`;
Expand Down
47 changes: 31 additions & 16 deletions src/finalisers/shared/setupNamespace.ts
Expand Up @@ -4,7 +4,6 @@ import { property } from './sanitize';
export default function setupNamespace(
name: string,
root: string,
forAssignment: boolean,
globals: GlobalsOption,
compact: boolean
) {
Expand All @@ -14,24 +13,40 @@ export default function setupNamespace(
}

const _ = compact ? '' : ' ';
parts.pop();

let acc = root;
return (
parts
.map(
part => ((acc += property(part)), `${acc}${_}=${_}${acc}${_}||${_}{}${compact ? '' : ';'}`)
)
.join(compact ? ',' : '\n') + (compact && parts.length ? ';' : '\n')
);
}

export function assignToDeepVariable(
deepName: string,
root: string,
globals: GlobalsOption,
compact: boolean,
assignment: string
): string {
const _ = compact ? '' : ' ';
const parts = deepName.split('.');
if (globals) {
parts[0] = (typeof globals === 'function' ? globals(parts[0]) : globals[parts[0]]) || parts[0];
}
const last = parts.pop();

let acc = root;
if (forAssignment) {
return parts
.map(part => ((acc += property(part)), `${acc}${_}=${_}${acc}${_}||${_}{}`))
.concat(`${acc}${property(last)}`)
.join(`,${_}`);
} else {
return (
parts
.map(
part => (
(acc += property(part)), `${acc}${_}=${_}${acc}${_}||${_}{}${compact ? '' : ';'}`
)
)
.join(compact ? ',' : '\n') + (compact && parts.length ? ';' : '\n')
);
let deepAssignment = parts
.map(part => ((acc += property(part)), `${acc}${_}=${_}${acc}${_}||${_}{}`))
.concat(`${acc}${property(last)}`)
.join(`,${_}`)
.concat(`${_}=${_}${assignment}`);
if (parts.length > 0) {
deepAssignment = `(${deepAssignment})`;
}
return deepAssignment;
}
92 changes: 54 additions & 38 deletions src/finalisers/umd.ts
Expand Up @@ -6,20 +6,20 @@ import { compactEsModuleExport, esModuleExport } from './shared/esModuleExport';
import getExportBlock from './shared/getExportBlock';
import getInteropBlock from './shared/getInteropBlock';
import { keypath, property } from './shared/sanitize';
import setupNamespace from './shared/setupNamespace';
import { assignToDeepVariable } from './shared/setupNamespace';
import trimEmptyImports from './shared/trimEmptyImports';
import warnOnBuiltins from './shared/warnOnBuiltins';

function globalProp(name: string) {
function globalProp(name: string, globalVar: string) {
if (!name) return 'null';
return `global${keypath(name)}`;
return `${globalVar}${keypath(name)}`;
}

function safeAccess(name: string, compact: boolean) {
function safeAccess(name: string, globalVar: string, _: string) {
const parts = name.split('.');

let acc = 'global';
return parts.map(part => ((acc += property(part)), acc)).join(compact ? '&&' : ` && `);
let acc = globalVar;
return parts.map(part => ((acc += property(part)), acc)).join(`${_}&&${_}`);
}

export default function umd(
Expand All @@ -38,6 +38,8 @@ export default function umd(
) {
const _ = options.compact ? '' : ' ';
const n = options.compact ? '' : '\n';
const factoryVar = options.compact ? 'f' : 'factory';
const globalVar = options.compact ? 'g' : 'global';

if (hasExports && !options.name) {
error({
Expand All @@ -52,16 +54,20 @@ export default function umd(
const cjsDeps = dependencies.map(m => `require('${m.id}')`);

const trimmedImports = trimEmptyImports(dependencies);
const globalDeps = trimmedImports.map(module => globalProp(module.globalName));
const globalDeps = trimmedImports.map(module => globalProp(module.globalName, globalVar));
const factoryArgs = trimmedImports.map(m => m.name);

if (namedExportsMode && (hasExports || options.noConflict === true)) {
amdDeps.unshift(`'exports'`);
cjsDeps.unshift(`exports`);
globalDeps.unshift(
`${setupNamespace(options.name, 'global', true, options.globals, options.compact)}${_}=${_}${
options.extend ? `${globalProp(options.name)}${_}||${_}` : ''
}{}`
assignToDeepVariable(
options.name,
globalVar,
options.globals,
options.compact,
`${options.extend ? `${globalProp(options.name, globalVar)}${_}||${_}` : ''}{}`
)
);

factoryArgs.unshift('exports');
Expand All @@ -74,58 +80,68 @@ export default function umd(
(amdDeps.length ? `[${amdDeps.join(`,${_}`)}],${_}` : ``);

const define = amdOptions.define || 'define';

const cjsExport = !namedExportsMode && hasExports ? `module.exports${_}=${_}` : ``;
const defaultExport =
!namedExportsMode && hasExports
? `${setupNamespace(options.name, 'global', true, options.globals, options.compact)}${_}=${_}`
: '';

const useStrict = options.strict !== false ? `${_}'use strict';${n}` : ``;

let globalExport;
let iifeExport;

if (options.noConflict === true) {
const noConflictExportsVar = options.compact ? 'e' : 'exports';
let factory;

if (!namedExportsMode && hasExports) {
factory = `var exports${_}=${_}factory(${globalDeps});`;
factory = `var ${noConflictExportsVar}${_}=${_}${assignToDeepVariable(
options.name,
globalVar,
options.globals,
options.compact,
`${factoryVar}(${globalDeps.join(`,${_}`)})`
)};`;
} else if (namedExportsMode) {
const module = globalDeps.shift();
factory =
`var exports${_}=${_}${module};${n}` +
`${t}${t}factory(${['exports'].concat(globalDeps)});`;
`var ${noConflictExportsVar}${_}=${_}${module};${n}` +
`${t}${t}${factoryVar}(${[noConflictExportsVar].concat(globalDeps).join(`,${_}`)});`;
}
globalExport =
`(function()${_}{${n}` +
`${t}${t}var current${_}=${_}${safeAccess(options.name, options.compact)};${n}` +
iifeExport =
`(function${_}()${_}{${n}` +
`${t}${t}var current${_}=${_}${safeAccess(options.name, globalVar, _)};${n}` +
`${t}${t}${factory}${n}` +
`${t}${t}${globalProp(options.name)}${_}=${_}exports;${n}` +
`${t}${t}exports.noConflict${_}=${_}function()${_}{${_}` +
`${globalProp(options.name)}${_}=${_}current;${_}return exports${
`${t}${t}${noConflictExportsVar}.noConflict${_}=${_}function${_}()${_}{${_}` +
`${globalProp(options.name, globalVar)}${_}=${_}current;${_}return ${noConflictExportsVar}${
options.compact ? '' : '; '
}};${n}` +
`${t}})()`;
`${t}}())`;
} else {
globalExport = `${defaultExport}factory(${globalDeps})`;
iifeExport = `${factoryVar}(${globalDeps.join(`,${_}`)})`;
if (!namedExportsMode && hasExports) {
iifeExport = assignToDeepVariable(
options.name,
globalVar,
options.globals,
options.compact,
iifeExport
);
}
}

const iifeNeedsGlobal = hasExports || (options.noConflict === true && namedExportsMode);
const globalParam = iifeNeedsGlobal ? `global,${_}` : '';
const globalArg = iifeNeedsGlobal
? `typeof self${_}!==${_}'undefined'${_}?${_}self${_}:${_}this,${_}`
: '';
const iifeNeedsGlobal =
hasExports || (options.noConflict === true && namedExportsMode) || globalDeps.length > 0;
const globalParam = iifeNeedsGlobal ? `${globalVar},${_}` : '';
const globalArg = iifeNeedsGlobal ? `this,${_}` : '';
const iifeStart = iifeNeedsGlobal ? `(${globalVar}${_}=${_}${globalVar}${_}||${_}self,${_}` : '';
const iifeEnd = iifeNeedsGlobal ? ')' : '';
const cjsIntro = iifeNeedsGlobal
? `${t}typeof exports${_}===${_}'object'${_}&&${_}typeof module${_}!==${_}'undefined'${_}?` +
`${_}${cjsExport}factory(${cjsDeps.join(`,${_}`)})${_}:${n}`
`${_}${cjsExport}${factoryVar}(${cjsDeps.join(`,${_}`)})${_}:${n}`
: '';

const wrapperIntro =
`(function${_}(${globalParam}factory)${_}{${n}` +
`(function${_}(${globalParam}${factoryVar})${_}{${n}` +
cjsIntro +
`${t}typeof ${define}${_}===${_}'function'${_}&&${_}${define}.amd${_}?${_}${define}(${amdParams}factory)${_}:${n}` +
`${t}${globalExport};${n}` +
`}(${globalArg}function${_}(${factoryArgs})${_}{${useStrict}${n}`;
`${t}typeof ${define}${_}===${_}'function'${_}&&${_}${define}.amd${_}?${_}${define}(${amdParams}${factoryVar})${_}:${n}` +
`${t}${iifeStart}${iifeExport}${iifeEnd};${n}` +
`}(${globalArg}function${_}(${factoryArgs.join(', ')})${_}{${useStrict}${n}`;

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

Expand Down
4 changes: 2 additions & 2 deletions test/cli/samples/module-name/_expected.js
@@ -1,8 +1,8 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.myBundle = factory();
}(typeof self !== 'undefined' ? self : this, function () { 'use strict';
(global = global || self, global.myBundle = factory());
}(this, function () { 'use strict';

var main = 42;

Expand Down
11 changes: 5 additions & 6 deletions test/cli/samples/no-conflict/_expected.js
@@ -1,13 +1,12 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(function() {
(global = global || self, (function () {
var current = global.conflictyName;
var exports = factory();
global.conflictyName = exports;
exports.noConflict = function() { global.conflictyName = current; return exports; };
})();
}(typeof self !== 'undefined' ? self : this, function () { 'use strict';
var exports = global.conflictyName = factory();
exports.noConflict = function () { global.conflictyName = current; return exports; };
}()));
}(this, function () { 'use strict';

var main = {};

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

var buffer = new ArrayBuffer( 8 );

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

exports.Foo = class Foo {};
exports.Foo = lol( exports.Foo );
Expand Down
4 changes: 2 additions & 2 deletions test/form/samples/assignment-to-exports/_expected/umd.js
@@ -1,8 +1,8 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
factory(global.bundle = {});
}(typeof self !== 'undefined' ? self : this, function (exports) { 'use strict';
(global = global || self, factory(global.bundle = {}));
}(this, function (exports) { 'use strict';

// Unassigned export
var foo1;
Expand Down
4 changes: 2 additions & 2 deletions test/form/samples/catch-parameter-shadowing/_expected/umd.js
@@ -1,8 +1,8 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
factory(global.bundle = {});
}(typeof self !== 'undefined' ? self : this, function (exports) { 'use strict';
(global = global || self, factory(global.bundle = {}));
}(this, function (exports) { 'use strict';

const e = 2.7182818284;

Expand Down
2 changes: 1 addition & 1 deletion test/form/samples/compact/_expected/umd.js
@@ -1,4 +1,4 @@
(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory(require('external')):typeof define==='function'&&define.amd?define(['external'],factory):global.foo=factory(global.x);}(typeof self!=='undefined'?self:this,function(x){'use strict';x=x&&x.hasOwnProperty('default')?x['default']:x;var self = {get default(){return foo$$1}};if(typeof Symbol!=='undefined'&&Symbol.toStringTag)Object.defineProperty(self,Symbol.toStringTag,{value:'Module'});else Object.defineProperty(self,'toString',{value:function(){return'[object Module]';}});/*#__PURE__*/Object.freeze(self);console.log(self);
(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?module.exports=f(require('external')):typeof define==='function'&&define.amd?define(['external'],f):(g=g||self,g.foo=f(g.x));}(this,function(x){'use strict';x=x&&x.hasOwnProperty('default')?x['default']:x;var self = {get default(){return foo$$1}};if(typeof Symbol!=='undefined'&&Symbol.toStringTag)Object.defineProperty(self,Symbol.toStringTag,{value:'Module'});else Object.defineProperty(self,'toString',{value:function(){return'[object Module]';}});/*#__PURE__*/Object.freeze(self);console.log(self);
function foo$$1 () {
console.log( x );
}
Expand Down
4 changes: 2 additions & 2 deletions test/form/samples/computed-properties/_expected/umd.js
@@ -1,8 +1,8 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
factory(global.computedProperties = {});
}(typeof self !== 'undefined' ? self : this, function (exports) { 'use strict';
(global = global || self, factory(global.computedProperties = {}));
}(this, function (exports) { 'use strict';

var foo = 'foo';
var bar = 'bar';
Expand Down
4 changes: 2 additions & 2 deletions test/form/samples/conflicting-imports/_expected/iife.js
@@ -1,8 +1,8 @@
(function (foo,bar) {
(function (foo, bar) {
'use strict';

console.log( bar.a );

console.log( foo.a );

}(foo,bar));
}(foo, bar));
7 changes: 4 additions & 3 deletions test/form/samples/conflicting-imports/_expected/umd.js
@@ -1,7 +1,8 @@
(function (factory) {
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('foo'), require('bar')) :
typeof define === 'function' && define.amd ? define(['foo', 'bar'], factory) :
factory(global.foo,global.bar);
}(function (foo,bar) { 'use strict';
(global = global || self, factory(global.foo, global.bar));
}(this, function (foo, bar) { 'use strict';

console.log( bar.a );

Expand Down

0 comments on commit 8f71071

Please sign in to comment.