Skip to content

Commit 2f643c0

Browse files
Joscha RohmannKanaye
Joscha Rohmann
authored andcommitted
WIP: Changed build process to generate self executing closures arround modules.
TODO: Fix modules not returning their export. TODO: Test. Fixes #153.
1 parent b324a83 commit 2f643c0

File tree

1 file changed

+84
-8
lines changed

1 file changed

+84
-8
lines changed

build/tasks/build.js

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
module.exports = function (grunt) {
2-
var blocks = require('blocks');
2+
var esprima = require('esprima');
3+
var escodegen = require('escodegen');
4+
var estrvarse = require('estraverse');
5+
var definedModuleNames = {};
36
var requirejsConfig = {};
47
var requirejsOptions = {
58
baseUrl: 'src',
@@ -8,7 +11,7 @@ module.exports = function (grunt) {
811
optimize: 'none',
912
skipSemiColonInsertion: true,
1013
onBuildWrite: function (name, path, contents) {
11-
var rdefineEnd = /\}\);[^}\w]*$/;
14+
var rdefineEnd = /\}\);[^}\w]*$/;
1215

1316
if (/.\/var\//.test(path)) {
1417
contents = contents
@@ -17,6 +20,46 @@ module.exports = function (grunt) {
1720

1821
} else {
1922
contents = contents
23+
.replace(/\/\*\s*ExcludeStart\s*\*\/[\w\W]*?\/\*\s*ExcludeEnd\s*\*\//ig, '')
24+
.replace(/\/\/\s*BuildExclude\n\r?[\w\W]*?\n\r?/ig, '');
25+
var ast = esprima.parse(contents, {
26+
tokens: true,
27+
comment: true,
28+
range: true
29+
});
30+
31+
estrvarse.attachComments(ast, ast.comments, ast.tokens);
32+
33+
if (ast.body[0].expression.callee.name == 'define') {
34+
var moduleExpression = findModuleExpressionArgument(ast.body[0].expression.arguments);
35+
if (!moduleExpression || !moduleExpression.body.body[0] || moduleExpression.body.body[0].type == 'ReturnStatement') {
36+
// Null out empty define statements e.g. define(['./query/ready', '...'])
37+
// and expresions without an expression or only an return statement e.g. define([], function () { return blocks; })
38+
contents = '';
39+
} else {
40+
var moduleName;
41+
try {
42+
moduleName = findModuleExportIdentifier(moduleExpression.body.body) || /\/(\w+).?j?s?$/.exec(name)[1];
43+
} catch(e) {}
44+
if (moduleName && definedModuleNames[moduleName] && definedModuleNames[moduleName] != path) {
45+
grunt.fail.warn('[NamingConflict]: Module ' + path + ' tried to define ' + moduleName + ' which is already defined by ' + definedModuleNames[moduleName] + ' !');
46+
} else if (moduleName){
47+
definedModuleNames[moduleName] = path;
48+
}
49+
ast = wrapModuleAst(moduleExpression, moduleName);
50+
contents = escodegen.generate(ast, {
51+
format: {
52+
indent: {
53+
style: ' ',
54+
base: 0,
55+
adjustMultilineComment: true
56+
}
57+
},
58+
comment: true
59+
});
60+
}
61+
}
62+
/* contents = contents
2063
.replace(/\s*return\s+[^\}]+(\}\);[^\w\}]*)$/, '$1')
2164
// Multiple exports
2265
.replace(/\s*exports\.\w+\s*=\s*\w+;/g, '');
@@ -29,19 +72,52 @@ module.exports = function (grunt) {
2972
// Remove anything wrapped with
3073
// /* ExcludeStart */ /* ExcludeEnd */
3174
// or a single line directly after a // BuildExclude comment
32-
contents = contents
33-
.replace(/\/\*\s*ExcludeStart\s*\*\/[\w\W]*?\/\*\s*ExcludeEnd\s*\*\//ig, '')
34-
.replace(/\/\/\s*BuildExclude\n\r?[\w\W]*?\n\r?/ig, '');
3575

3676
// Remove empty definitions
37-
contents = contents
38-
.replace(/define\(\[[^\]]+\]\)[\W\n]+$/, '');
77+
/* contents = contents
78+
.replace(/define\(\[[^\]]+\]\)[\W\n]+$/, '');*/
79+
3980
}
4081

4182
return contents;
4283
}
4384
};
4485

86+
function findModuleExportIdentifier (module) {
87+
for (var i = module.length -1 ; i >= 0; i--) {
88+
var expression = module[i];
89+
if (expression.type == 'ReturnStatement') {
90+
return expression.argument.name;
91+
}
92+
}
93+
throw new Error('No return statement');
94+
}
95+
96+
function findModuleExpressionArgument(args) {
97+
for (var i in args) {
98+
var arg = args[i];
99+
if (arg.type == 'FunctionExpression') {
100+
return arg;
101+
}
102+
}
103+
}
104+
105+
106+
function wrapModuleAst (node, exportName) {
107+
var wrapedModule;
108+
var bodyNode;
109+
if (exportName) {
110+
wrapedModule = esprima.parse('var ' + exportName + ' = (function () { })();');
111+
bodyNode = wrapedModule.body[0].declarations[0].init.callee.body;
112+
} else {
113+
wrapedModule = esprima.parse('(function () { })();');
114+
bodyNode = wrapedModule.body[0].expression.callee.body;
115+
}
116+
// insert body of the original "define"-function to the
117+
bodyNode.body = node.body.body;
118+
return wrapedModule;
119+
}
120+
45121
var names = ['query', 'mvc', 'node'];
46122
names.forEach(function (name) {
47123
grunt.config.set('name', name);
@@ -54,7 +130,7 @@ module.exports = function (grunt) {
54130
grunt.registerTask('build', function () {
55131
var tasks = [];
56132
for (var i = 0; i < arguments.length; i++) {
57-
tasks.push('requirejs:' + arguments[i])
133+
tasks.push('requirejs:' + arguments[i]);
58134
}
59135
grunt.task.run(tasks.length ? tasks : 'requirejs');
60136
});

0 commit comments

Comments
 (0)