Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ build/tests/lib/typescriptConfig/main-built.js
build/tests/lib/umd/main-built.js
build/tests/lib/umd2/built.js
build/tests/lib/umdNested/main-built.js
build/tests/lib/umdTypeScript/main-built.js
build/tests/lib/unicode/main-built.js
build/tests/lib/urlToEmpty/main-built.js
build/tests/lib/wrap/outBothArray.js
Expand Down
45 changes: 37 additions & 8 deletions build/jslib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
node.type === 'ArrowFunctionExpression'));
}

/**
* Detect whether the passed AST uses TypeScript UMD.
*
* @param {Node} astRoot an AST root node.
* @returns {Boolean}
*/
function isTypeScriptUmd(astRoot) {
return astRoot.body.length === 1 &&
astRoot.body[0].type === 'ExpressionStatement' &&
astRoot.body[0].expression.type === 'CallExpression' &&
astRoot.body[0].expression.callee.type === 'FunctionExpression' &&
astRoot.body[0].expression.callee.params.length === 2 &&
astRoot.body[0].expression.callee.params[0].type === 'Identifier' &&
astRoot.body[0].expression.callee.params[0].name === 'deps';
}

/**
* Main parse function. Returns a string of any valid require or
* define/require.def calls as part of one JavaScript source string.
Expand All @@ -130,13 +146,17 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
options = options || {};

//Set up source input
var i, moduleCall, depString,
var i, moduleCall, depString, tsDeps,
moduleDeps = [],
result = '',
moduleList = [],
needsDefine = true,
astRoot = esprima.parse(fileContents);

if (isTypeScriptUmd(astRoot)) {
tsDeps = astRoot.body[0].expression.arguments[0];
}

parse.recurse(astRoot, function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) {
if (!deps) {
deps = [];
Expand Down Expand Up @@ -164,7 +184,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
//If define was found, no need to dive deeper, unless
//the config explicitly wants to dig deeper.
return !!options.findNestedDependencies;
}, options);
}, options, null, tsDeps);

if (options.insertNeedsDefine && needsDefine) {
result += 'require.needsDefine("' + moduleName + '");';
Expand Down Expand Up @@ -214,7 +234,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
* @param {Object} [fnExpScope] holds list of function expresssion
* argument identifiers, set up internally, not passed in
*/
parse.recurse = function (object, onMatch, options, fnExpScope) {
parse.recurse = function (object, onMatch, options, fnExpScope, tsDeps) {
//Like traverse, but skips if branches that would not be processed
//after has application that results in tests of true or false boolean
//literal values.
Expand All @@ -233,13 +253,13 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
object.test.type === 'Literal') {
if (object.test.value) {
//Take the if branch
this.recurse(object.consequent, onMatch, options, fnExpScope);
this.recurse(object.consequent, onMatch, options, fnExpScope, tsDeps);
} else {
//Take the else branch
this.recurse(object.alternate, onMatch, options, fnExpScope);
this.recurse(object.alternate, onMatch, options, fnExpScope, tsDeps);
}
} else {
result = this.parseNode(object, onMatch, fnExpScope);
result = this.parseNode(object, onMatch, fnExpScope, tsDeps);
if (result === false) {
return;
} else if (typeof result === 'string') {
Expand Down Expand Up @@ -276,7 +296,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
if (object.hasOwnProperty(key)) {
child = object[key];
if (typeof child === 'object' && child !== null) {
result = this.recurse(child, onMatch, options, fnExpScope);
result = this.recurse(child, onMatch, options, fnExpScope, tsDeps);
if (typeof result === 'string') {
break;
}
Expand Down Expand Up @@ -813,7 +833,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
* @returns {String} a JS source string with the valid require/define call.
* Otherwise null.
*/
parse.parseNode = function (node, onMatch, fnExpScope) {
parse.parseNode = function (node, onMatch, fnExpScope, tsDeps) {
var name, deps, cjsDeps, arg, factory, exp, refsDefine, bodyNode,
args = node && node[argPropName],
callName = parse.hasRequire(node),
Expand Down Expand Up @@ -856,6 +876,15 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
isUmd = true;
factory = name;
name = null;
} else if (tsDeps && name.type === 'Identifier' &&
name.name === 'deps' && args.length === 2 &&
hasProp(fnExpScope, name.name)) {
//define(deps, ...)
//TypeScript UMD
isUmd = true;
factory = deps;
deps = tsDeps;
name = null;
} else if (name.type !== 'Literal') {
//An object literal, just null out
name = deps = factory = null;
Expand Down
6 changes: 6 additions & 0 deletions build/jslib/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ function (esprima, parse, logger, lang) {
//define([], ...);
needsId = true;
depAction = 'skip';
} else if (firstArg.type === 'Identifier' &&
firstArg.name === 'deps') {
//define(deps, ...);
//TypeScript UMD
needsId = true;
depAction = 'skip';
} else if (firstArg.type === 'Literal' &&
typeof firstArg.value === 'string') {
//define('string', ....)
Expand Down
18 changes: 18 additions & 0 deletions build/tests/builds.js
Original file line number Diff line number Diff line change
Expand Up @@ -2451,6 +2451,24 @@ define(['build', 'env!env/file', 'env', 'lang'], function (build, file, env, lan
);
doh.run();

//Detect TypeScript style UMD wrappers
doh.register("umdTypeScript",
[
function umdNested(t) {
file.deleteFile("lib/umdTypeScript/main-built.js");

build(["lib/umdTypeScript/build.js"]);

t.is(nol(c("lib/umdTypeScript/expected.js")),
nol(c("lib/umdTypeScript/main-built.js")));

require._buildReset();
}

]
);
doh.run();

//Keep parsing if a define uses an identifier, but is not part of
//a function wrapper for UMD
//https://github.com/jrburke/requirejs/issues/1133
Expand Down
6 changes: 6 additions & 0 deletions build/tests/lib/umdTypeScript/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
baseUrl: '.',
name: 'main',
out: 'main-built.js',
optimize: 'none'
}
22 changes: 22 additions & 0 deletions build/tests/lib/umdTypeScript/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(function (deps, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define('lib',deps, factory);
}
})(['require', 'exports'], function (require, exports) {
console.log('');
});


(function (deps, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define('main',deps, factory);
}
})(['require', 'exports', 'lib'], function (require, exports) {
var lib = require('lib');
});
10 changes: 10 additions & 0 deletions build/tests/lib/umdTypeScript/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(function (deps, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define(deps, factory);
}
})(['require', 'exports'], function (require, exports) {
console.log('');
});
10 changes: 10 additions & 0 deletions build/tests/lib/umdTypeScript/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(function (deps, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define(deps, factory);
}
})(['require', 'exports', 'lib'], function (require, exports) {
var lib = require('lib');
});