Skip to content

Commit

Permalink
Access global.* using array notation
Browse files Browse the repository at this point in the history
This allows arbitrary `moduleName` (including names with dashes like `ui-router` or scoped npm packages like `@angular/core`) to be added to the global object.

Closes #582
Closes #584
  • Loading branch information
christopherthielen committed Dec 14, 2016
1 parent fcf3928 commit f43fd99
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 11 deletions.
15 changes: 11 additions & 4 deletions src/finalisers/iife.js
Expand Up @@ -3,16 +3,23 @@ import { getName } from '../utils/map-helpers.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
import propertyStringFor from './shared/propertyStringFor';

// thisProp('foo.bar.baz') === "this['foo']['bar']['baz']"
const thisProp = propertyStringFor('this');

// propString('foo.bar') === "['foo']['bar']"
const propString = propertyStringFor('');

function setupNamespace ( keypath ) {
const parts = keypath.split( '.' ); // TODO support e.g. `foo['something-hyphenated']`?
const parts = keypath.split( '.' );

parts.pop();

let acc = 'this';

return parts
.map( part => ( acc += `.${part}`, `${acc} = ${acc} || {};` ) )
.map( part => ( acc += propString(part), `${acc} = ${acc} || {};` ) )
.join( '\n' ) + '\n';
}

Expand All @@ -31,7 +38,7 @@ export default function iife ( bundle, magicString, { exportMode, indentString,
}

if ( exportMode === 'named' ) {
dependencies.unshift( `(this.${name} = this.${name} || {})` );
dependencies.unshift( `(${thisProp(name)} = ${thisProp(name)} || {})` );
args.unshift( 'exports' );
}

Expand All @@ -41,7 +48,7 @@ export default function iife ( bundle, magicString, { exportMode, indentString,
const wrapperOutro = `\n\n}(${dependencies}));`;

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

if ( isNamespaced ) {
Expand Down
17 changes: 17 additions & 0 deletions src/finalisers/shared/propertyStringFor.js
@@ -0,0 +1,17 @@
// Generate strings which dereference dotted properties, but use array notation

const shouldUseDot = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/;
const dereferenceString = prop =>
prop.match(shouldUseDot) ? `.${prop}` : `['${prop}']`;

/**
* returns a function which generates property dereference strings for the given name
*
* const getGlobalProp = propertyStringFor('global');
* getGlobalProp('foo.bar.baz') => `global['foo']['bar']['baz']`
*/
const propertyStringFor = objName => propName =>
objName + propName.split('.').map(dereferenceString).join('');


export default propertyStringFor;
21 changes: 14 additions & 7 deletions src/finalisers/umd.js
Expand Up @@ -4,15 +4,22 @@ import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
import esModuleExport from './shared/esModuleExport.js';
import propertyStringFor from './shared/propertyStringFor.js';

// globalProp('foo.bar') === "global['foo']['bar']"
const globalProp = propertyStringFor('global');

// propString('foo.bar') === "['foo']['bar']"
const propString = propertyStringFor('');

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

let acc = 'global';
return parts
.map( part => ( acc += `.${part}`, `${acc} = ${acc} || {}` ) )
.concat( `global.${name}` )
.map( part => ( acc += propString(part), `${acc} = ${acc} || {}` ) )
.concat( globalProp(name) )
.join( ', ' );
}

Expand All @@ -27,14 +34,14 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i

const amdDeps = bundle.externalModules.map( quotePath );
const cjsDeps = bundle.externalModules.map( req );
const globalDeps = bundle.externalModules.map( module => `global.${globalNameMaker( module )}` );
const globalDeps = bundle.externalModules.map( module => globalProp(globalNameMaker( module )) );

const args = bundle.externalModules.map( getName );

if ( exportMode === 'named' ) {
amdDeps.unshift( `'exports'` );
cjsDeps.unshift( `exports` );
globalDeps.unshift( `(${setupNamespace(options.moduleName)} = global.${options.moduleName} || {})` );
globalDeps.unshift( `(${setupNamespace(options.moduleName)} = ${globalProp(options.moduleName)} || {})` );

args.unshift( 'exports' );
}
Expand All @@ -50,10 +57,10 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i

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

const wrapperIntro =
Expand Down
6 changes: 6 additions & 0 deletions test/form/module-name-scoped-package/_config.js
@@ -0,0 +1,6 @@
module.exports = {
description: 'allows module name with dashes to be added to the global object',
options: {
moduleName: '@scoped/npm-package'
}
};
9 changes: 9 additions & 0 deletions test/form/module-name-scoped-package/_expected/amd.js
@@ -0,0 +1,9 @@
define(['exports'], function (exports) { 'use strict';

let foo = 'foo';

exports.foo = foo;

Object.defineProperty(exports, '__esModule', { value: true });

});
7 changes: 7 additions & 0 deletions test/form/module-name-scoped-package/_expected/cjs.js
@@ -0,0 +1,7 @@
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

let foo = 'foo';

exports.foo = foo;
3 changes: 3 additions & 0 deletions test/form/module-name-scoped-package/_expected/es.js
@@ -0,0 +1,3 @@
let foo = 'foo';

export { foo };
8 changes: 8 additions & 0 deletions test/form/module-name-scoped-package/_expected/iife.js
@@ -0,0 +1,8 @@
(function (exports) {
'use strict';

let foo = 'foo';

exports.foo = foo;

}((this['@scoped/npm-package'] = this['@scoped/npm-package'] || {})));
13 changes: 13 additions & 0 deletions test/form/module-name-scoped-package/_expected/umd.js
@@ -0,0 +1,13 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global['@scoped/npm-package'] = global['@scoped/npm-package'] || {})));
}(this, (function (exports) { 'use strict';

let foo = 'foo';

exports.foo = foo;

Object.defineProperty(exports, '__esModule', { value: true });

})));
1 change: 1 addition & 0 deletions test/form/module-name-scoped-package/main.js
@@ -0,0 +1 @@
export let foo = 'foo';
6 changes: 6 additions & 0 deletions test/form/module-name-with-dashes/_config.js
@@ -0,0 +1,6 @@
module.exports = {
description: 'allows module name with dashes to be added to the global object',
options: {
moduleName: 'module-name-with-dashes'
}
};
9 changes: 9 additions & 0 deletions test/form/module-name-with-dashes/_expected/amd.js
@@ -0,0 +1,9 @@
define(['exports'], function (exports) { 'use strict';

let foo = 'foo';

exports.foo = foo;

Object.defineProperty(exports, '__esModule', { value: true });

});
7 changes: 7 additions & 0 deletions test/form/module-name-with-dashes/_expected/cjs.js
@@ -0,0 +1,7 @@
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

let foo = 'foo';

exports.foo = foo;
3 changes: 3 additions & 0 deletions test/form/module-name-with-dashes/_expected/es.js
@@ -0,0 +1,3 @@
let foo = 'foo';

export { foo };
8 changes: 8 additions & 0 deletions test/form/module-name-with-dashes/_expected/iife.js
@@ -0,0 +1,8 @@
(function (exports) {
'use strict';

let foo = 'foo';

exports.foo = foo;

}((this['module-name-with-dashes'] = this['module-name-with-dashes'] || {})));
13 changes: 13 additions & 0 deletions test/form/module-name-with-dashes/_expected/umd.js
@@ -0,0 +1,13 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global['module-name-with-dashes'] = global['module-name-with-dashes'] || {})));
}(this, (function (exports) { 'use strict';

let foo = 'foo';

exports.foo = foo;

Object.defineProperty(exports, '__esModule', { value: true });

})));
1 change: 1 addition & 0 deletions test/form/module-name-with-dashes/main.js
@@ -0,0 +1 @@
export let foo = 'foo';

0 comments on commit f43fd99

Please sign in to comment.