Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add translations, and tweak the esprimaing.

  • Loading branch information...
commit 087d9fbceea506ee7c96f67bd7785d6d671d9097 1 parent 5985089
Kenneth Kufluk authored
View
4 lib/loadbuilder/analyzer.js
@@ -1,5 +1,5 @@
var parser = require('esprima'),
- File = require('./file');
+ file = require('./file');
function match(fragment, tree) {
var matches = [];
@@ -54,7 +54,7 @@ function walk(matcher, tree, parent, index) {
function analyze(matcher, source) {
// treeish can be source string, file object (from asset.js) or ast
var tree;
- if (source instanceof File) {
+ if (file.isFile(source)) {
tree = source.ast();
} else if (typeof source == 'object') {
tree = source;
View
67 lib/loadbuilder/asset.js
@@ -1,18 +1,9 @@
var util = require('./util'),
analyzer = require('./analyzer'),
- fs = require('fs'),
path = require('path'),
jshint = require('jshint').JSHINT,
- File = require('./file');
+ file = require('./file');
-var USING = {
- type: "CallExpression",
- callee: {
- type: "Identifier",
- name: "using"
- },
- arguments: null
-};
var ARRAY_ARG = {
type: "ArrayExpression",
elements: null
@@ -21,22 +12,6 @@ var STRING_ARG = {
type: "Literal",
value: null
};
-var PROVIDE = {
- type: "CallExpression",
- callee: {
- type: "Identifier",
- name: "provide"
- },
- arguments: null
-};
-var REQUIRE = {
- type: "CallExpression",
- callee: {
- type: "Identifier",
- name: "require"
- },
- arguments: null
-};
var dependencyCache = {};
var dependencyCacheLastUpdate = {};
@@ -47,10 +22,9 @@ function Script(id) {
util.extend(Script.prototype, {
dependencies: function() {
-
var usings, dependencies = [];
- usings = analyzer.analyze(USING, this.fromFile());
+ usings = this.fromFile().usings();
usings.forEach(function(call) {
var args = call.values[0];
@@ -75,7 +49,6 @@ util.extend(Script.prototype, {
}
}, this);
}, this);
-
return dependencies;
},
lint: function(options) {
@@ -87,12 +60,33 @@ util.extend(Script.prototype, {
process.exit(1);
}
},
+ ast: function() {
+ return this.fromFile().ast();
+ },
+ addTranslationMarkers: function(fnName) {
+ var UNDERSCORE = {
+ type: "CallExpression",
+ callee: {
+ type: "Identifier",
+ name: fnName
+ },
+ arguments: null
+ },
+ tree = this.ast(),
+ underscores = analyzer.analyze(UNDERSCORE, tree);
+ // replace the underscore functions
+ underscores.forEach(function(underscore) {
+ var string = underscore.values[0];
+ string[0].value = '____i18n____' + string[0].value + '____/i18n____';
+ this.fromFile().taintAst();
+ }, this);
+ },
toSource: function() {
- return this.deferWrapper(this.fromFile().source);
+ return this.deferWrapper(this.fromFile().generate());
},
fromFile: function() {
if (!this.file) {
- this.file = new File(this.fullPath(), this.builder.preProcessor);
+ this.file = file(this.fullPath(), this.builder.preProcessor);
}
this.file.load();
return this.file;
@@ -137,7 +131,7 @@ util.extend(Module.prototype, {
}
},
isCJS: function() {
- return !analyzer.analyze(PROVIDE, this.fromFile()).length;
+ return !this.fromFile().provides().length;
},
amdWrappedSource: function() {
var deps = ['module', 'require', 'exports'].concat(this.dependencies().map(function(d) { return d.id; })),
@@ -145,7 +139,7 @@ util.extend(Module.prototype, {
JSON.stringify(deps) + ",function(module, require, exports) {\n",
postamble = "\n});"
- return preamble + this.fromFile().source + postamble;
+ return preamble + this.fromFile().generate() + postamble;
},
dependencies: function() {
@@ -156,7 +150,7 @@ util.extend(Module.prototype, {
}
},
dependenciesFromRequire: function() {
- var requires = analyzer.analyze(REQUIRE, this.fromFile());
+ var requires = this.fromFile().requires();
return requires.map(function(r) {
var asset = this.builder.matchAsset(r.values[0][0].value, false);
@@ -164,8 +158,8 @@ util.extend(Module.prototype, {
}, this).filter(function(item){ return item!=null; });
},
addId: function() {
- var provides = analyzer.analyze(PROVIDE, this.fromFile()),
- tree = this.fromFile().ast(),
+ var provides = this.fromFile().provides(),
+ tree = this.ast(),
provide;
if (provides.length > 0) {
@@ -177,6 +171,7 @@ util.extend(Module.prototype, {
type: "Literal",
value: this.id
});
+ this.fromFile().taintAst();
}
}
return this.fromFile().generate();
View
69 lib/loadbuilder/builder.js
@@ -2,15 +2,23 @@ var util = require('./util'),
asset = require('./asset'),
path = require('path'),
fs = require('fs'),
- uglify = require("uglify-js");
+ uglify = require("uglify-js"),
+ crypto = require('crypto'),
+ mkdirp = require('mkdirp');
+
+function hashString(string) {
+ var md5sum = crypto.createHash('md5');
+ md5sum.update(string);
+ return md5sum.digest('hex');
+}
function collect(excluded, assets, includeDependencies) {
var collected = [];
assets.forEach(function(asset) {
if(!asset) {
- console.warn('Undefined asset in assets list');
- console.log(new Error().stack);
+ this.warn('Undefined asset in assets list');
+ this.log(new Error().stack);
return;
}
var deps = [];
@@ -24,7 +32,7 @@ function collect(excluded, assets, includeDependencies) {
collect(excluded, deps, includeDependencies)
).concat(asset);
}
- });
+ }, this);
return collected;
}
@@ -113,7 +121,6 @@ util.extend(Builder.prototype, {
},
mapAssets: function(assets) {
var mapped = [];
-
for (var i=0, asset; asset = assets[i]; i++) {
if (typeof asset == 'string') {
asset = this.matchAsset(asset);
@@ -121,7 +128,6 @@ util.extend(Builder.prototype, {
mapped.push(asset);
}
-
return mapped;
},
lint: function(options) {
@@ -155,6 +161,9 @@ util.extend(Builder.prototype, {
toSource: function() {
var source = this.collectedAssets().map(function(a) {
this.log('* ' + a.id);
+ if (this.i18nFn) {
+ a.addTranslationMarkers(this.i18nFn);
+ }
return a.toSource();
}, this).join('\n');
@@ -190,6 +199,54 @@ util.extend(Builder.prototype, {
},
collectedAssets: function() {
return dedupe(collect(this.excludes, this.assets, this.options.includeDependencies));
+ },
+ publishi18n: function(outputName, translations, cb){
+ var bundle = this;
+ // Create the output folder if required
+ mkdirp(path.dirname(outputName), function() {
+ // Collect the bundle dependencies and build
+ this.addI18nMarkers('_').minify({ except: ['_', '$'] }).translate(translations).writei18n(outputName, cb);
+ }.bind(this));
+ },
+ addI18nMarkers: function(underscore) {
+ // replace _() strings with something that's easy to regex for
+ this.i18nFn = underscore; // name of i18n function
+ return this;
+ },
+ translate: function(translations) {
+ this.translations = translations;
+ return this;
+ },
+ writei18n: function(fileName, success) {
+ var manifest = this.manifest(),
+ res = {},
+ source = this.toSource();
+
+ res[path] = manifest;
+
+ if (!this.i18nFn || !this.translations) {
+ return this.log('did not translate - check i18n function name and translation table');
+ }
+
+ Object.keys(this.translations).forEach(function(langCode) {
+ var path = fileName.replace('<lang>', langCode);
+ var langSrc = source.replace(/____i18n____(.*?)____\/i18n____/g, function(fullMatch, subMatch) {
+ if (this.translations[langCode].hasOwnProperty(subMatch)) {
+ return this.translations[langCode][subMatch];
+ }
+ // revert to English if we have no string
+ return subMatch;
+ }.bind(this));
+ var hash = hashString(langSrc);
+ path = path.replace('<hash>', hash);
+ fs.writeFileSync(path, langSrc, 'utf8');
+ }, this);
+
+ success(res);
+
+ this.log('> ' + path);
+
+ return this;
}
});
View
87 lib/loadbuilder/file.js
@@ -3,9 +3,39 @@ var util = require('./util'),
esprima = require('esprima'),
escodegen= require('escodegen');
+var USING = {
+ type: "CallExpression",
+ callee: {
+ type: "Identifier",
+ name: "using"
+ },
+ arguments: null
+};
+var REQUIRE = {
+ type: "CallExpression",
+ callee: {
+ type: "Identifier",
+ name: "require"
+ },
+ arguments: null
+};
+var PROVIDE = {
+ type: "CallExpression",
+ callee: {
+ type: "Identifier",
+ name: "provide"
+ },
+ arguments: null
+};
+
+var lbAnalyzer; // solution for circular ref
+
+var files = {};
+
function File(fullPath, preProcessor) {
this.fullPath = fullPath;
this.preProcessor = preProcessor;
+ this.derived = {};
this.load();
}
@@ -16,32 +46,69 @@ util.extend(File.prototype, {
if (!this.source || this.mtime != mtime) {
this.mtime = mtime;
this.source = this.preProcess(fs.readFileSync(this.fullPath, 'utf8'));
- this.esprimaAst = null;
+ this.taint();
}
},
+ taint: function() {
+ this.derived = {};
+ },
+ taintAst: function() {
+ this.derived.generated = null;
+ },
preProcess: function(data) {
return this.preProcessor ? this.preProcessor(data) : data;
},
ast: function() {
- if (!this.esprimaAst) {
- this.esprimaAst = esprima.parse(this.source, {
+ if (!this.derived.esprimaAst) {
+ var ast = esprima.parse(this.source, {
range: true,
tokens: true,
comment: true
});
- this.esprimaAst = escodegen.attachComments(this.esprimaAst, this.esprimaAst.comments, this.esprimaAst.tokens);
+ ast = escodegen.attachComments(ast, ast.comments, ast.tokens);
+ this.derived.esprimaAst = ast;
}
- return this.esprimaAst
+ return this.derived.esprimaAst;
},
generate: function(options) {
- options = options || {};
- var tree = this.ast();
- return escodegen.generate(tree, util.extend({ comment: true }, options));
+ if (this.derived.generated) {
+ options = options || {};
+ var tree = this.ast();
+ this.source = this.derived.generated = escodegen.generate(tree, util.extend({ comment: true }, options));
+ }
+ return this.derived.generated;
+ },
+ usings: function() {
+ if (!this.derived.usings) {
+ this.derived.usings = this.analyzer().analyze(USING, this.ast());
+ }
+ return this.derived.usings;
+ },
+ requires: function() {
+ if (!this.derived.requires) {
+ this.derived.requires = this.analyzer().analyze(REQUIRE, this.ast());
+ }
+ return this.derived.requires;
+ },
+ provides: function() {
+ if (!this.derived.provides) {
+ this.derived.provides = this.analyzer().analyze(PROVIDE, this.ast());
+ }
+ return this.derived.provides;
+ },
+ analyzer: function() {
+ if (!lbAnalyzer) lbAnalyzer = require('./analyzer');
+ return lbAnalyzer;
}
});
function file(fullPath, preProcessor) {
- return new File(fullPath, preProcessor);
+ if (files[fullPath]) return files[fullPath];
+ return files[fullPath] = new File(fullPath, preProcessor);
+}
+
+file.isFile = function(obj) {
+ return obj instanceof File;
}
-module.exports = File;
+module.exports = file;
View
3  package.json
@@ -14,7 +14,8 @@
"opts": "1.2.2",
"uglify-js": "git://github.com/kennethkufluk/UglifyJS.git#loadbuilder",
"colors": "0.6.0-1",
- "glob": "3.1.9"
+ "glob": "3.1.9",
+ "mkdirp": "0.3.0"
},
"devDependencies": {
"expresso": ">=0.9.2"
Please sign in to comment.
Something went wrong with that request. Please try again.