Permalink
Browse files

Using esprima for the toTransport calls. Not quite done yet.

  • Loading branch information...
1 parent ed83df9 commit 2f579ab21319eae4da417203443ec5dfe78eaa02 @jrburke jrburke committed May 26, 2012
View
91 build/jslib/build.js
@@ -9,9 +9,9 @@
define([ 'lang', 'logger', 'env!env/file', 'parse', 'optimize', 'pragma',
- 'env!env/load', 'requirePatch', 'env!env/quit'],
+ 'transform', 'env!env/load', 'requirePatch', 'env!env/quit'],
function (lang, logger, file, parse, optimize, pragma,
- load, requirePatch, quit) {
+ transform, load, requirePatch, quit) {
'use strict';
var build, buildBaseConfig,
@@ -55,15 +55,15 @@ function (lang, logger, file, parse, optimize, pragma,
//Method used by plugin writeFile calls, defined up here to avoid
//jslint warning about "making a function in a loop".
- function makeWriteFile(anonDefRegExp, namespaceWithDot, layer) {
+ function makeWriteFile(namespace, layer) {
function writeFile(name, contents) {
logger.trace('Saving plugin-optimized file: ' + name);
file.saveUtf8File(name, contents);
}
writeFile.asModule = function (moduleName, fileName, contents) {
writeFile(fileName,
- build.toTransport(anonDefRegExp, namespaceWithDot, moduleName, fileName, contents, layer));
+ build.toTransport(namespace, moduleName, fileName, contents, layer));
};
return writeFile;
@@ -403,8 +403,7 @@ function (lang, logger, file, parse, optimize, pragma,
//inserted (by passing null for moduleName) since the files are
//standalone, one module per file.
fileContents = file.readFile(fileName);
- fileContents = build.toTransport(config.anonDefRegExp,
- config.namespaceWithDot,
+ fileContents = build.toTransport(config.namespace,
null,
fileName,
fileContents);
@@ -455,8 +454,7 @@ function (lang, logger, file, parse, optimize, pragma,
moduleMap.name,
require,
makeWriteFile(
- config.anonDefRegExp,
- config.namespaceWithDot
+ config.namespace
),
context.config
);
@@ -878,12 +876,6 @@ function (lang, logger, file, parse, optimize, pragma,
'startFile/endFile: ' + wrapError.toString());
}
-
- //Set up proper info for namespaces and using namespaces in transport
- //wrappings.
- config.namespaceWithDot = config.namespace ? config.namespace + '.' : '';
- config.anonDefRegExp = build.makeAnonDefRegExp(config.namespaceWithDot);
-
//Do final input verification
if (config.context) {
throw new Error('The build argument "context" is not supported' +
@@ -1048,10 +1040,9 @@ function (lang, logger, file, parse, optimize, pragma,
*/
build.flattenModule = function (module, layer, config) {
var buildFileContents = "",
- namespace = config.namespace ? config.namespace + '.' : '',
+ namespace = config.namespace || '',
stubModulesByName = (config.stubModules && config.stubModules._byName) || {},
context = layer.context,
- anonDefRegExp = config.anonDefRegExp,
path, reqIndex, fileContents, currContents,
i, moduleName, shim,
parts, builder, writeApi;
@@ -1097,7 +1088,7 @@ function (lang, logger, file, parse, optimize, pragma,
writeApi.asModule = function (moduleName, input) {
fileContents += "\n" +
addSemiColon(
- build.toTransport(anonDefRegExp, namespace, moduleName, path, input, layer));
+ build.toTransport(namespace, moduleName, path, input, layer));
if (config.onBuildWrite) {
fileContents = config.onBuildWrite(moduleName, path, fileContents);
}
@@ -1128,7 +1119,7 @@ function (lang, logger, file, parse, optimize, pragma,
currContents = pragma.namespace(currContents, config.namespace);
}
- currContents = build.toTransport(anonDefRegExp, namespace, moduleName, path, currContents, layer);
+ currContents = build.toTransport(namespace, moduleName, path, currContents, layer);
if (config.onBuildWrite) {
currContents = config.onBuildWrite(moduleName, path, currContents);
@@ -1182,70 +1173,16 @@ function (lang, logger, file, parse, optimize, pragma,
}).join('","') + '"]';
};
- /**
- * Creates the regexp to find anonymous defines.
- * @param {String} namespace an optional namespace to use. The namespace
- * should *include* a trailing dot. So a valid value would be 'foo.'
- * @returns {RegExp}
- */
- build.makeAnonDefRegExp = function (namespace) {
- //This regexp is not bullet-proof, and it has one optional part to
- //avoid issues with some Dojo transition modules that use a
- //define(\n//begin v1.x content
- //for a comment.
- return new RegExp('(^|[^\\.])(' + (namespace || '').replace(/\./g, '\\.') +
- 'define|define)\\s*\\(\\s*(\\/\\/[^\\n\\r]*[\\r\\n])?(\\[|function|[\\w\\d_\\-\\$]+\\s*\\)|\\{|["\']([^"\']+)["\'])(\\s*,\\s*f)?');
- };
-
- build.leadingCommaRegExp = /^\s*,/;
-
- build.toTransport = function (anonDefRegExp, namespace, moduleName, path, contents, layer) {
-
- //If anonymous module, insert the module name.
- return contents.replace(anonDefRegExp, function (match, start, callName, possibleComment, suffix, namedModule, namedFuncStart) {
- //A named module with either listed dependencies or an object
- //literal for a value. Skip it. If named module, only want ones
- //whose next argument is a function literal to scan for
- //require('') dependecies.
- if (namedModule && !namedFuncStart) {
- return match;
- }
-
+ build.toTransport = function (namespace, moduleName, path, contents, layer) {
+ function onFound(info) {
//Only mark this module as having a name if not a named module,
//or if a named module and the name matches expectations.
- if (layer && (!namedModule || namedModule === moduleName)) {
+ if (layer && (info.needsId || info.foundId === moduleName)) {
layer.modulesWithNames[moduleName] = true;
}
+ }
- var deps = null,
- finalName;
-
- //Look for CommonJS require calls inside the function if this is
- //an anonymous define call that just has a function registered.
- //Also look if a named define function but has a factory function
- //as the second arg that should be scanned for dependencies.
- if (suffix.indexOf('f') !== -1 || (namedModule)) {
- deps = parse.getAnonDeps(path, contents);
-
- if (deps.length) {
- deps = deps.map(function (dep) {
- return "'" + dep + "'";
- });
- } else {
- deps = [];
- }
- }
-
- finalName = namedModule || moduleName || '';
- if (finalName) {
- finalName = "'" + (namedModule || moduleName) + "',";
- }
-
- return start + namespace + "define(" + finalName +
- (deps ? ('[' + deps.toString() + '],') : '') +
- (namedModule ? namedFuncStart.replace(build.leadingCommaRegExp, '') : suffix);
- });
-
+ return transform.toTransport(namespace, moduleName, path, contents, onFound);
};
return build;
View
218 build/jslib/transform.js
@@ -0,0 +1,218 @@
+/**
+ * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/requirejs for details
+ */
+
+/*jslint */
+
+define(['./esprima', './parse', 'logger'], function (esprima, parse, logger) {
+ 'use strict';
+
+ return {
+ toTransport: function (namespace, moduleName, path, contents, onFound) {
+
+ var defineRanges = [],
+ contentInsertion = '',
+ tokens, info, deps;
+
+ try {
+ tokens = esprima.parse(contents, {
+ tokens: true,
+ range: true
+ }).tokens;
+ } catch(e) {
+ logger.trace('toTransport skipping ' + path + ': ' +
+ e.toString());
+ return;
+ }
+
+ //Find the define calls and their position in the files.
+ tokens.forEach(function (token, i) {
+ var prev, prev2, next, next2, next3, next4,
+ needsId, depAction, foundId;
+
+ if (token.type === 'Identifier' && token.value === 'define') {
+ //Possible match. Do not want something.define calls
+ //though, and only defines follow by a paren
+ prev = tokens[i - 1];
+ next = tokens[i + 1];
+
+ if (prev && prev.type === 'Punctuator' &&
+ prev.value === '.') {
+ //a define on a sub-object, not a top level
+ //define() call. If the sub object is the
+ //namespace, then it is ok.
+ prev2 = tokens[i - 2];
+ if (!prev2) {
+ return;
+ }
+
+ //If the prev2 does not match namespace, then bail.
+ if (!namespace || prev2.type !== 'Identifier' ||
+ prev2.value !== namespace) {
+ return;
+ }
+ }
+
+ if (!next || next.type !== 'Punctuator' ||
+ next.value !== '(') {
+ //Not a define() function call. Bail.
+ return;
+ }
+
+ next2 = tokens[i + 2];
+ if (!next2) {
+ return;
+ }
+
+ //Figure out if this needs a named define call.
+ if (next2.type === 'Punctuator' &&
+ next2.value === '[') {
+ //Dependency array
+ needsId = true;
+ depAction = 'skip';
+ } else if (next2.type === 'Punctuator' &&
+ next2.value === '{') {
+ //Object literal
+ needsId = true;
+ depAction = 'skip';
+ } else if (next2.type === 'Keyword' &&
+ next2.value === 'function') {
+ //function
+ needsId = true;
+ depAction = 'scan';
+ } else if (next2.type === 'String') {
+ //Named module
+ needsId = false;
+
+ //The value includes the quotes around the string,
+ //so remove them.
+ foundId = next2.value.substring(1,
+ next2.value.length - 1);
+
+ //assumed it does not need dependencies injected
+ depAction = 'skip';
+ } else if (next2.type === 'Identifier') {
+ //May be the define(factory); type.
+ next3 = tokens[i + 3];
+ if (!next3) {
+ return;
+ }
+ if (next3.type === 'Punctuator' &&
+ next3.value === ')') {
+ needsId = true;
+ depAction = 'empty';
+ } else {
+ return;
+ }
+ } else if (next2.type === 'Numeric') {
+ //May be the define(12345); type.
+ next3 = tokens[i + 3];
+ if (!next3) {
+ return;
+ }
+ if (next3.type === 'Punctuator' &&
+ next3.value === ')') {
+ needsId = true;
+ depAction = 'skip';
+ } else {
+ return;
+ }
+ } else if (next2.type === 'Punctuator' &&
+ next2.value === '-') {
+ //May be the define(-12345); type.
+ next3 = tokens[i + 3];
+ if (!next3) {
+ return;
+ }
+ if (next3.type === 'Numeric') {
+ next4 = tokens[i + 4];
+ if (!next4) {
+ return;
+ }
+ if (next4.type === 'Punctuator' &&
+ next4.value === ')') {
+ needsId = true;
+ depAction = 'skip';
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ } else {
+ //Not a match, skip it.
+ return;
+ }
+
+ defineRanges.push({
+ foundId: foundId,
+ needsId: needsId,
+ depAction: depAction,
+ defineRange: token.range,
+ parenRange: next.range
+ });
+ }
+ });
+
+ //Only do naming and dependency injection if there is one define
+ //call in the file.
+ if (defineRanges.length > 1) {
+ return contents;
+ }
+ if (!defineRanges.length) {
+ return contents;
+ }
+
+ info = defineRanges[0];
+
+ //Do the modifications "backwards", in other words, start with the
+ //one that is farthest down and work up, so that the ranges in the
+ //defineRanges still apply. So that means deps, id, then namespace.
+
+ if (info.needsId && moduleName) {
+ contentInsertion += "'" + moduleName + "',";
+ }
+
+ if (info.depAction === 'scan') {
+ deps = parse.getAnonDeps(path, contents);
+
+ if (deps.length) {
+ contentInsertion += '[' + deps.map(function (dep) {
+ return "'" + dep + "'";
+ }) + ']';
+ } else {
+ contentInsertion += '[]';
+ }
+ contentInsertion += ',';
+ } else if (info.depAction === 'empty') {
+ contentInsertion += '[],';
+ }
+
+ if (contentInsertion) {
+ contents = contents.substring(0, info.parenRange[1]) +
+ contentInsertion +
+ contents.substring(info.parenRange[1],
+ contents.length);
+ }
+
+ //Do namespace last so that ui does not mess upthe parenRange
+ //used above.
+ if (namespace) {
+ contents = contents.substring(0, info.defineRange[0]) +
+ namespace + '.' +
+ contents.substring(info.defineRange[0],
+ contents.length);
+ }
+
+
+ //Notify any listener for the found info
+ if (onFound) {
+ onFound(info);
+ }
+
+ return contents;
+ }
+ };
+});
View
10 build/tests/buildUtils.js
@@ -20,11 +20,11 @@ define(['build'], function (build) {
modulesWithNames: {}
};
- t.is(bad1, build.toTransport(build.makeAnonDefRegExp(), '', 'bad/1', 'bad1', bad1, layer));
- t.is(bad2, build.toTransport(build.makeAnonDefRegExp(), '', 'bad/2', 'bad2', bad2, layer));
- t.is(bad3, build.toTransport(build.makeAnonDefRegExp(), '', 'bad/3', 'bad3', bad3, layer));
- t.is(bad4, build.toTransport(build.makeAnonDefRegExp(), '', 'bad/4', 'bad4', bad4, layer));
- t.is(goodExpected1, build.toTransport(build.makeAnonDefRegExp(), '', 'good/1', 'good1', good1, layer));
+ t.is(bad1, build.toTransport('', 'bad/1', 'bad1', bad1, layer));
+ t.is(bad2, build.toTransport('', 'bad/2', 'bad2', bad2, layer));
+ t.is(bad3, build.toTransport('', 'bad/3', 'bad3', bad3, layer));
+ t.is(bad4, build.toTransport('', 'bad/4', 'bad4', bad4, layer));
+ t.is(goodExpected1, build.toTransport('', 'good/1', 'good1', good1, layer));
}
]
);
View
2 build/tests/expected/unoExcludeShallow.js
@@ -1,5 +1,5 @@
-define('tres',[],
+define("tres",
function() {
return {
name: "tres"
View
2 build/tests/lib/nameInsertion/expected.js
@@ -1,6 +1,6 @@
-define('foo',[], function () {
+define('foo', function () {
});
View
1 dist.js
@@ -48,6 +48,7 @@ var fs = require('fs'),
'build/jslib/uglifyjs/process.js',
'build/jslib/uglifyjs/index.js',
'build/jslib/parse.js',
+ 'build/jslib/transform.js',
'build/jslib/pragma.js',
'env!env/optimize',
'build/jslib/optimize.js',

0 comments on commit 2f579ab

Please sign in to comment.