Changes to make the compiler work on the client-side when using a module bundler

This commit is contained in:
Patrick Steele-Idem 2014-10-22 11:11:54 -06:00
parent 2ad817a20d
commit 294cd24bd3
20 changed files with 292 additions and 222 deletions

View File

@ -29,8 +29,8 @@ var _Node = require('./Node');
var ElementNode = require('./ElementNode');
var TextNode = require('./TextNode');
var TagHandlerNode = require('../taglibs/core/TagHandlerNode');
var raptorModulesResolver = require('raptor-modules/resolver');
var fs = require('fs');
var deresolve = require('./util/deresolve');
var upToDate = require('./up-to-date');
function TemplateCompiler(path, options) {
this.dirname = nodePath.dirname(path);
@ -202,10 +202,6 @@ TemplateCompiler.prototype = {
}
throw createError(new Error('Node class not found for tag "' + tagName + '"'));
},
createTag: function () {
var Taglib = require('./Taglib');
return new Taglib.Tag();
},
inheritNode: function(Ctor) {
if (!Ctor.prototype.__NODE) {
@ -241,28 +237,7 @@ TemplateCompiler.prototype = {
},
getLastModified: function() {
var sourceFile = this.path;
var statSource = fs.statSync(sourceFile);
var lastModifiedTime = statSource.mtime.getTime();
var taglibFiles = this.taglibs.getInputFiles();
var len = taglibFiles.length;
for (var i=0; i<len; i++) {
var taglibFileStat;
var taglibFile = taglibFiles[i];
try {
taglibFileStat = fs.statSync(taglibFile);
} catch(e) {
continue;
}
lastModifiedTime = Math.max(lastModifiedTime, taglibFileStat.mtime.getTime());
}
return lastModifiedTime;
return upToDate.getLastModified(this.path, this.taglibs);
},
checkUpToDate: function(targetFile) {
@ -270,46 +245,11 @@ TemplateCompiler.prototype = {
return false;
}
var sourceFile = this.path;
var statTarget;
try {
statTarget = fs.statSync(targetFile);
} catch(e) {
return false;
}
var statSource = fs.statSync(sourceFile);
if (statSource.mtime.getTime() > statTarget.mtime.getTime()) {
return false;
}
// Now check if any of the taglib files have been modified after the target file was generated
var taglibFiles = this.taglibs.getInputFiles();
var len = taglibFiles.length;
for (var i=0; i<len; i++) {
var taglibFileStat;
var taglibFile = taglibFiles[i];
try {
taglibFileStat = fs.statSync(taglibFile);
} catch(e) {
continue;
}
if (taglibFileStat.mtime.getTime() > statTarget.mtime.getTime()) {
return false;
}
}
return true;
return upToDate.checkUpToDate(targetFile, this.path, this.taglibs);
},
getRequirePath: function(targetModuleFile) {
return raptorModulesResolver.deresolve(targetModuleFile, this.dirname);
return deresolve(targetModuleFile, this.dirname);
}
};
module.exports = TemplateCompiler;

View File

@ -14,8 +14,8 @@
* limitations under the License.
*/
'use strict';
var extend = require('raptor-util').extend;
var fs = require('fs');
var extend = require('raptor-util/extend');
var req = require; // Fool code inspectors used by client-side bundles
var nodePath = require('path');
var defaultOptions = {
@ -64,6 +64,8 @@ extend(exports, {
},
compileFile: function(path, options, callback) {
var fs = req('fs');
if (typeof options === 'function') {
callback = options;
options = null;

5
compiler/optimizer.json Normal file
View File

@ -0,0 +1,5 @@
{
"dependencies": [
"../taglibs/optimizer.json"
]
}

View File

@ -1,3 +1,6 @@
{
"main": "./marko-compiler.js"
"main": "./marko-compiler.js",
"browser": {
"./up-to-date.js": "./up-to-date-browser.js"
}
}

View File

@ -26,7 +26,7 @@ function merge(target, source) {
var targetArray = target[k];
var sourceArray = source[k];
if (!Array.isArray(targetArray)) {
targetArray = [targetArray];
@ -44,7 +44,7 @@ function merge(target, source) {
merge(newTarget, source[k]);
target[k] = newTarget;
}
} else {
target[k] = source[k];
}
@ -175,7 +175,7 @@ TaglibLookup.prototype = {
*/
var transformers = [];
function addTransformer(transformer) {
if (!transformer || !transformer.getFunc) {
throw createError(new Error('Invalid transformer'));
@ -190,12 +190,14 @@ TaglibLookup.prototype = {
* Start with the least specific and end with the most specific.
*/
if (this.merged.tags[tagKey]) {
this.merged.tags[tagKey].forEachTransformer(addTransformer);
}
if (this.merged.tags) {
if (this.merged.tags[tagKey]) {
this.merged.tags[tagKey].forEachTransformer(addTransformer);
}
if (this.merged.tags['*']) {
this.merged.tags['*'].forEachTransformer(addTransformer);
if (this.merged.tags['*']) {
this.merged.tags['*'].forEachTransformer(addTransformer);
}
}
transformers.sort(transformerComparator);
@ -203,8 +205,10 @@ TaglibLookup.prototype = {
transformers.forEach(callback, thisObj);
},
forEachTextTransformer: function (callback, thisObj) {
this.merged.textTransformers.sort(transformerComparator);
this.merged.textTransformers.forEach(callback, thisObj);
if (this.merged.textTransformers) {
this.merged.textTransformers.sort(transformerComparator);
this.merged.textTransformers.forEach(callback, thisObj);
}
},
getInputFiles: function() {
if (!this._inputFiles) {

View File

@ -0,0 +1,5 @@
{
"browser": {
"./taglib-finder.js": "./taglib-finder-browser.js"
}
}

View File

@ -0,0 +1,10 @@
function find(dirname, registeredTaglibs) {
return registeredTaglibs || [];
}
function excludeDir(dirname) {
// no-op
}
exports.find = find;
exports.excludeDir = excludeDir;

View File

@ -0,0 +1,103 @@
var taglibLoader = require('./taglib-loader');
var excludedDirs = {};
var nodePath = require('path');
var fs = require('fs');
var existsCache = {};
var findCache = {};
var taglibsByPath = {};
function existsCached(path) {
var exists = existsCache[path];
if (exists === undefined) {
exists = fs.existsSync(path);
existsCache = exists;
}
return exists;
}
function tryDir(dirname, found) {
var taglibPath = nodePath.join(dirname, 'marko-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
found.push(taglib);
}
}
function tryNodeModules(parent, found) {
if (nodePath.basename(parent) === 'node_modules') {
return;
}
var nodeModulesDir = nodePath.join(parent, 'node_modules');
var taglibs = taglibsByPath[nodeModulesDir];
if (taglibs !== undefined) {
if (taglibs !== null) {
for (var i = 0, len = taglibs.length; i < len; i++) {
found.push(taglibs[i]);
}
}
return;
}
if (existsCached(nodeModulesDir)) {
taglibs = [];
var children = fs.readdirSync(nodeModulesDir);
children.forEach(function(moduleDirBasename) {
var moduleDir = nodePath.join(nodeModulesDir, moduleDirBasename);
var taglibPath = nodePath.join(moduleDir, 'marko-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
taglib.moduleName = moduleDirBasename;
taglibs.push(taglib);
found.push(taglib);
}
});
taglibsByPath[nodeModulesDir] = taglibs.length ? taglibs : null;
} else {
taglibsByPath[nodeModulesDir] = null;
}
}
function findHelper(dirname, found) {
if (!excludedDirs[dirname]) {
tryDir(dirname, found);
tryNodeModules(dirname, found);
}
var parent = nodePath.dirname(dirname);
if (parent && parent !== dirname) {
// TODO: Don't use recursion (there's a simpler way)
findHelper(parent, found);
}
}
function find(dirname, registeredTaglibs) {
var found = findCache[dirname];
if (found) {
return found;
}
found = [];
findHelper(dirname, found);
found = found.concat(registeredTaglibs);
findCache[dirname] = found;
return found;
}
function excludeDir(dirname) {
excludedDirs[dirname] = true;
}
exports.find = find;
exports.excludeDir = excludeDir;

View File

@ -1,4 +1,13 @@
var fs = require('fs');
var fs ;
var req = require;
try {
fs = req('fs');
} catch(e) {
}
var ok = require('assert').ok;
var nodePath = require('path');
var Taglib = require('./Taglib');
@ -6,9 +15,18 @@ var cache = {};
var forEachEntry = require('raptor-util').forEachEntry;
var raptorRegexp = require('raptor-regexp');
var tagDefFromCode = require('./tag-def-from-code');
var resolve = require('raptor-modules/resolver').serverResolveRequire;
var resolve = require('../util/resolve'); // NOTE: different implementation for browser
var propertyHandlers = require('property-handlers');
function exists(path) {
try {
require.resolve(path);
return true;
} catch(e) {
return false;
}
}
function createDefaultTagDef() {
return {
attributes: {
@ -52,7 +70,7 @@ function buildAttribute(attr, attrProps, path) {
attr.removeDashes = value === true;
},
description: function() {
}
}, path);
@ -101,15 +119,12 @@ function buildTag(tagObject, path, taglib, dirname) {
renderer: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Renderer at path "' + path + '" does not exist.');
}
tag.renderer = path;
},
template: function(value) {
var path = nodePath.resolve(dirname, value);
if (!fs.existsSync(path)) {
if (!exists(path)) {
throw new Error('Template at path "' + path + '" does not exist.');
}
@ -120,9 +135,6 @@ function buildTag(tagObject, path, taglib, dirname) {
},
nodeClass: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Node module at path "' + path + '" does not exist.');
}
tag.nodeClass = path;
},
@ -138,10 +150,6 @@ function buildTag(tagObject, path, taglib, dirname) {
propertyHandlers(value, {
path: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Transformer at path "' + path + '" does not exist.');
}
transformer.path = path;
},
@ -187,7 +195,7 @@ function buildTag(tagObject, path, taglib, dirname) {
nestedVariable = {};
propertyHandlers(v, {
name: function(value) {
nestedVariable.name = value;
},
@ -279,7 +287,7 @@ function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, taglib) {
taglib.addTag(tag);
} else {
// marko-tag.json does *not* exist... checking for a 'renderer.js'
if (fs.existsSync(rendererFile)) {
var rendererCode = fs.readFileSync(rendererFile, {encoding: 'utf8'});
@ -316,20 +324,18 @@ function load(path) {
return cache[path];
}
var src = fs.readFileSync(path, {encoding: 'utf8'});
var taglib = new Taglib(path);
taglib.addInputFile(path);
var dirname = nodePath.dirname(path);
var taglibObject;
try {
taglibObject = JSON.parse(src);
}
catch(e) {
taglibObject = require(path);
} catch(e) {
throw new Error('Unable to parse taglib JSON at path "' + path + '". Exception: ' + e);
}
var taglib = new Taglib(path);
taglib.addInputFile(path);
var dirname = nodePath.dirname(path);
propertyHandlers(taglibObject, {
attributes: function(value) {
handleAttributes(value, taglib, path);
@ -344,18 +350,15 @@ function load(path) {
if (typeof path === 'string') {
path = nodePath.resolve(dirname, path);
taglib.addInputFile(path);
tagDirname = nodePath.dirname(path);
if (!fs.existsSync(path)) {
if (!exists(path)) {
throw new Error('Tag at path "' + path + '" does not exist. Taglib: ' + taglib.id);
}
var tagJSON = fs.readFileSync(path, {encoding: 'utf8'});
try {
tagObject = JSON.parse(tagJSON);
}
catch(e) {
tagObject = require(path);
} catch(e) {
throw new Error('Unable to parse tag JSON for tag at path "' + path + '"');
}
} else {
@ -394,10 +397,6 @@ function load(path) {
propertyHandlers(value, {
path: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Transformer at path "' + path + '" does not exist.');
}
transformer.path = path;
}

View File

@ -1,107 +1,15 @@
var nodePath = require('path');
var fs = require('fs');
var taglibLoader = require('./taglib-loader');
var existsCache = {};
var taglibFinder = require('./taglib-finder');
var TaglibLookup = require('./TaglibLookup');
exports.registeredTaglibs = [];
var lookupCache = {};
var discoverCache = {};
var excludedDirs = {};
var taglibsByPath = {};
function existsCached(path) {
var exists = existsCache[path];
if (exists === undefined) {
exists = fs.existsSync(path);
existsCache = exists;
}
return exists;
}
function tryDir(dirname, discovered) {
var taglibPath = nodePath.join(dirname, 'marko-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
discovered.push(taglib);
}
}
function tryNodeModules(parent, discovered) {
if (nodePath.basename(parent) === 'node_modules') {
return;
}
var nodeModulesDir = nodePath.join(parent, 'node_modules');
var taglibs = taglibsByPath[nodeModulesDir];
if (taglibs !== undefined) {
if (taglibs !== null) {
for (var i = 0, len = taglibs.length; i < len; i++) {
discovered.push(taglibs[i]);
}
}
return;
}
if (existsCached(nodeModulesDir)) {
taglibs = [];
var children = fs.readdirSync(nodeModulesDir);
children.forEach(function(moduleDirBasename) {
var moduleDir = nodePath.join(nodeModulesDir, moduleDirBasename);
var taglibPath = nodePath.join(moduleDir, 'marko-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
taglib.moduleName = moduleDirBasename;
taglibs.push(taglib);
discovered.push(taglib);
}
});
taglibsByPath[nodeModulesDir] = taglibs.length ? taglibs : null;
} else {
taglibsByPath[nodeModulesDir] = null;
}
}
function discoverHelper(dirname, discovered) {
if (!excludedDirs[dirname]) {
tryDir(dirname, discovered);
tryNodeModules(dirname, discovered);
}
var parent = nodePath.dirname(dirname);
if (parent && parent !== dirname) {
// TODO: Don't use recursion (there's a simpler way)
discoverHelper(parent, discovered);
}
}
function discover(dirname) {
var discovered = discoverCache[dirname];
if (discovered) {
return discovered;
}
discovered = [];
discoverHelper(dirname, discovered);
discovered = discovered.concat(exports.registeredTaglibs);
discoverCache[dirname] = discovered;
return discovered;
}
function buildLookup(dirname) {
var taglibs = discover(dirname);
var taglibs = taglibFinder.find(dirname, exports.registeredTaglibs);
var lookupCacheKey = taglibs
.map(function(taglib) {
@ -112,7 +20,7 @@ function buildLookup(dirname) {
var lookup = lookupCache[lookupCacheKey];
if (lookup === undefined) {
lookup = new TaglibLookup();
for (var i=taglibs.length-1; i>=0; i--) {
lookup.addTaglib(taglibs[i]);
}
@ -131,10 +39,6 @@ function registerTaglib(taglib) {
exports.registeredTaglibs.push(taglib);
}
function excludeDir(dirname) {
excludedDirs[dirname] = true;
}
exports.excludeDir = excludeDir;
exports.excludeDir = taglibFinder.excludeDir;
exports.registerTaglib = registerTaglib;
exports.buildLookup = buildLookup;

View File

@ -0,0 +1,7 @@
exports.getLastModified = function(sourceFile, taglibs) {
return -1;
};
exports.checkUpToDate = function(targetFile, sourceFile, taglibs) {
return false;
};

63
compiler/up-to-date.js Normal file
View File

@ -0,0 +1,63 @@
var fs = require('fs'); // Fool the code inspectors for client-side bundlers
exports.getLastModified = function(sourceFile, taglibs) {
var statSource = fs.statSync(sourceFile);
var lastModifiedTime = statSource.mtime.getTime();
var taglibFiles = taglibs.getInputFiles();
var len = taglibFiles.length;
for (var i=0; i<len; i++) {
var taglibFileStat;
var taglibFile = taglibFiles[i];
try {
taglibFileStat = fs.statSync(taglibFile);
} catch(e) {
continue;
}
lastModifiedTime = Math.max(lastModifiedTime, taglibFileStat.mtime.getTime());
}
return lastModifiedTime;
};
exports.checkUpToDate = function(targetFile, sourceFile, taglibs) {
var statTarget;
try {
statTarget = fs.statSync(targetFile);
} catch(e) {
return false;
}
var statSource = fs.statSync(sourceFile);
if (statSource.mtime.getTime() > statTarget.mtime.getTime()) {
return false;
}
// Now check if any of the taglib files have been modified after the target file was generated
var taglibFiles = taglibs.getInputFiles();
var len = taglibFiles.length;
for (var i=0; i<len; i++) {
var taglibFileStat;
var taglibFile = taglibFiles[i];
try {
taglibFileStat = fs.statSync(taglibFile);
} catch(e) {
continue;
}
if (taglibFileStat.mtime.getTime() > statTarget.mtime.getTime()) {
return false;
}
}
return true;
};

View File

@ -0,0 +1,3 @@
module.exports = function(resolvedPath, from) {
return resolvedPath;
};

View File

@ -0,0 +1 @@
module.exports = require('raptor-modules/resolver').deresolve;

View File

@ -0,0 +1,6 @@
{
"browser": {
"./resolve.js": "./resolve-browser.js",
"./deresolve.js": "./deresolve-browser.js"
}
}

View File

@ -0,0 +1,5 @@
var nodePath = require('path');
module.exports = function(target, from) {
return nodePath.join(from, target);
};

1
compiler/util/resolve.js Normal file
View File

@ -0,0 +1 @@
module.exports = require('raptor-modules/resolver').serverResolveRequire;

View File

@ -16,14 +16,20 @@
'use strict';
var stringify = require('raptor-json/stringify');
var nodePath = require('path');
var fs = require('fs');
var req = require;
var fs;
try {
fs = req('fs');
} catch(e) {}
var extend = require('raptor-util').extend;
function IncludeNode(props) {
if (IncludeNode.$super) {
IncludeNode.$super.call(this);
IncludeNode.$super.call(this);
}
if (props) {
this.setProperties(props);
}
@ -50,7 +56,7 @@ IncludeNode.prototype = {
dataExpression = {
toString: function () {
var propParts = [];
_this.forEachProperty(function (name, value) {
name = name.replace(/-([a-z])/g, function (match, lower) {
return lower.toUpperCase();

View File

@ -28,8 +28,6 @@ var WithNode = require('./WithNode');
var TagHandlerNode = require('./TagHandlerNode');
var IncludeNode = require('./IncludeNode');
var resolver = require('raptor-modules/resolver');
var coreAttrHandlers = [
[
'c-space', function(attr, node) {
@ -352,7 +350,7 @@ module.exports = function transform(node, compiler, template) {
node.setInputExpression(template.makeExpression(inputAttr));
}
} else {
var templatePath = resolver.deresolve(tag.template, compiler.dirname);
var templatePath = compiler.getRequirePath(tag.template);
// The tag is mapped to a template that will be used to perform
// the rendering so convert the node into a "IncludeNode" that can
// be used to include the output of rendering a template

5
taglibs/optimizer.json Normal file
View File

@ -0,0 +1,5 @@
{
"dependencies": [
"require: **/*.js"
]
}