From 566c4fc1eef6baba607d356190863407d28eb5b9 Mon Sep 17 00:00:00 2001 From: jrburke Date: Thu, 26 Apr 2012 21:55:04 -0700 Subject: [PATCH] legacy build test passes. --- build/jslib/build.js | 28 +++++--- build/jslib/lang.js | 12 +++- build/jslib/requirePatch.js | 91 +++++++++++++++++-------- build/tests/builds.js | 24 +++++++ build/tests/lib/legacyBasic/build.js | 7 ++ build/tests/lib/legacyBasic/expected.js | 74 ++++++++++++++++++++ require.js | 74 ++++++++++++++++---- 7 files changed, 255 insertions(+), 55 deletions(-) create mode 100644 build/tests/lib/legacyBasic/build.js create mode 100644 build/tests/lib/legacyBasic/expected.js diff --git a/build/jslib/build.js b/build/jslib/build.js index 3a2f2d0a..31bf7d9b 100644 --- a/build/jslib/build.js +++ b/build/jslib/build.js @@ -942,7 +942,7 @@ function (lang, logger, file, parse, optimize, pragma, context = layer.context, anonDefRegExp = config.anonDefRegExp, path, reqIndex, fileContents, currContents, - i, moduleName, + i, moduleName, legacy, parts, builder, writeApi; //Use override settings, particularly for pragmas @@ -1020,16 +1020,13 @@ function (lang, logger, file, parse, optimize, pragma, //after the module is processed. //If we have a name, but no defined module, then add in the placeholder. if (moduleName && !layer.modulesWithNames[moduleName] && !config.skipModuleInsertion) { - //If including jquery, register the module correctly, otherwise - //register an empty function. For jquery, make sure jQuery is - //a real object, and perhaps not some other file mapping, like - //to zepto. - if (moduleName === 'jquery') { - fileContents += '\n(function () {\n' + - 'var jq = typeof jQuery !== "undefined" && jQuery;\n' + - namespace + - 'define("jquery", [], function () { return jq; });\n' + - '}());\n'; + legacy = config.legacy && config.legacy[moduleName]; + if (legacy) { + fileContents += '\n' + namespace + 'define("' + moduleName + '", ' + + (legacy.deps && legacy.deps.length ? + build.makeJsArrayString(legacy.deps) + ', ' : '') + + (legacy.exports ? legacy.exports() : 'function(){}') + + ');\n'; } else { fileContents += '\n' + namespace + 'define("' + moduleName + '", function(){});\n'; } @@ -1044,6 +1041,15 @@ function (lang, logger, file, parse, optimize, pragma, }; }; + //Converts an JS array of strings to a string representation. + //Not using JSON.stringify() for Rhino's sake. + build.makeJsArrayString = function (ary) { + return '["' + ary.map(function (item) { + //Escape any double quotes, backslashes + return lang.jsEscape(item); + }).join('","') + '"]'; + }; + /** * Creates the regexp to find anonymous defines. * @param {String} namespace an optional namespace to use. The namespace diff --git a/build/jslib/lang.js b/build/jslib/lang.js index 2760ad2f..56898ca5 100644 --- a/build/jslib/lang.js +++ b/build/jslib/lang.js @@ -109,8 +109,18 @@ define(function () { return function () { return fn.apply(obj, arguments); }; - } + }, + //Escapes a content string to be be a string that has characters escaped + //for inclusion as part of a JS string. + jsEscape: function (content) { + return content.replace(/(["'\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r"); + } }; return lang; }); diff --git a/build/jslib/requirePatch.js b/build/jslib/requirePatch.js index 31d930f4..d11dad17 100644 --- a/build/jslib/requirePatch.js +++ b/build/jslib/requirePatch.js @@ -58,34 +58,6 @@ function (file, pragma, parse, lang) { //Stored cached file contents for reuse in other layers. require._cachedFileContents = {}; - /** Reset state for each build layer pass. */ - require._buildReset = function () { - var oldContext = require.s.contexts._; - - //Clear up the existing context. - delete require.s.contexts._; - - //Set up new context, so the layer object can hold onto it. - require({}); - - layer = require._layer = { - buildPathMap: {}, - buildFileToModule: {}, - buildFilePaths: [], - pathAdded: {}, - modulesWithNames: {}, - needsDefine: {}, - existingRequireUrl: "", - context: require.s.contexts._ - }; - - //Return the previous context in case it is needed, like for - //the basic config object. - return oldContext; - }; - - require._buildReset(); - /** * Makes sure the URL is something that can be supported by the * optimization tool. @@ -124,6 +96,35 @@ function (file, pragma, parse, lang) { context.fullExec = {}; context.plugins = {}; + //Override the legacy exports function generator to just + //spit out strings that can be used in the stringified + //build output. + context.makeLegacyExports = function (exports) { + var result; + if (typeof exports === 'string') { + result = function () { + return '(function (global) {\n' + + ' return function () {\n' + + ' return global["' + exports + '"];\n' + + ' }\n' + + '}(this))'; + }; + } else { + result = function () { + return '(function (global) {\n' + + ' return function () {\n' + + ' var func = ' + exports.toString() + ';\n' + + ' return func.apply(global, arguments);\n' + + ' }\n' + + '}(this))'; + }; + } + + //Mark the result has being tranformed by the build already. + result.__buildReady = true; + return result; + }; + context.enable = function (depMap, parent) { var id = depMap.id, parentId = parent && parent.map.id, @@ -144,7 +145,7 @@ function (file, pragma, parse, lang) { }; //Override load so that the file paths can be collected. - context.load = function (context, moduleName, url) { + context.load = function (moduleName, url) { /*jslint evil: true */ var contents, pluginBuilderMatch, builderName; @@ -315,6 +316,38 @@ function (file, pragma, parse, lang) { return context; }; + //Clear up the existing context so that the newContext modifications + //above will be active. + delete require.s.contexts._; + + /** Reset state for each build layer pass. */ + require._buildReset = function () { + var oldContext = require.s.contexts._; + + //Clear up the existing context. + delete require.s.contexts._; + + //Set up new context, so the layer object can hold onto it. + require({}); + + layer = require._layer = { + buildPathMap: {}, + buildFileToModule: {}, + buildFilePaths: [], + pathAdded: {}, + modulesWithNames: {}, + needsDefine: {}, + existingRequireUrl: "", + context: require.s.contexts._ + }; + + //Return the previous context in case it is needed, like for + //the basic config object. + return oldContext; + }; + + require._buildReset(); + //Override define() to catch modules that just define an object, so that //a dummy define call is not put in the build file for them. They do //not end up getting defined via context.execCb, so we need to catch them diff --git a/build/tests/builds.js b/build/tests/builds.js index 86b32fce..eb9e19cf 100644 --- a/build/tests/builds.js +++ b/build/tests/builds.js @@ -883,4 +883,28 @@ define(['build', 'env!env/file'], function (build, file) { ] ); doh.run(); + + + doh.register("legacyBasic", + [ + function legacyBasic(t) { + var outFile = "../../../requirejs/tests/legacy/built/basic-tests.js"; + + file.deleteFile(outFile); + + build(["lib/legacyBasic/build.js"]); + + //Also remove spaces, since rhino and node differ on their + //Function.prototype.toString() output by whitespace, and + //the semicolon on end of A.name + t.is(nol(c("lib/legacyBasic/expected.js")).replace(/\s+/g, '').replace(/A\.name\;/g, 'A.name'), + nol(c(outFile)).replace(/\s+/g, '').replace(/A\.name\;/g, 'A.name')); + + require._buildReset(); + } + + ] + ); + doh.run(); + }); diff --git a/build/tests/lib/legacyBasic/build.js b/build/tests/lib/legacyBasic/build.js new file mode 100644 index 00000000..771b3a20 --- /dev/null +++ b/build/tests/lib/legacyBasic/build.js @@ -0,0 +1,7 @@ +{ + baseUrl: '../../../../../requirejs/tests/legacy', + mainConfigFile: '../../../../../requirejs/tests/legacy/basic-tests.js', + name: 'basic-tests', + out: '../../../../../requirejs/tests/legacy/built/basic-tests.js', + optimize: 'none' +} diff --git a/build/tests/lib/legacyBasic/expected.js b/build/tests/lib/legacyBasic/expected.js new file mode 100644 index 00000000..1c3a366e --- /dev/null +++ b/build/tests/lib/legacyBasic/expected.js @@ -0,0 +1,74 @@ + +(function (root) { + root.A = { + name: 'a' + }; +}(this)); + +define("a", (function (global) { + return function () { + var func = function (){return this.A.name}; + return func.apply(global, arguments); + } +}(this))); + +function D() { + this.name = 'd'; +}; + +define("d", function(){}); + +var B = { + name: 'b', + aValue: A.name, + dValue: new D() +}; + +define("b", function(){}); + +var C = { + name: 'c', + a: A, + b: B +}; + +define("c", ["a","b"], (function (global) { + return function () { + return global["C"]; + } +}(this))); + +require({ + baseUrl: './', + legacy: { + a: { + exports: function () { + return this.A.name; + } + }, + 'b': ['a', 'd'], + 'c': { + deps: ['a', 'b'], + exports: 'C' + } + } + }, + ['a', 'c'], + function(a, c) { + doh.register( + 'legacyBasic', + [ + function legacyBasic(t){ + t.is('a', a); + t.is('a', c.b.aValue); + t.is('b', c.b.name); + t.is('c', c.name); + t.is('d', c.b.dValue.name); + } + ] + ); + doh.run(); + } +); + +define("basic-tests", function(){}); diff --git a/require.js b/require.js index 45c976b3..200ec53f 100644 --- a/require.js +++ b/require.js @@ -7,7 +7,7 @@ /*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ var requirejs, require, define; -(function () { +(function (global) { 'use strict'; var version = "2.0.0zdev", @@ -145,7 +145,8 @@ var requirejs, require, define; waitSeconds: 7, baseUrl: "./", paths: {}, - pkgs: {} + pkgs: {}, + legacy: {} }, registry = {}, undefEvents = {}, @@ -432,6 +433,9 @@ var requirejs, require, define; return (mod.module = { id: mod.map.id, uri: mod.map.url, + config: function () { + return config.config[mod.map.id] || {}; + }, exports: defined[mod.map.id] }); } @@ -664,6 +668,7 @@ var requirejs, require, define; Module = function (map) { this.events = undefEvents[map.id] || {}; this.map = map; + this.legacy = config.legacy[map.id]; this.depExports = []; this.depMaps = []; this.depMatched = []; @@ -776,20 +781,29 @@ var requirejs, require, define; context.startTime = (new Date()).getTime(); - var map = this.map, - url = map.url, - id = map.id; + var map = this.map; //If the manager is for a plugin managed resource, //ask the plugin to load it now. if (map.prefix) { this.callPlugin(); + } else if (this.legacy) { + makeRequire(this, true)(this.legacy.deps || [], bind(this, function () { + this.load(); + })); } else { //Regular dependency. - if (!urlFetched[url] && !this.inited) { - urlFetched[url] = true; - context.load(context, id, url); - } + this.load(); + } + }, + + load: function() { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); } }, @@ -1060,7 +1074,7 @@ var requirejs, require, define; * @param {Object} cfg config object to integrate. */ configure: function (cfg) { - var paths, packages, pkgs; + var paths, packages, pkgs, legacy; //Make sure the baseUrl ends in a slash. if (cfg.baseUrl) { @@ -1074,6 +1088,7 @@ var requirejs, require, define; paths = config.paths; packages = config.packages; pkgs = config.pkgs; + legacy = config.legacy; //Mix in the config values, favoring the new values over //existing ones in context.config. @@ -1083,6 +1098,23 @@ var requirejs, require, define; mixin(paths, cfg.paths, true); config.paths = paths; + //Merge legacy + if (cfg.legacy) { + eachProp(cfg.legacy, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if (value.exports && !value.exports.__buildReady) { + value.exports = context.makeLegacyExports(value.exports); + } + legacy[id] = value; + }); + config.legacy = legacy; + } + //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { @@ -1119,6 +1151,19 @@ var requirejs, require, define; } }, + makeLegacyExports: function (exports) { + + if (typeof exports === 'string') { + return function () { + return global[exports]; + }; + } else { + return function () { + return exports.apply(global, arguments); + }; + } + }, + require: function (deps, callback, errback, relMap) { var moduleName, id, map, requireMod, args; if (typeof deps === "string") { @@ -1243,7 +1288,8 @@ var requirejs, require, define; * @param {String} moduleName the name of the module to potentially complete. */ completeLoad: function (moduleName) { - var found, args; + var legacy = config.legacy[moduleName] || {}, + found, args; context.takeGlobalQueue(); @@ -1269,7 +1315,7 @@ var requirejs, require, define; if (!found && !defined[moduleName]) { //A script that does not call define(), so just simulate //the call for it. - callGetModule([moduleName, [], null]); + callGetModule([moduleName, (legacy.deps || []), legacy.exports]); } checkLoaded(); @@ -1351,7 +1397,7 @@ var requirejs, require, define; //Delegates to req.load. Broken out as a separate function to //allow overriding in the optimizer. - load: function (context, id, url) { + load: function (id, url) { req.load(context, id, url); }, @@ -1821,4 +1867,4 @@ var requirejs, require, define; ctx.require([]); }, 0); } -}()); +}(this));