diff --git a/lib/helpers/memoized.js b/lib/helpers/memoized.js index 5b79ebafe..7d04ee32f 100644 --- a/lib/helpers/memoized.js +++ b/lib/helpers/memoized.js @@ -32,4 +32,4 @@ exports.walkData = helpers.walkData exports.isTemplate = helpers.isTemplate exports.isStylesheet = helpers.isStylesheet exports.isJavaScript = helpers.isJavaScript - +exports.needsBrowserify = helpers.needsBrowserify diff --git a/lib/helpers/raw.js b/lib/helpers/raw.js index d0918b1db..6f1bf933f 100644 --- a/lib/helpers/raw.js +++ b/lib/helpers/raw.js @@ -15,7 +15,7 @@ var TerraformError = exports.TerraformError = require("../error").TerraformError var processors = exports.processors = { "html": ["jade", "ejs", "md"], "css" : ["styl", "less", "scss", "sass"], - "js" : ["coffee"] + "js" : ["js", "coffee"] } @@ -219,18 +219,18 @@ var dataTree = exports.dataTree = function (filename) { obj._contents = [] try{ - var dataPath = path.resolve(dirPath, "_data.json") - var fileData = fs.readFileSync(dataPath) - obj._data = JSON.parse(fileData) + var dataPath = path.resolve(dirPath, "_data") + obj._data = require(dataPath) + delete require.cache[require.resolve(dataPath)] }catch(e){ - if(e.code && e.code === "ENOENT"){ + if(e.code && e.code === "MODULE_NOT_FOUND"){ // data file failed or does not exist }else{ e.source = "Data" e.dest = "Globals" e.lineno = -1 - e.filename = dataPath - e.stack = fileData.toString() + e.filename = require.resolve(dataPath) + e.stack = fs.readFileSync(e.filename) throw new TerraformError(e) } //console.log(e.code) @@ -362,7 +362,7 @@ var outputPath = exports.outputPath = function(source, allowAlternateExtensions) for(var targetExtension in processors){ // .html, .css, .js if (processors.hasOwnProperty(targetExtension)) { processors[targetExtension].forEach(function(sourceExtension){ // .jade, .ejs, .md - if (allowAlternateExtensions) { + if (allowAlternateExtensions && targetExtension !== sourceExtension) { // Don’t bother if it’s .js to .js // Search for a alternate extension before the known source extension e.g. foobar.bar.jade var alternateFileExtension = new RegExp("^.*\\.(\\w{3,4})\\." + sourceExtension + "$") var match = alternateFileExtension.exec(source) @@ -496,3 +496,14 @@ exports.isJavaScript = function(filePath){ return processors["js"].indexOf(ext) !== -1 } + +/** + * needsBrowserify + * + * returns true if the code uses require, exports or module but doesn't declare them + */ + +exports.needsBrowserify = function(source) { + return /^[^#\/'"*]*(require|module|exports)\b/m.test(source) + && !(/\b(function|var|global) +(require|module|exports)\b|\b(module|require) *=[^=]/.test(source)) +} diff --git a/lib/javascript/index.js b/lib/javascript/index.js index bf5be4e9f..7f45b204c 100644 --- a/lib/javascript/index.js +++ b/lib/javascript/index.js @@ -1,7 +1,9 @@ -var path = require("path") -var fs = require("fs") -var helpers = require('../helpers') +var path = require("path") +var fs = require("fs") +var helpers = require('../helpers') var minify = require('harp-minify') +var browserify = require('browserify') +var through = require('through') /** * Build Processor list for javascripts. @@ -13,8 +15,9 @@ var minify = require('harp-minify') * } * */ - var processors = {} + var extensions = [], processors = {} helpers.processors["js"].forEach(function(sourceType){ + extensions.push('.' + sourceType) processors[sourceType] = require("./processors/" + sourceType) }) @@ -22,6 +25,10 @@ module.exports = function(root, filePath, callback){ var srcPath = path.resolve(root, filePath) var ext = path.extname(srcPath).replace(/^\./, '') + var minifyOpts = { + compress: false, + mangle: false + } fs.readFile(srcPath, function(err, data){ @@ -41,18 +48,58 @@ module.exports = function(root, filePath, callback){ * Lookup Directories */ - var render = processors[ext].compile(srcPath, data, function(err, js) { - if (err) return callback(err); - - /** - * Consistently minify - */ - var post = minify.js(js, { - compress: false, - mangle: true - }); - callback(null, post); - }) + var render = function(ext, data, cb) { + processors[ext].compile(srcPath, data, function(err, js) { + if (err) return cb(err) + + /** + * Consistently minify + */ + cb(null, minify.js(js, minifyOpts)) + }) + } + + if(helpers.needsBrowserify(data.toString())) { + var post = '', success = true + + var exceptionHandler = function(err) { + success = false + console.log(err.message) + render(ext, data, callback) + } + + process.once('uncaughtException', exceptionHandler) + browserify(srcPath, {extensions: extensions}).transform(function(file) { + var result = '' + return through(write, end) + + function write(buf) { + result += buf + } + function end() { + if(success) { + var that = this + render(path.extname(file).replace(/^\./, '').toLowerCase(), result, function(err, data) { + that.queue(data) + that.queue(null) + }) + } + } + }).on('error', exceptionHandler).bundle() + .on('data', function(buf) { + if (success) { + post += buf + } + }).on('end', function() { + if (success) { + process.removeListener('uncaughtException', exceptionHandler) + callback(null, minify.js(post, minifyOpts)) + } + }) + } + else { + render(ext, data, callback) + } }) diff --git a/lib/javascript/processors/js.js b/lib/javascript/processors/js.js new file mode 100644 index 000000000..4291f1541 --- /dev/null +++ b/lib/javascript/processors/js.js @@ -0,0 +1,3 @@ +exports.compile = function(filePath, fileContents, callback){ + callback(null, fileContents.toString()) +} diff --git a/lib/template/processors/jade.js b/lib/template/processors/jade.js index 93c91fd21..2b230c9f7 100644 --- a/lib/template/processors/jade.js +++ b/lib/template/processors/jade.js @@ -1,4 +1,4 @@ -var jade = require('harp-jade') +var jade = require('jade') var TerraformError = require("../../error").TerraformError module.exports = function(fileContents, options){ diff --git a/package.json b/package.json index 223d37b7a..bc8e73f1d 100644 --- a/package.json +++ b/package.json @@ -12,37 +12,50 @@ }, "author": "Brock Whitten ", "contributors": [ - { "name": "Brock Whitten", "email": "brock@chloi.io" }, - { "name": "Brian Donovan", "email": "donovan@squareup.com" }, - { "name": "Kenneth Ormandy", "email": "kenneth@chloi.io" }, - { "name": "Zhang Yichao", "email": "echaozh@gmail.com" }, - { "name": "Carlos Rodriguez" }, - { "name": "Zeke Sikelianos", "email": "zeke@sikelianos.com" }, - { "name": "Guilherme Rodrigues", "email": "gadr90@gmail.com" }, - { "name": "Radu Brehar", "email": "radu@jslog.com" }, - { "name": "Glen Maddern", "email": "glenmaddern@gmail.com" }, - { "name": "Jed Foster", "email": "jed@jedfoster.com" }, - { "name": "Sehrope Sarkuni", "email": "sehrope@jackdb.com" }, - { "name": "Keiichiro Matsumoto", "email": "matsumos@gmail.com" }, - { "name": "Najam Khn", "email": "najamkhn@gmail.com" } + "Brock Whitten ", + "Brian Donovan ", + "Kenneth Ormandy ", + "Zhang Yichao ", + "Carlos Rodriguez", + "Zeke Sikelianos ", + "Guilherme Rodrigues ", + "Radu Brehar ", + "Glen Maddern ", + "Jed Foster ", + "Sehrope Sarkuni ", + "Keiichiro Matsumoto ", + "Najam Khn ", + "Eric Drechsel", + "Najam Khan", + "Zhang Yichao ", + "Dave Jensen", + "Ryan Lewis", + "Julian Duque ", + "Lu Nelson ", + "Stephen Way", + "Pierre Spring ", + "John Boxall ", + "TJ Nicolaides " ], "license": "MIT", "dependencies": { - "lru-cache": "2.7.0", - "harp-jade": "1.9.3-bc.4", + "autoprefixer": "6.2.3", + "browserify": "12.0.1", "coffee-script": "1.10.0", - "node-sass": "3.4.2", "ejs": "2.3.4", - "marked": "0.3.5", - "lodash": "3.10.1", + "harp-minify": "0.4.0", + "jade": "1.11.0", "less": "2.5.3", + "lodash": "3.10.1", + "lru-cache": "4.0.0", + "marked": "0.3.5", + "node-sass": "3.4.2", + "postcss": "5.0.14", "stylus": "0.53.0", - "harp-minify": "0.3.3", - "autoprefixer": "6.1.0", - "postcss": "5.0.12" + "through": "2.3.8" }, "devDependencies": { - "mocha": "1.8.2", + "mocha": "2.3.4", "should": "1.2.2" } } diff --git a/test/data.js b/test/data.js index b1f78d65d..4409fed53 100644 --- a/test/data.js +++ b/test/data.js @@ -96,4 +96,16 @@ describe("data", function(){ }) }) + describe("dynamic", function(){ + it("should return public object", function(done){ + var root = __dirname + "/fixtures/data/dynamic" + var poly = polymer.root(root) + poly.render("pub.json.jade", { "layout": false }, function(err, result){ + var pub = JSON.parse(result) + should.exist(pub["articles"]["_data"]["hello-world"]) + should.not.exist(pub[".foo"]) + done() + }) + }) + }) }) diff --git a/test/fixtures/data/dynamic/.foo/placeholder.txt b/test/fixtures/data/dynamic/.foo/placeholder.txt new file mode 100644 index 000000000..5c532474c --- /dev/null +++ b/test/fixtures/data/dynamic/.foo/placeholder.txt @@ -0,0 +1 @@ +This file is here to test ignoring directories that begin with a dot (".") diff --git a/test/fixtures/data/dynamic/_layout.jade b/test/fixtures/data/dynamic/_layout.jade new file mode 100644 index 000000000..e46014a31 --- /dev/null +++ b/test/fixtures/data/dynamic/_layout.jade @@ -0,0 +1,3 @@ +h1 My Articles +h5.feature= public.articles._data['hello-world'].title +!= yield \ No newline at end of file diff --git a/test/fixtures/data/dynamic/articles/_data.js b/test/fixtures/data/dynamic/articles/_data.js new file mode 100644 index 000000000..178320175 --- /dev/null +++ b/test/fixtures/data/dynamic/articles/_data.js @@ -0,0 +1,6 @@ +module.exports = { + "hello-world": { + "title" : "Earth people, New York to California", + "author": "Brock Whitten" + } +} \ No newline at end of file diff --git a/test/fixtures/data/dynamic/articles/hello-world.jade b/test/fixtures/data/dynamic/articles/hello-world.jade new file mode 100644 index 000000000..8cf7296e9 --- /dev/null +++ b/test/fixtures/data/dynamic/articles/hello-world.jade @@ -0,0 +1,4 @@ +h3= title +h4= author + +p Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \ No newline at end of file diff --git a/test/fixtures/data/dynamic/index.jade b/test/fixtures/data/dynamic/index.jade new file mode 100644 index 000000000..a0ce712f0 --- /dev/null +++ b/test/fixtures/data/dynamic/index.jade @@ -0,0 +1,6 @@ +h2 Home + +h3 Articles + +for article, slug in public.articles._data + != partial('articles/' + slug + '.jade', { author: "Kool Keith" }) \ No newline at end of file diff --git a/test/fixtures/data/dynamic/pub.json.jade b/test/fixtures/data/dynamic/pub.json.jade new file mode 100644 index 000000000..d6b8764c8 --- /dev/null +++ b/test/fixtures/data/dynamic/pub.json.jade @@ -0,0 +1 @@ +!= JSON.stringify(public) \ No newline at end of file diff --git a/test/fixtures/javascripts/browserify/Math.coffee b/test/fixtures/javascripts/browserify/Math.coffee new file mode 100644 index 000000000..ee5932f46 --- /dev/null +++ b/test/fixtures/javascripts/browserify/Math.coffee @@ -0,0 +1,4 @@ +# Let's see if we can mix .coffee & .es + +exports.pow = (num) -> + num * num diff --git a/test/fixtures/javascripts/browserify/Math.js b/test/fixtures/javascripts/browserify/Math.js new file mode 100644 index 000000000..c462e3352 --- /dev/null +++ b/test/fixtures/javascripts/browserify/Math.js @@ -0,0 +1,5 @@ +// Let's see if we can mix .es & .js + +exports.pow = function(num) { + return num * num; +}; diff --git a/test/fixtures/javascripts/browserify/comment.coffee b/test/fixtures/javascripts/browserify/comment.coffee new file mode 100644 index 000000000..a1fb7b864 --- /dev/null +++ b/test/fixtures/javascripts/browserify/comment.coffee @@ -0,0 +1,3 @@ +# pow = require('./Math').pow; + +console.log(pow(4)); diff --git a/test/fixtures/javascripts/browserify/comment.js b/test/fixtures/javascripts/browserify/comment.js new file mode 100644 index 000000000..93418d714 --- /dev/null +++ b/test/fixtures/javascripts/browserify/comment.js @@ -0,0 +1,5 @@ +/* + * pow = require('./Math').pow; + */ + +console.log(pow(4)); diff --git a/test/fixtures/javascripts/browserify/declared.coffee b/test/fixtures/javascripts/browserify/declared.coffee new file mode 100644 index 000000000..c209946aa --- /dev/null +++ b/test/fixtures/javascripts/browserify/declared.coffee @@ -0,0 +1,6 @@ +require = (file) -> + # custom implementation + +pow = require('./Math').pow + +console.log pow(4) diff --git a/test/fixtures/javascripts/browserify/declared.js b/test/fixtures/javascripts/browserify/declared.js new file mode 100644 index 000000000..40484a0fb --- /dev/null +++ b/test/fixtures/javascripts/browserify/declared.js @@ -0,0 +1,7 @@ +var require = function(file) { + // custom implementation +}; + +var pow = require('./Math').pow; + +console.log(pow(4)); diff --git a/test/fixtures/javascripts/browserify/nested/way/in/here/nested.coffee b/test/fixtures/javascripts/browserify/nested/way/in/here/nested.coffee new file mode 100644 index 000000000..f0aef7e8d --- /dev/null +++ b/test/fixtures/javascripts/browserify/nested/way/in/here/nested.coffee @@ -0,0 +1,3 @@ +pow = require('./../../../../Math.js').pow + +console.log pow(4) diff --git a/test/fixtures/javascripts/browserify/nested/way/in/here/nested.js b/test/fixtures/javascripts/browserify/nested/way/in/here/nested.js new file mode 100644 index 000000000..c5026a310 --- /dev/null +++ b/test/fixtures/javascripts/browserify/nested/way/in/here/nested.js @@ -0,0 +1,3 @@ +var pow = require('./../../../../Math.js').pow; + +console.log(pow(4)); diff --git a/test/fixtures/javascripts/browserify/require_coffee.coffee b/test/fixtures/javascripts/browserify/require_coffee.coffee new file mode 100644 index 000000000..43b6e2e87 --- /dev/null +++ b/test/fixtures/javascripts/browserify/require_coffee.coffee @@ -0,0 +1,3 @@ +pow = require('./Math.coffee').pow + +console.log pow(4) diff --git a/test/fixtures/javascripts/browserify/require_coffee.js b/test/fixtures/javascripts/browserify/require_coffee.js new file mode 100644 index 000000000..86a4bc2a3 --- /dev/null +++ b/test/fixtures/javascripts/browserify/require_coffee.js @@ -0,0 +1,3 @@ +var pow = require('./Math.coffee').pow; + +console.log(pow(4)); diff --git a/test/fixtures/javascripts/browserify/require_js.coffee b/test/fixtures/javascripts/browserify/require_js.coffee new file mode 100644 index 000000000..614e2afc8 --- /dev/null +++ b/test/fixtures/javascripts/browserify/require_js.coffee @@ -0,0 +1,3 @@ +pow = require('./Math.js').pow + +console.log pow(4) diff --git a/test/fixtures/javascripts/browserify/require_js.js b/test/fixtures/javascripts/browserify/require_js.js new file mode 100644 index 000000000..2d2e69f5e --- /dev/null +++ b/test/fixtures/javascripts/browserify/require_js.js @@ -0,0 +1,3 @@ +var pow = require('./Math.js').pow; + +console.log(pow(4)); diff --git a/test/helpers.js b/test/helpers.js index 83e7e1ff9..abc17662b 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -23,6 +23,16 @@ describe("helpers", function(){ done() }) + it('should return all possible file names for js ordered by priority.', function(done){ + var list = polymer.helpers.buildPriorityList('/js/bundle.js') + list.should.be.an.instanceOf(Array) + list.should.have.lengthOf(4) + var plist = "js/bundle.js, js/bundle.coffee, js/bundle.js.js, js/bundle.js.coffee".split(', ') + list.should.eql(plist) + done() + }) + + it('should build priority list assuming template file when unknown.', function(done){ var list = polymer.helpers.buildPriorityList('feed.xml') list.should.be.an.instanceOf(Array) @@ -257,6 +267,18 @@ describe("helpers", function(){ done() }) + it('should return true if javascript file.', function(done){ + polymer.helpers.isJavaScript(path.join('foo.js')).should.be.true + polymer.helpers.isJavaScript(path.join('foo', 'bar', 'baz.js')).should.be.true + done() + }) + + it('should return true if minified javascript file.', function(done){ + polymer.helpers.isJavaScript(path.join('foo.min.js')).should.be.true + polymer.helpers.isJavaScript(path.join('foo', 'bar', 'bas.min.js')).should.be.true + done() + }) + it('should return false if less file.', function(done){ polymer.helpers.isStylesheet(path.join('foo.less')).should.be.true polymer.helpers.isStylesheet(path.join('foo', 'bar', 'baz.less')).should.be.true diff --git a/test/javascripts.js b/test/javascripts.js index 432b16d35..dd71d0ec4 100644 --- a/test/javascripts.js +++ b/test/javascripts.js @@ -42,4 +42,82 @@ describe("javascripts", function(){ }) + describe("browserify", function() { + var root = __dirname + "/fixtures/javascripts/browserify" + var poly = polymer.root(root) + + process.chdir(root) + + it("should require coffeescript file in coffeescript", function(done) { + poly.render("require_coffee.coffee", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should require javascript file in coffeescript", function(done) { + poly.render("require_js.coffee", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should require coffeescript file in javascript", function(done) { + poly.render("require_coffee.js", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should require javascript file in javascript", function(done) { + poly.render("require_js.js", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should skip commented require in coffeescript", function(done) { + poly.render("comment.coffee", function(errors, body) { + should.not.exist(errors) + body.should.not.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should skip commented require in javascript", function(done) { + poly.render("comment.js", function(errors, body) { + should.not.exist(errors) + body.should.not.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should skip already declared require in coffeescript", function(done) { + poly.render("declared.coffee", function(errors, body) { + should.not.exist(errors) + body.should.not.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should skip already declared require in javascript", function(done) { + poly.render("declared.js", function(errors, body) { + should.not.exist(errors) + body.should.not.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should require javascript file in heavily nested coffeescript", function(done) { + poly.render("nested/way/in/here/nested.coffee", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + it("should require javascript file in heavily nested javascript file", function(done) { + poly.render("nested/way/in/here/nested.js", function(errors, body) { + should.not.exist(errors) + body.should.include("MODULE_NOT_FOUND") + done() + }) + }) + }) + }) diff --git a/test/templates.js b/test/templates.js index 9bb436a1b..d9ad2504c 100644 --- a/test/templates.js +++ b/test/templates.js @@ -58,11 +58,10 @@ describe("templates", function(){ describe(".jade", function(){ - it("should give deprecated !!! warning", function(done){ + it("should not give deprecated !!! warning", function(done){ poly.render("deprecated/jade/index.jade", function(error, body){ - should.not.exist(error) - should.exist('`!!!` is deprecated, you must now use `doctype`') - should.exist(body) + should.exist(error) + should.not.exist(body) done() }) })