From e2f64a6549fce0a35b87b0182b9a7f7b3f8d8902 Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Wed, 2 Apr 2014 14:11:43 -0600 Subject: [PATCH] Improved compilation and added a complete up-to-date check --- bin/rhtmlc.js | 290 ++++++++++++++---- compiler/lib/Taglib.js | 10 + compiler/lib/TaglibLookup.js | 25 ++ compiler/lib/TaglibXmlLoader.js | 1 + compiler/lib/TemplateCompiler.js | 44 ++- compiler/lib/taglib-loader.js | 13 +- package.json | 111 +++---- runtime/lib/loader.js | 19 +- test/.gitignore | 2 + .../rhtml-templates/simple.rhtml.js | 59 ++-- .../rxml-templates/simple.rxml.js | 64 ++-- 11 files changed, 478 insertions(+), 160 deletions(-) diff --git a/bin/rhtmlc.js b/bin/rhtmlc.js index 0a3a632f7..95db8de57 100644 --- a/bin/rhtmlc.js +++ b/bin/rhtmlc.js @@ -1,29 +1,52 @@ var raptorTemplatesCompiler = require('../compiler'); -var glob = require("glob"); var fs = require('fs'); -var globPatterns; -var raptorPromises = require('raptor-promises'); +var nodePath = require('path'); +var Minimatch = require('minimatch').Minimatch; +var cwd = process.cwd(); +require('raptor-ecma/es6'); -var argv = require('raptor-args').createParser({ +var mmOptions = { + matchBase: true, + dot: true, + flipNegate: true +}; + +function relPath(path) { + if (path.startsWith(cwd)) { + return path.substring(cwd.length+1); + } +} + +var args = require('raptor-args').createParser({ '--help': { type: 'boolean', description: 'Show this help message' }, - '--templates --template -t *': { + '--files --file -f *': { type: 'string[]', - description: 'The path to a template to compile' + description: 'A set of directories or files to compile' + }, + '--ignore -i': { + type: 'string[]', + description: 'An ignore rule (default: --ignore "/node_modules" ".*")' + }, + '--clean -c': { + type: 'boolean', + description: 'Clean all of the *.rhtml.js files' } }) .usage('Usage: $0 [options]') - .example('Compile a single template', '$0 rhtml template.rhtml') - .example('Compile all templates in the directory tree', '$0 rhtml **/*.rhtml') + .example('Compile a single template', '$0 template.rhtml') + .example('Compile all templates in the current directory', '$0 .') + .example('Compile multiple templates', '$0 template.rhtml src/ foo/') + .example('Delete all *.rhtml.js files in the current directory', '$0 . --clean') .validate(function(result) { if (result.help) { this.printUsage(); process.exit(0); } - if (!result.templates || result.templates.length === 0) { + if (!result.files || result.files.length === 0) { this.printUsage(); process.exit(1); } @@ -40,64 +63,225 @@ var argv = require('raptor-args').createParser({ }) .parse(); -globPatterns = argv.templates; -var found = {}; -var promises = []; -function compile(path) { - if (found[path]) { - return; +var ignoreRules = args.ignore; + +if (!ignoreRules) { + ignoreRules = ['/node_modules', '.*']; +} + +ignoreRules = ignoreRules.filter(function (s) { + s = s.trim(); + return s && !s.match(/^#/); +}); + +ignoreRules = ignoreRules.map(function (pattern) { + + return new Minimatch(pattern, mmOptions); +}); + + +function isIgnored(path, dir, stat) { + if (path.startsWith(dir)) { + path = path.substring(dir.length); } - found[path] = true; + path = path.replace(/\\/g, '/'); - var deferred = raptorPromises.defer(); + var ignore = false; + var ignoreRulesLength = ignoreRules.length; + for (var i=0; i 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 statTarget.mtime.getTime()) { + return false; + } + } + + return true; + } }; module.exports = TemplateCompiler; \ No newline at end of file diff --git a/compiler/lib/taglib-loader.js b/compiler/lib/taglib-loader.js index 702bda22b..9e2c01c6c 100644 --- a/compiler/lib/taglib-loader.js +++ b/compiler/lib/taglib-loader.js @@ -278,6 +278,11 @@ function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, taglib) { var tagFile = nodePath.join(dir, childFilename, 'raptor-tag.json'); var tagObject; var tag; + var rendererFile = nodePath.join(dir, childFilename, 'renderer.js'); + + // Record dependencies so that we can check if a template is up-to-date + taglib.addInputFile(tagFile); + taglib.addInputFile(rendererFile); if (fs.existsSync(tagFile)) { // raptor-tag.json exists in the directory, use that as the tag definition @@ -287,7 +292,7 @@ function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, taglib) { taglib.addTag(tag); } else { // raptor-tag.json does *not* exist... checking for a 'renderer.js' - var rendererFile = nodePath.join(dir, childFilename, 'renderer.js'); + if (fs.existsSync(rendererFile)) { var rendererCode = fs.readFileSync(rendererFile, {encoding: 'utf8'}); var tagDef = tagDefFromCode.extractTagDef(rendererCode); @@ -318,6 +323,7 @@ function load(path) { var src = fs.readFileSync(path, {encoding: 'utf8'}); var taglib = new Taglib(path); + taglib.addInputFile(path); var dirname = nodePath.dirname(path); function handleNS(ns) { @@ -355,6 +361,8 @@ function load(path) { if (typeof path === 'string') { path = nodePath.resolve(dirname, path); + taglib.addInputFile(path); + tagDirname = nodePath.dirname(path); if (!fs.existsSync(path)) { throw new Error('Tag at path "' + path + '" does not exist. Taglib: ' + taglib.id); @@ -368,8 +376,7 @@ function load(path) { catch(e) { throw new Error('Unable to parse tag JSON for tag at path "' + path + '"'); } - } - else { + } else { tagDirname = dirname; // Tag is in the same taglib file tagObject = path; path = '<' + tagName + '> tag in ' + taglib.id; diff --git a/package.json b/package.json index 8ea108a33..f35c466e9 100644 --- a/package.json +++ b/package.json @@ -1,56 +1,57 @@ { - "name": "raptor-templates", - "description": "Raptor Templates", - "keywords": [ - "templating", - "template", - "async", - "streaming" - ], - "repository": { - "type": "git", - "url": "https://github.com/raptorjs3/raptor-templates.git" - }, - "scripts": { - "test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test" - }, - "author": "Patrick Steele-Idem ", - "maintainers": [ - "Patrick Steele-Idem " - ], - "dependencies": { - "raptor-detect": "^0.2.0-beta", - "raptor-logging": "^0.2.0-beta", - "raptor-strings": "^0.2.0-beta", - "raptor-regexp": "^0.2.0-beta", - "raptor-util": "^0.2.0-beta", - "raptor-arrays": "^0.2.0-beta", - "raptor-json": "^0.2.0-beta", - "raptor-modules": "^0.2.0-beta", - "raptor-render-context": "^0.2.0-beta", - "raptor-data-providers": "^0.2.0-beta", - "raptor-xml": "^0.2.0-beta", - "raptor-objects": "^0.2.0-beta", - "raptor-ecma": "^0.2.0-beta", - "raptor-files": "^0.2.0-beta", - "htmlparser2": "~3.5.1", - "char-props": "~0.1.5", - "raptor-promises": "^0.2.0-beta", - "glob": "^3.2.9", - "raptor-args": "^0.1.9-beta" - }, - "devDependencies": { - "mocha": "~1.15.1", - "chai": "~1.8.1", - "raptor-cache": "^0.2.0-beta" - }, - "license": "Apache License v2.0", - "bin": { - "rhtmlc": "bin/rhtmlc" - }, - "main": "runtime/lib/raptor-templates.js", - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "version": "0.2.23-beta" -} \ No newline at end of file + "name": "raptor-templates", + "description": "Raptor Templates", + "keywords": [ + "templating", + "template", + "async", + "streaming" + ], + "repository": { + "type": "git", + "url": "https://github.com/raptorjs3/raptor-templates.git" + }, + "scripts": { + "test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test" + }, + "author": "Patrick Steele-Idem ", + "maintainers": [ + "Patrick Steele-Idem " + ], + "dependencies": { + "raptor-detect": "^0.2.0-beta", + "raptor-logging": "^0.2.0-beta", + "raptor-strings": "^0.2.0-beta", + "raptor-regexp": "^0.2.0-beta", + "raptor-util": "^0.2.0-beta", + "raptor-arrays": "^0.2.0-beta", + "raptor-json": "^0.2.0-beta", + "raptor-modules": "^0.2.0-beta", + "raptor-render-context": "^0.2.0-beta", + "raptor-data-providers": "^0.2.0-beta", + "raptor-xml": "^0.2.0-beta", + "raptor-objects": "^0.2.0-beta", + "raptor-ecma": "^0.2.0-beta", + "raptor-files": "^0.2.0-beta", + "htmlparser2": "~3.5.1", + "char-props": "~0.1.5", + "raptor-promises": "^0.2.0-beta", + "glob": "^3.2.9", + "raptor-args": "^0.1.9-beta", + "minimatch": "^0.2.14" + }, + "devDependencies": { + "mocha": "~1.15.1", + "chai": "~1.8.1", + "raptor-cache": "^0.2.0-beta" + }, + "license": "Apache License v2.0", + "bin": { + "rhtmlc": "bin/rhtmlc" + }, + "main": "runtime/lib/raptor-templates.js", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "version": "0.2.23-beta" +} diff --git a/runtime/lib/loader.js b/runtime/lib/loader.js index 588b18216..7837dfcd7 100644 --- a/runtime/lib/loader.js +++ b/runtime/lib/loader.js @@ -1,14 +1,16 @@ var nodePath = require('path'); var fs = require('fs'); var Module = require('module').Module; -var compiler = require('../../compiler'); +var raptorTemplatesCompiler = require('../../compiler'); function loadSource(templatePath, compiledSrc) { var templateModulePath = templatePath + '.js'; + var templateModule = new Module(templateModulePath, module); templateModule.paths = Module._nodeModulePaths(nodePath.dirname(templateModulePath)); templateModule.filename = templateModulePath; + templateModule._compile( compiledSrc, templateModulePath); @@ -17,10 +19,21 @@ function loadSource(templatePath, compiledSrc) { } module.exports = function load(templatePath) { + var targetFile = templatePath + '.js'; + var compiler = raptorTemplatesCompiler.createCompiler(templatePath); + var isUpToDate = compiler.checkUpToDate(templatePath, targetFile); + if (isUpToDate) { + return require(targetFile); + } + var templateSrc = fs.readFileSync(templatePath, {encoding: 'utf8'}); - var compiledSrc = compiler.compile(templateSrc, templatePath); + var compiledSrc = compiler.compile(templateSrc); + // console.log('Compiled code for "' + templatePath + '":\n' + compiledSrc); - return loadSource(templatePath, compiledSrc); + + fs.writeFileSync(targetFile, compiledSrc, {encoding: 'utf8'}); + + return require(targetFile); }; module.exports.loadSource = loadSource; \ No newline at end of file diff --git a/test/.gitignore b/test/.gitignore index 2fd42ae9e..102048e64 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,2 +1,4 @@ +*.rhtml.js +*.rxml.js *.actual.js *.actual.html \ No newline at end of file diff --git a/test/test-project/rhtml-templates/simple.rhtml.js b/test/test-project/rhtml-templates/simple.rhtml.js index a49029fcb..4ee29caac 100644 --- a/test/test-project/rhtml-templates/simple.rhtml.js +++ b/test/test-project/rhtml-templates/simple.rhtml.js @@ -1,22 +1,37 @@ - $rtmpl("simple", function (templating) { - var empty = templating.e, - notEmpty = templating.ne, - forEach = templating.f; - return function (data, context) { - var write = context.w, - rootClass = data.rootClass, - colors = data.colors, - message = data.message; - write('
', message, '
'); - if (notEmpty(colors)) { - write('
    '); - forEach(colors, function (color) { - write('
  • ', color, '
  • '); - }); - write('
'); - } - if (empty(colors)) { - write('
No colors!
'); - } - } - }); \ No newline at end of file +module.exports = function create(helpers) { + var empty = helpers.e, + notEmpty = helpers.ne, + escapeXmlAttr = helpers.xa, + escapeXml = helpers.x, + forEach = helpers.f; + + return function render(data, context) { + var rootClass=data.rootClass; + + var colors=data.colors; + + var message=data.message; + + context.w('
') + .w(escapeXml(message)) + .w('
'); + + if (notEmpty(colors)) { + context.w('
    '); + + forEach(colors, function(color) { + context.w('
  • ') + .w(escapeXml(color)) + .w('
  • '); + }); + + context.w('
'); + } + + if (empty(colors)) { + context.w('
No colors!
'); + } + }; +} \ No newline at end of file diff --git a/test/test-project/rxml-templates/simple.rxml.js b/test/test-project/rxml-templates/simple.rxml.js index a49029fcb..2a5841862 100644 --- a/test/test-project/rxml-templates/simple.rxml.js +++ b/test/test-project/rxml-templates/simple.rxml.js @@ -1,22 +1,42 @@ - $rtmpl("simple", function (templating) { - var empty = templating.e, - notEmpty = templating.ne, - forEach = templating.f; - return function (data, context) { - var write = context.w, - rootClass = data.rootClass, - colors = data.colors, - message = data.message; - write('
', message, '
'); - if (notEmpty(colors)) { - write('
    '); - forEach(colors, function (color) { - write('
  • ', color, '
  • '); - }); - write('
'); - } - if (empty(colors)) { - write('
No colors!
'); - } - } - }); \ No newline at end of file +module.exports = function create(helpers) { + var empty = helpers.e, + notEmpty = helpers.ne, + hello_renderer = require("../hello-renderer"), + escapeXmlAttr = helpers.xa, + escapeXml = helpers.x, + forEach = helpers.f; + + return function render(data, context) { + var rootClass = data.rootClass, + colors = data.colors, + message = data.message; + + helpers.t(context, + hello_renderer, + { + "name": "World" + }); + + context.w('
') + .w(escapeXml(message)) + .w('
'); + + if (notEmpty(colors)) { + context.w('
    '); + + forEach(colors, function(color) { + context.w('
  • ') + .w(escapeXml(color)) + .w('
  • '); + }); + + context.w('
'); + } + + if (empty(colors)) { + context.w('
No colors!
'); + } + }; +} \ No newline at end of file