foam-framework
Version:
MVC metaprogramming framework
292 lines (282 loc) • 9.46 kB
JavaScript
/**
* @license
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CLASS({
package: 'foam.build',
name: 'I18nApp',
requires: [
'foam.dao.NodeFileDAO as FileDAO',
'foam.dao.File',
'foam.core.dao.OrDAO',
'node.dao.ModelFileDAO',
'foam.i18n.GlobalController',
'foam.i18n.IdGenerator',
'foam.i18n.MessageGenerator'
],
imports: [ 'error' ],
properties: [
{
model_: 'StringProperty',
name: 'appDefinition',
required: true
},
{
model_: 'StringProperty',
name: 'targetPath',
help: multiline(function() {/* Directory to write output files to. Will
be created if it doesn't exist. When outputFormat=foamData, this is
treated as the classpath where data file should be put. For example,
if targetPath='/foo', and dataId='bar.baz.Messages', then data will be
stored in /foo/bar/baz/Messages.js. */}),
required: true
},
{
model_: 'StringArrayProperty',
name: 'extraFiles',
help: 'Extra files to both load before loading models for i18n.',
adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
},
{
model_: 'StringArrayProperty',
name: 'extraClassPaths',
help: 'List of extra .js hierarchies to load models from. Paths will be checked in the order given, finally falling back to the main FOAM js/ hierarchy.',
adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
},
{
model_: 'StringProperty',
name: 'extractorModel',
defaultValue: 'foam.i18n.MessagesExtractor'
},
{
model_: 'StringProperty',
name: 'messageGeneratorModel',
defaultValue: 'foam.i18n.MessageGenerator'
},
{
model_: 'StringProperty',
name: 'placeholderModel',
defaultValue: 'foam.i18n.Placeholder'
},
{
model_: 'StringProperty',
name: 'messageModel',
defaultValue: 'foam.i18n.Message'
},
{
model_: 'StringProperty',
name: 'messageBundleModel',
defaultValue: 'foam.i18n.MessageBundle'
},
{
model_: 'foam.core.types.StringEnumProperty',
name: 'outputFormat',
choices: [
['chrome', 'Chrome messages.json'],
['foamData', 'FOAM __DATA(MessageBundle JSON)'],
['foamJSON', 'FOAM MessageBundle messages.json']
],
defaultValue: 'chrome'
},
{
model_: 'StringProperty',
name: 'dataId',
help: 'Only applies to outputFormat=foamData. Package-qualified ID for data file.',
defaultValue: 'foam.i18n.messages.Messages'
},
{
name: 'path',
factory: function() { return require('path'); }
},
{
name: 'fileDAO',
factory: function() { return this.FileDAO.create(); }
},
{
name: 'i18nController'
},
{
name: 'visitedModels_',
lazyFactory: function() { return {}; }
},
{
model_: 'IntProperty',
name: 'pending_',
defaultValue: 0,
postSet: function(old, nu) {
if ( old === nu ) return;
if ( nu === 0 ) {
if ( this.outputFormat === 'foamData' ) {
this.outputFoamData_();
} else if ( this.outputFormat === 'foamJSON' ) {
this.outputFoamJSON_();
} else if ( this.outputFormat === 'chrome' ) {
this.outputChromeMessages_();
} else {
this.outputFormat_(this.outputFormat);
}
}
}
}
],
methods: {
execute: function() {
var i;
for ( i = 0; i < this.extraFiles.length; i++ ) {
require(this.extraFiles[i]);
}
for ( i = 0; i < this.extraClassPaths.length ; i++ ) {
this.X.ModelDAO = this.OrDAO.create({
delegate: this.ModelFileDAO.create({
classpath: this.extraClassPaths[i]
}),
primary: this.X.ModelDAO
});
}
apar(this.X.arequire(this.extractorModel),
this.X.arequire(this.messageGeneratorModel),
this.X.arequire(this.placeholderModel),
this.X.arequire(this.messageModel),
this.X.arequire(this.messageBundleModel))(
function(Extractor, MessageGenerator, Placeholder, Message,
MessageBundle) {
var idGenerator = this.IdGenerator.create();
this.i18nController = this.GlobalController.create({
idGenerator: idGenerator,
extractor: Extractor.create({
idGenerator: idGenerator,
messageGenerator: MessageGenerator.create({
idGenerator: idGenerator,
placeholderFactory: Placeholder.create.bind(Placeholder),
messageFactory: Message.create.bind(Message)
}),
messageBundleFactory: MessageBundle.create.bind(MessageBundle)
})
});
this.execute_();
}.bind(this));
},
execute_: function() {
var self = this;
self.X.ModelDAO.find(
self.appDefinition,
{
put: function(app) {
if ( ! (app.defaultView || app.controller) ) {
self.error('ERROR: App definition has neither a default view ' +
'nor a controller');
}
var models = app.extraModels.slice(0);
if ( app.defaultView ) models.push(app.defaultView);
if ( app.controller ) models.push(app.controller);
// Manually manage pending_ count for top-level async calls.
this.pending_ = models.length;
models.forEach(function(modelId) {
return this.X.arequire(modelId)(self.visitModel_.bind(self));
});
}.bind(this),
error: function() {
self.error('ERROR: Failed to load app definition from: ' +
self.appDefinition);
}
});
},
visitModel_: function(model) {
return this.i18nController.extractor.avisitModel(model)(
this.visitModel__.bind(this));
},
visitModel__: function(model) {
this.visitedModels_[model.id] = true;
var deps = model.getAllRequires();
for ( var i = 0; i < deps.length; ++i ) {
if ( ! this.visitedModels_[deps[i]] ) {
this.arequire_(deps[i]);
}
}
--this.pending_;
},
arequire_: function(modelId) {
++this.pending_;
this.X.arequire(modelId)(this.visitModel_.bind(this));
},
outputFoamData_: function() {
var self = this;
this.i18nController.extractor.amessagesFile(self.dataId, function(str) {
var filePath = self.dataId.replace(/[.]/g, self.path.sep) + '.js';
var file = self.File.create({
path: self.targetPath + self.path.sep + filePath,
contents: str
});
self.fileDAO.put(file, {
put: function() { process.exit(0); },
error: function() {
self.error('ERROR writing file: ', file.path);
process.exit(1);
}
});
});
},
outputFoamJSON_: function() {
var self = this;
this.i18nController.extractor.amessages(function(data) {
var str = JSONUtil.compact.where(NOT_TRANSIENT).stringify(data);
var file = self.File.create({
path: self.targetPath + self.path.sep + 'messages.json',
contents: str
});
self.fileDAO.put(file, {
put: function() { process.exit(0); },
error: function() {
self.error('ERROR writing file: ', file.path);
process.exit(1);
}
});
});
},
outputChromeMessages_: function() {
var self = this;
this.i18nController.extractor.achromeMessages(function(data) {
var str = JSON.stringify(data);
var file = self.File.create({
path: self.targetPath + self.path.sep + 'messages.json',
contents: str
});
self.fileDAO.put(file, {
put: function() { process.exit(0); },
error: function() {
self.error('ERROR writing file: ', file.path);
process.exit(1);
}
});
});
},
outputFormat_: function(format) {
var self = this;
this.i18nController.extractor.ai18n(format, function(data) {
var file = self.File.create({
path: self.targetPath + self.path.sep + data.fileName,
contents: data.str
});
self.fileDAO.put(file, {
put: function() { process.exit(0); },
error: function() {
self.error('ERROR writing file: ', file.path);
process.exit(1);
}
});
});
}
}
});