diff --git a/http.js b/http.js index 7a357349..79d493cd 100644 --- a/http.js +++ b/http.js @@ -36,6 +36,11 @@ function start (entry, opts) { if (name === 'styles:bundle') emitter.emit('styles:bundle', node) }) + router.route(/^\/(favicon\.)(ico|png|gif)$/, function (req, res, params) { + var filename = params[1] + params[2] + pump(send(req, filename), res) + }) + router.route(/^\/manifest\.json$/, function (req, res, params) { compiler.manifest(function (err, node) { if (err) { diff --git a/index.js b/index.js index daf8e9d8..816f1587 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ var ServerRender = require('./ssr') var assetsNode = require('./lib/graph-assets') var documentNode = require('./lib/graph-document') +var faviconNode = require('./lib/graph-favicon') var manifestNode = require('./lib/graph-manifest') var reloadNode = require('./lib/graph-reload') var scriptNode = require('./lib/graph-script') @@ -100,12 +101,13 @@ function Bankai (entry, opts) { }) // Insert nodes into the graph. - var documentDependencies = [ 'assets:list', 'manifest:bundle', 'styles:bundle', 'scripts:bundle' ] + var documentDependencies = [ 'assets:list', 'manifest:bundle', 'styles:bundle', 'scripts:bundle', 'favicon:bundle' ] if (opts.reload) { documentDependencies.push('reload:bundle') this.graph.node('reload', reloadNode) } + this.graph.node('favicon', faviconNode) this.graph.node('assets', assetsNode) this.graph.node('documents', documentDependencies, documentNode) this.graph.node('manifest', manifestNode) diff --git a/lib/cmd-build.js b/lib/cmd-build.js index 28cd879c..0b31aaa0 100644 --- a/lib/cmd-build.js +++ b/lib/cmd-build.js @@ -50,6 +50,7 @@ function build (entry, outdir, opts) { var stepName = nodeName + ':' + edgeName if (stepName === 'assets:list') writeAssets('assets') if (stepName === 'documents:list') writeDocuments('documents') + if (stepName === 'favicon:bundle') writeFavicon() if (stepName === 'scripts:bundle') writeScripts('scripts') if (stepName === 'service-worker:bundle') { var filename = compiler.graph.metadata.serviceWorker @@ -76,32 +77,27 @@ function build (entry, outdir, opts) { var dirname = utils.dirname(dest) if (dirname === dest) { - copy(done) + copy(src, dest, compiler.dirname, done) } else { mkdirp(dirname, function (err) { if (err) return done(err) - copy(done) + copy(src, dest, compiler.dirname, done) }) } + } + } - // Node <= 8.x does not have fs.copyFile(). This API is cool because - // on some OSes it is zero-copy all the way; e.g. never leaves the - // kernel! :D - function copy (done) { - fsCompare.mtime(src, dest, function (err, diff) { - if (err) return done(err) - if (diff === 1) { - if (fs.copyFile) fs.copyFile(src, dest, fin) - else pump(fs.createReadStream(src), fs.createWriteStream(dest), fin) - } - }) - function fin (err) { - if (err) return done(err) - created(compiler.dirname, dest) - done() - } - } + function writeFavicon () { + var favicon = compiler.graph.data.favicon.bundle.buffer.toString() + if (favicon.length === 0) { + return } + var src = path.join(compiler.dirname, favicon) + var dest = path.join(outdir, favicon) + copy(src, dest, compiler.dirname, function (err) { + if (err) return log.error(err) + completed('favicon') + }) } function writeScripts (step) { @@ -182,6 +178,24 @@ function build (entry, outdir, opts) { } } + // Node <= 8.x does not have fs.copyFile(). This API is cool because + // on some OSes it is zero-copy all the way; e.g. never leaves the + // kernel! :D + function copy (src, dest, dirname, done) { + fsCompare.mtime(src, dest, function (err, diff) { + if (err) return done(err) + if (diff === 1) { + if (fs.copyFile) fs.copyFile(src, dest, fin) + else pump(fs.createReadStream(src), fs.createWriteStream(dest), fin) + } + }) + function fin (err) { + if (err) return done(err) + created(dirname, dest) + done() + } + } + function created (basedir, filename) { var relative = path.relative(process.cwd(), filename) log.info(`${clr('created', 'magenta')}: ${relative}`) diff --git a/lib/graph-document.js b/lib/graph-document.js index d81cbd91..d064cbf6 100644 --- a/lib/graph-document.js +++ b/lib/graph-document.js @@ -108,6 +108,7 @@ function node (state, createEdge) { }), preloadTag(), loadFontsTag({ fonts: fonts, base: base }), + faviconsTag({ favicon: state.favicon.bundle.buffer }), manifestTag({ base: base }), descriptionTag({ description: state.manifest.bundle.description }), themeColorTag({ color: state.manifest.bundle.color }), @@ -197,6 +198,14 @@ function manifestTag (opts) { `.replace(/\n +/g, '') } +function faviconsTag (opts) { + var favicon = opts.favicon.toString() + if (favicon.length > 0) { + return `` + } + return `` +} + function viewportTag () { return ` diff --git a/lib/graph-favicon.js b/lib/graph-favicon.js new file mode 100644 index 00000000..56f0a255 --- /dev/null +++ b/lib/graph-favicon.js @@ -0,0 +1,22 @@ +var path = require('path') +var utils = require('./utils') + +module.exports = node + +function node (state, createEdge) { + var dirname = state.metadata.dirname + var filenames = [ + 'favicon.ico', + 'favicon.png', + 'favicon.gif' + ] + + // find favicon at the root + utils.find(dirname, filenames, function (err, filename) { + if (err || filename === void 0) { + createEdge('bundle', Buffer.from('')) + return + } + createEdge('bundle', Buffer.from(path.relative(dirname, filename))) + }) +}