UNPKG

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
!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) });