fid-umd
Version:
Easily add UMD to your JavaScripts so they can be loaded with CommonJS, AMD (RequireJS), Rhino, node.js, module.js, in the browser and everywhere else.
1,128 lines (909 loc) • 26.2 kB
JavaScript
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.FidUmd=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* Configuration object for FidUmd
*/
"use strict";
/**
* Create a new configuration object
*
* @constructor
* @alias module:Config
* @property {string} name Module name
* @property {!Array.<~depends>} depends Dependencies
* @property {boolean} global When true, factory runs in global scope
* @property {boolean} jslint Flag enabling jslint comments
* @property {string} name Name of module being created
*/
function Config() {
if (!(this instanceof Config)) {
return new Config();
}
this.debug = false;
this.depends = [];
this.functionsNeeded = {};
this.global = false;
this.globalVariables = {}; // For jslint's special comment
this.jslint = false;
this.name = 'Unknown';
}
/**
* Config properties used when loading/saving fid-umd header markers.
* Keep these in non-alphabetical order and instead sort them by
* where they should appear in the resulting object.
*
* @readonly
* @const
*/
Config.prototype.configProperties = {
name: 'string',
debug: 'boolean',
jslint: 'boolean',
global: 'boolean',
depends: 'array'
};
/**
* Module loading systems
*
* @readonly
* @const
*/
Config.prototype.configModuleSystems = [
"amd",
"commonjs",
"modulejs",
"nodejs",
"root",
"yui"
];
/**
* Additional configuration for module loading systems
*/
Config.prototype.configModuleSystemsAdditional = [
"commonjsmod"
];
/**
* Grab a value from all of the depends
*
* @param {!Array.<~depends>} depends Array of depends objects
* @param {string} property Name of property
* @return {Array.<string>} The values from the depends objects
*/
Config.prototype.dependsProperty = function (property) {
return this.depends.map(function (oneDependency) {
return oneDependency[property];
});
};
/**
* Expanded depends objects
*
* @typedef {Object} Config~depends
* @property {string} amd Module name/path for AMD
* @property {string} commonjs Filename for CommonJS
* @property {string} commonjsmod Module name for CommonJS
* @property {string} name Module name as seen inside your function
* @property {string} root Name in a root object, like a browser's window
* @property {string} yui YUI Module name
*/
/**
* Expand a depends array into a an array of full object
*
* Full object has these properties
* amd: Module name/path to load, defaults to "name" property
* nodejs: Module or filename to load, defaults to "name" property
* commonjs: Filename to load, defaults to "name" property
* commonjsmod: Module to load, defaults using the object that's returned
* name: Name of the variable passed into the factory
* root: Property off root object, defaults to "name" property
* yui: YUI module name, defaults to "name" property
*
* Input (JSON) -> output (JavaScript) examples:
* "Name" ->
* { commonjs: "Name", commonjsmod: "", name: "Name",
* nodejs: "Name", amd: "Name", root: "Name", yui: "Name" }
* {"name":"Template",commonjs:"./template",root:"TemplateThing"} ->
* { commonjs: "./template", commonjsmod: "Template", name: "Template",
* nodejs: "Template", amd: "Template", root: "TemplateThing",
* yui: "Template" }
*
* @param {!(string|Object)} input
* @return {Config~depends}
*/
Config.prototype.expandDepends = function (input) {
var result, self;
function copyProperty(prop) {
if (input[prop] !== undefined) {
result[prop] = input[prop];
}
}
function makeDefault(str) {
var output;
output = {
name: str
};
self.configModuleSystems.forEach(function (systemName) {
output[systemName] = str;
});
self.configModuleSystemsAdditional.forEach(function (systemName) {
output[systemName] = '';
});
return output;
}
self = this;
if (typeof input === 'string') {
return makeDefault(input);
}
if (!input.name) {
result = makeDefault('Unknown');
} else {
result = makeDefault(input.name);
}
this.configModuleSystems.forEach(copyProperty);
this.configModuleSystemsAdditional.forEach(copyProperty);
return result;
};
/**
* Export the config as a string, only preserving properties we want and
* in a minimal format.
*
* Only preserves these properties:
* name: string
* depends: array of strings unless it is empty
* jslint: boolean, saved as 1 or unset
*
* @return {string}
*/
Config.prototype.exportConfig = function () {
var outObj, self;
self = this;
outObj = {};
Object.keys(this.configProperties).forEach(function (key) {
var v = self[key];
// We never save falsy values
if (!v) {
return;
}
switch (self.configProperties[key]) {
case 'array':
// Preserve if length > 0
if (!Array.isArray(v) || !v.length) {
return;
}
break;
case 'boolean':
// Save as a 1, a very short value
v = 1;
break;
case 'string':
// Save strings that have a length
if (typeof v !== 'string' || !v.length) {
return;
}
break;
}
outObj[key] = v;
});
// Shrink the depends array of objects
if (outObj.depends) {
outObj.depends = outObj.depends.map(this.unexpandDepends.bind(this));
}
return JSON.stringify(outObj);
};
/**
* Force properties to be in a standard form to reduce the code elsewhere.
*
* @param {!Object} inObj Shortened form of {@link Config}
* @return this
*/
Config.prototype.loadConfig = function (inObj) {
var self;
self = this;
Object.keys(this.configProperties).forEach(function (key) {
var v = inObj[key];
switch (self.configProperties[key]) {
case 'array':
// Preserve if length > 0
if (!Array.isArray(v) || !v.length) {
v = [];
}
break;
case 'boolean':
// Cast to boolean
v = !!v;
break;
case 'string':
// Save strings that have a length
if (typeof v !== 'string' || !v.length) {
v = "";
}
break;
}
self[key] = v;
});
// Set defaults
if (self.name === "") {
self.name = 'Unknown';
}
// Expand depends into full objects
self.depends = self.depends.map(this.expandDepends.bind(this));
return this;
};
/**
* Condense a depends object into as small of a form as possible.
* This is the reverse of expandDepends()
*
* @param {!Config~depends} input
* @return {(Object|string)} Shortened form of {@link FidUmd~depends}
*/
Config.prototype.unexpandDepends = function (input) {
var output;
output = {
name: input.name
};
this.configModuleSystems.forEach(function (prop) {
if (input[prop] !== input.name) {
output[prop] = input[prop];
}
});
this.configModuleSystemsAdditional.forEach(function (prop) {
if (input[prop]) {
output[prop] = input[prop];
}
});
if (Object.keys(output).length === 1) {
return output.name;
}
return output;
};
module.exports = Config;
},{}],2:[function(require,module,exports){
/**
* Amd - UMD generator fragment for AMD
*
* @module Amd
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Amd
* @param {Config} config
*/
function Amd(config) {
if (!(this instanceof Amd)) {
return new Amd(config);
}
this.config = config;
this.name = "amd";
this.depends = config.dependsProperty(this.name);
config.functionsNeeded.isObject = true;
config.globalVariables.define = true;
}
/**
* Return the condition that checks if this is the right environment.
*
* For AMD, you need to use the `define` function, but only after
* you check for `define.amd` to be truthy.
*
* @return {string}
*/
Amd.prototype.condition = function () {
return 'isObject(root.define) && root.define.amd';
};
/**
* Generate the module load code.
*
* For AMD, this looks like the following examples, making calls to
* a `define` function.
*
* define(factory);
* define([ "One", "Two" ], factory);
*
* @return {string}
*/
Amd.prototype.loader = function () {
var code;
code = 'root.define(name, [';
if (this.depends.length) {
code += '"' + this.depends.join('", "') + '"';
}
code += '], factory);';
return code;
};
module.exports = Amd;
},{}],3:[function(require,module,exports){
/**
* Commonjs - UMD generator fragment for CommonJS
*
* @module Commonjs
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Commonjs
* @param {Config} config
*/
function Commonjs(config) {
if (!(this instanceof Commonjs)) {
return new Commonjs(config);
}
this.name = "commonjs";
this.config = config;
this.config.globalVariables.exports = true;
this.dependsFile = config.dependsProperty(this.name);
this.dependsModule = config.dependsProperty(this.name + "mod");
config.functionsNeeded.isObject = true;
}
/**
* Return the condition that checks if this is the right environment.
*
* For common.js, you are supposed to only add properties to `exports`.
* To keep this in line with other module loaders, only one thing is added
* to `exports`, which matches the module name. This may not be the same
* for other CommonJS modules.
*
* @return {string}
*/
Commonjs.prototype.condition = function () {
return 'isObject(root.exports)';
};
/**
* Generate the module load code.
*
* For CommonJS, this looks like one of these examples.
*
* exports.myModule = factory();
* exports.myModule = factory(require('one').One, require('two').Two);
*
* @return {string}
*/
Commonjs.prototype.loader = function () {
var code, i;
code = 'root.exports[name] = factory(';
for (i = 0; i < this.dependsFile.length; i += 1) {
if (i > 0) {
code += ', ';
}
code += 'root.require(' + JSON.stringify(this.dependsFile[i]) + ')';
if (this.dependsModule[i]) {
code += '.' + this.dependsModule[i];
}
}
code += ');';
return code;
};
module.exports = Commonjs;
},{}],4:[function(require,module,exports){
/**
* Modulejs - UMD generator fragment for modulejs
*
* @module Modulejs
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Modulejs
* @param {Config} config
*/
function Modulejs(config) {
if (!(this instanceof Modulejs)) {
return new Modulejs(config);
}
this.config = config;
this.name = "modulejs";
this.depends = config.dependsProperty(this.name);
config.functionsNeeded.isObject = true;
config.globalVariables.modulejs = true;
}
/**
* Return the condition that checks if this is the right environment.
*
* For modulejs, everything is accessed via the `modulejs` object.
*
* @return {string}
*/
Modulejs.prototype.condition = function () {
return 'isObject(root.modulejs)';
};
/**
* Generate the module load code.
*
* For modulejs, this looks like the following examples, which is very
* similar to RequireJS.
*
* modulejs.define("myModule", factory);
* modulejs.define("myModule", [ "One", "Two" ], factory);
*
* @return {string}
*/
Modulejs.prototype.loader = function () {
var code;
code = 'root.modulejs.define(name, ';
if (this.depends.length) {
code += '["' + this.depends.join('", "') + '"], ';
}
code += 'factory);';
return code;
};
module.exports = Modulejs;
},{}],5:[function(require,module,exports){
/**
* Nodejs - UMD generator fragment for Node.js
*
* @module Nodejs
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Nodejs
* @param {Config} config
*/
function Nodejs(config) {
if (!(this instanceof Nodejs)) {
return new Nodejs(config);
}
this.config = config;
this.config.globalVariables.module = true;
this.name = "nodejs";
this.depends = config.dependsProperty(this.name);
config.functionsNeeded.isObject = true;
}
/**
* Return the condition that checks if this is the right environment.
*
* For Node.js, you are supposed to replace `module.exports` with the
* thing you wish to export, or add properties to `exports`. We choose
* the former version to export just one object.
*
* @return {string}
*/
Nodejs.prototype.condition = function () {
return 'isObject(root.module) && isObject(root.module.exports)';
};
/**
* Generate the module load code.
*
* For Node.js, this looks like `module.exports = factory()`, passing
* in dependencies to the factory.
*
* module.exports = factory();
* module.exports = factory(require('one'), require('two'));
*
* @return {string}
*/
Nodejs.prototype.loader = function () {
var code, i;
code = 'root.module.exports = factory(';
for (i = 0; i < this.depends.length; i += 1) {
if (i > 0) {
code += ', ';
}
code += 'root.require(' + JSON.stringify(this.depends[i]) + ')';
}
code += ');';
return code;
};
module.exports = Nodejs;
},{}],6:[function(require,module,exports){
/**
* Root - UMD generator fragment for adding to the global object
*
* @module Root
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Root
* @param {Config} config
*/
function Root(config) {
if (!(this instanceof Root)) {
return new Root(config);
}
this.config = config;
this.name = "root";
this.depends = config.dependsProperty(this.name);
}
/**
* Return the condition that checks if this is the right environment.
*
* When we attach to the global object, we don't need to ever check.
*
* @return {string}
*/
Root.prototype.condition = function () {
return '';
};
/**
* Generate the module load code.
*
* For attaching to the global object, we just assume things have been
* loaded.
*
* root.myModule = factory();
* root.myModule = factory(root.One, root.Two);
*
* @return {string}
*/
Root.prototype.loader = function () {
var code;
code = 'root[name] = factory(';
if (this.depends.length) {
code += 'root.' + this.depends.join(', root.');
}
code += ');';
return code;
};
module.exports = Root;
},{}],7:[function(require,module,exports){
/**
* Yui - UMD generator fragment for YUI
*
* @module Yui
* @license MIT
*/
"use strict";
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:Yui
* @param {Config} config
*/
function Yui(config) {
if (!(this instanceof Yui)) {
return new Yui(config);
}
this.config = config;
this.name = "yui";
this.config.functionsNeeded.isObject = true;
this.config.globalVariables.YUI = true;
this.depends = config.dependsProperty(this.name);
}
/**
* Return the condition that checks if this is the right environment.
*
* For YUI, you use the global YUI object.
*
* @return {string}
*/
Yui.prototype.condition = function () {
var code;
code = 'isObject(root.YUI)';
return code;
};
/**
* Generate the module load code.
*
* For YUI there are two syntaxes, one is for no dependencies and the other
* lets you specify a list of dependencies.
*
* YUI.add("myModule", function (Y) { Y.myModule = factory(); });
* YUI.add("myModule", function (Y) {
* Y.myModule = factory(Y.One, Y.Two); }, "",
* { requires: ["One", "Two"] });
*
* @return {string}
*/
Yui.prototype.loader = function () {
var code;
code = 'root.YUI.add(name, function (Y) { Y[name] = factory(';
if (this.depends.length) {
code += 'Y.' + this.depends.join(', Y.');
}
code += '); }';
if (this.depends.length) {
code += ', "", { requires: ["' + this.depends.join('", "') + '"] }';
}
code += ');';
return code;
};
module.exports = Yui;
},{}],8:[function(require,module,exports){
/**
* FidUmd - UMD generator to wrap your JavaScript modules
*
* @module FidUmd
* @license MIT
*/
"use strict";
var Config, moduleSystemObjects;
// Prepare our list of module systems in the order we wish to use them
moduleSystemObjects = [
require('./modules/nodejs'),
require('./modules/commonjs'),
require('./modules/amd'),
require('./modules/modulejs'),
require('./modules/yui'),
require('./modules/root')
];
Config = require('./config');
/**
* Create an object that can wrap code in UMD headers/footers
*
* @constructor
* @alias module:FidUmd
* @property {number} counter Used for generating unique variables in tryTo()
*/
function FidUmd() {
if (!(this instanceof FidUmd)) {
return new FidUmd();
}
}
/**
* Creates configured module system instances
*
* @return {Array.<Object>}
*/
FidUmd.prototype.configureModuleSystems = function (config) {
return moduleSystemObjects.map(function (System) {
return new System(config);
});
};
/**
* Create the postamble string. This is almost always at the end of
* the file
*
* @return {string}
*/
FidUmd.prototype.createPostamble = function () {
return ' // fid-umd post\n}));\n// fid-umd post-end\n';
};
/**
* Create a preamble string
*
* config is an object similar to this
*
* // No dependencies
* config = {
* name: "MyObject"
* }
*
* // Dependency on OneOne and TwoTwo
* // For node, OneOne is an external library, TwoTwo is something local
* config = {
* name: 'HorseRacingTrack',
* depends: [
* 'OneOne',
* 'TwoTwo'
* ]
* }
*
* @param {!Config} config Controls dependencies and exported name
* @return {string} Preamble
*/
FidUmd.prototype.createPreamble = function (config) {
var code, dependencies, i, moduleSystems, preamble;
moduleSystems = this.configureModuleSystems(config);
// Marker for the preamble
preamble = '// fid-umd ' + config.exportConfig() + '\n';
preamble += this.jslintGlobalVariables(config);
if (config.global) {
preamble += '(function (name, root, factoryForGlobal) {\n';
} else {
preamble += '(function (name, root, factory) {\n';
}
if (config.jslint) {
preamble += ' "use strict";\n';
}
if (config.global) {
// This calls factoryWithoutContext in the global scope
preamble += this.createPreambleGlobalScope(config);
}
preamble += this.writeNeededFunctions(config);
if (config.debug) {
preamble += ' console.log("Attempting to define " + name);\n';
}
// Add the various loaders
for (i = 0; i < moduleSystems.length; i += 1) {
if (i === 0) {
preamble += ' ';
} else {
preamble += ' else ';
}
code = moduleSystems[i].condition();
if (code) {
preamble += 'if (' + code + ') ';
}
preamble += '{\n';
if (config.debug) {
preamble += ' console.log("' + moduleSystems[i].name + ' detected");\n';
}
preamble += ' ';
preamble += moduleSystems[i].loader();
preamble += '\n';
if (config.debug) {
preamble += ' console.log("' + moduleSystems[i].name + ' success");\n';
}
preamble += ' }';
}
// Finish the preamble
dependencies = config.dependsProperty('name').join(', ');
preamble += "\n";
preamble += '}(' + JSON.stringify(config.name) + ', this, function (' + dependencies + ') {\n';
if (config.jslint) {
preamble += ' "use strict";\n';
}
preamble += ' // fid-umd end\n';
return preamble;
};
/**
* Defines the factory() function when we want to execute the
* factory in global scope
*
* @param {!Config} config Determines dependencies
* @return {string} Preamble
*/
FidUmd.prototype.createPreambleGlobalScope = function (config) {
var dependencies, preamble;
dependencies = config.dependsProperty('name').join(', ');
preamble = ' function factory(' + dependencies + ') { return factoryForGlobal.call(root';
if (dependencies) {
preamble += ', ' + dependencies;
}
preamble += '); };\n';
return preamble;
};
/**
* Detect an existing config or create a new default config
*
* @param {string} code
* @param {Function} ConfigConstructor
* @return {Object} Short version of {@link FidUmd~config} object
*/
FidUmd.prototype.detectConfig = function (code, ConfigConstructor) {
var config, matches, parsedConfig;
/*jslint regexp:true*/
matches = code.match(/\/\/ fid-umd (\{.*\})/);
/*jslint regexp:false*/
config = new ConfigConstructor();
if (matches) {
try {
parsedConfig = JSON.parse(matches[1]);
config.loadConfig(parsedConfig);
} catch (e) {
throw new Error('Invalid JSON: ' + matches[1]);
}
}
return config;
};
/**
* To avoid warnings with jslint, write a "globals" declaration
* at the top of the UMD
*
* @param {Config} config
* @return {string}
*/
FidUmd.prototype.jslintGlobalVariables = function (config) {
var globals, name;
globals = [];
for (name in config.globalVariables) {
if (config.globalVariables.hasOwnProperty(name)) {
globals.push(name);
}
}
if (!config.jslint || !globals.length) {
return '';
}
return '/* global ' + globals.join(', ') + ' */\n';
};
/**
* Split code into chunks
*
* Look for the first marker.
* If not found, return [ code, '' ]
* If found, look for the second marker after the first
* If not found, return [ code-before-first, code-after-first ]
* If found, return [code-before-first, code-after-second ]
*
* @param {string} code
* @param {!RegExp} startMarker
* @param {!RegExp} endMarker
* @return {Array.<string>} Code before and after markers
*/
FidUmd.prototype.splitCode = function (code, startMarker, endMarker) {
var match, result;
result = [
code,
''
];
match = code.match(startMarker);
if (!match) {
// Did not match
return result;
}
result[0] = code.substr(0, match.index);
result[1] = code.substr(match.index + match[0].length);
// Now look for the end marker
match = result[1].match(endMarker);
if (match) {
// Limit the "after" portion to everything after the marker
result[1] = result[1].substr(match.index + match[0].length);
}
return result;
};
/**
* Update the UMD code in some JavaScript
*
* @param {string} oldCode
* @return {string} Updated code with new preamble and postamble
*/
FidUmd.prototype.update = function (oldCode) {
var config, newCode;
if (!oldCode) {
oldCode = '';
}
oldCode = oldCode.toString();
config = this.detectConfig(oldCode, Config);
newCode = oldCode;
newCode = this.updatePreamble(newCode, config);
newCode = this.updatePostamble(newCode);
return newCode;
};
/**
* Update the postamble
*
* @param {string} inCode Code to update
* @param {!Config} config
* @return {string} Updated code
*/
FidUmd.prototype.updatePostamble = function (inCode) {
var outCode, outCodePieces;
outCodePieces = this.splitCode(inCode, /^[\t ]*\/\/ fid-umd post\n?/m, /[\t ]*\/\/ fid-umd post-end\n?/m);
outCode = outCodePieces[0];
if (outCode.substr(-1) !== "\n") {
outCode += "\n";
}
outCode += this.createPostamble() + outCodePieces[1];
return outCode;
};
/**
* Update the preamble
*
* @param {string} inCode Code to update
* @param {!Config} config
* @return {string} Updated code
*/
FidUmd.prototype.updatePreamble = function (inCode, config) {
var outCode, outCodePieces;
/*jslint regexp:true*/
outCodePieces = this.splitCode(inCode, /^[ \t]*\/\/ fid-umd \{.*\n?/m, /^[ \t]*\/\/ fid-umd end\n?/m);
/*jslint regexp:false*/
// Force the preamble to be at the beginning
if (outCodePieces[1] === '') {
outCodePieces[1] = outCodePieces[0];
outCodePieces[0] = '';
}
outCode = outCodePieces[0] + this.createPreamble(config) + outCodePieces[1];
return outCode;
};
/**
* Write out the code for necessary functions.
*
* @param {!Config} config
* @return {string}
*/
FidUmd.prototype.writeNeededFunctions = function (config) {
var result;
result = '';
if (config.functionsNeeded.isObject) {
result += ' function isObject(x) { return typeof x === "object"; }\n';
}
return result;
};
module.exports = FidUmd;
},{"./config":1,"./modules/amd":2,"./modules/commonjs":3,"./modules/modulejs":4,"./modules/nodejs":5,"./modules/root":6,"./modules/yui":7}]},{},[8])(8)
});