Skip to content

Commit

Permalink
Merge pull request #1144 from christopherthielen/master
Browse files Browse the repository at this point in the history
Access global.* using array notation
  • Loading branch information
Rich-Harris committed Dec 18, 2016
2 parents 98abe24 + b1e4384 commit dfb8a3f
Show file tree
Hide file tree
Showing 24 changed files with 187 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.qux') === "this.foo['bar-baz'].qux"
const thisProp = propertyStringFor('this');

// propString('foo.bar-baz.qux') === ".foo['bar-baz'].qux"
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
18 changes: 18 additions & 0 deletions src/finalisers/shared/propertyStringFor.js
@@ -0,0 +1,18 @@
// Generate strings which dereference dotted properties, but use array notation `['prop-deref']`
// if the property name isn't trivial

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.qux') => `global.bar['bar-baz'].qux`
*/
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-baz') === "global.foo['bar-baz']"
const globalProp = propertyStringFor('global');

// propString('foo.bar-baz') === ".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-wat/_config.js
@@ -0,0 +1,6 @@
module.exports = {
description: 'properly dereferences properties on the global object regardless of nesting',
options: {
moduleName: 'foo.@scoped/npm-package.bar.why-would-you-do-this'
}
};
9 changes: 9 additions & 0 deletions test/form/module-name-wat/_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-wat/_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-wat/_expected/es.js
@@ -0,0 +1,3 @@
let foo = 'foo';

export { foo };
11 changes: 11 additions & 0 deletions test/form/module-name-wat/_expected/iife.js
@@ -0,0 +1,11 @@
this.foo = this.foo || {};
this.foo['@scoped/npm-package'] = this.foo['@scoped/npm-package'] || {};
this.foo['@scoped/npm-package'].bar = this.foo['@scoped/npm-package'].bar || {};
(function (exports) {
'use strict';

let foo = 'foo';

exports.foo = foo;

}((this.foo['@scoped/npm-package'].bar['why-would-you-do-this'] = this.foo['@scoped/npm-package'].bar['why-would-you-do-this'] || {})));
13 changes: 13 additions & 0 deletions test/form/module-name-wat/_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.foo = global.foo || {}, global.foo['@scoped/npm-package'] = global.foo['@scoped/npm-package'] || {}, global.foo['@scoped/npm-package'].bar = global.foo['@scoped/npm-package'].bar || {}, global.foo['@scoped/npm-package'].bar['why-would-you-do-this'] = global.foo['@scoped/npm-package'].bar['why-would-you-do-this'] || {})));
}(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-wat/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 dfb8a3f

Please sign in to comment.