187 строки
6.2 KiB
JavaScript
187 строки
6.2 KiB
JavaScript
var fsExtra = require('fs-extra');
|
|
var path = require('path');
|
|
var urllib = require('url');
|
|
|
|
var budo = require('budo');
|
|
var shell = require('shelljs');
|
|
var yonder = require('yonder');
|
|
|
|
var browserify = require('browserify');
|
|
var UglifyJS = require('uglify-js');
|
|
|
|
// In dev -> run budo
|
|
// In production -> run templates and browserify+uglify
|
|
var isProduction = process.env.NODE_ENV === 'production';
|
|
|
|
var OPTS = {
|
|
entry: 'public/media/js/main.js',
|
|
entryRelative: 'media/js/main.js', // for the <script> tag in budo
|
|
assets: {
|
|
glob: {
|
|
strict: true,
|
|
cwd: path.resolve(__dirname, 'public'),
|
|
include: '**/*',
|
|
ignore: '**/*.html',
|
|
nonull: true
|
|
},
|
|
inputDir: path.resolve(__dirname, 'public'),
|
|
outputDir: path.resolve(__dirname, '_prod')
|
|
},
|
|
nunjucks: {
|
|
glob: {
|
|
include: '**/*.html'
|
|
},
|
|
extensionsFile: path.resolve(__dirname, 'nunjucks-helpers.js'),
|
|
inputDir: path.resolve(__dirname, 'public'),
|
|
outputDir: path.resolve(__dirname, '_prod')
|
|
}
|
|
};
|
|
OPTS.routerPath = path.join(OPTS.assets.inputDir, 'ROUTER');
|
|
|
|
var budoMiddleware = [
|
|
staticMiddlewareForFilesWithoutTrailingSlashes
|
|
];
|
|
|
|
// Create server-side redirects (defined in the `ROUTER` file).
|
|
// See https://github.com/sintaxi/yonder#readme for usage.
|
|
if (fsExtra.existsSync(OPTS.routerPath)) {
|
|
budoMiddleware.push(yonder.middleware(OPTS.routerPath));
|
|
}
|
|
|
|
// run dev script
|
|
if (isProduction) {
|
|
// Generate nunjucks and bundle our entry file
|
|
initialBuild(() => {
|
|
console.log('Bundling %s…', OPTS.entry);
|
|
browserify(OPTS.entry, {
|
|
debug: false // no source mapping for now…
|
|
}).bundle((err, src) => {
|
|
if (err) throw err;
|
|
console.log('Compressing bundle…');
|
|
const result = UglifyJS.minify(src.toString(), { fromString: true }).code;
|
|
// Writes to nunjucks output, not assets output !
|
|
var outFile = path.resolve(OPTS.nunjucks.outputDir, OPTS.entryRelative);
|
|
fsExtra.writeFile(outFile, result, err => {
|
|
if (err) throw err;
|
|
});
|
|
});
|
|
});
|
|
} else {
|
|
initialBuild(() => startServer());
|
|
}
|
|
|
|
function startServer () {
|
|
var app = budo(OPTS.entry, {
|
|
serve: OPTS.entryRelative,
|
|
port: 3000,
|
|
dir: '_prod',
|
|
cors: true,
|
|
stream: process.stdout,
|
|
verbose: true,
|
|
middleware: budoMiddleware
|
|
})
|
|
.live() // we specify LiveReload *manually*, not in budo options!
|
|
.watch([ // enable the chokidar file watcher
|
|
'public/**/*', // only watch public for source changes
|
|
'_prod/**/*.{html,css}' // only trigger LiveReload on HTML/CSS changes
|
|
])
|
|
.on('watch', function (ev, file) {
|
|
file = path.resolve(file);
|
|
|
|
var isEntry = file === path.resolve(OPTS.entry);
|
|
var isInput = [
|
|
OPTS.assets.inputDir, OPTS.nunjucks.inputDir
|
|
].some(dir => {
|
|
return file.toLowerCase().indexOf(dir.toLowerCase()) === 0;
|
|
});
|
|
|
|
if (isEntry) {
|
|
// Special case for the bundle, the bundle.js is "virtual"
|
|
// and not rendered to disk
|
|
app.reload();
|
|
} else if (isInput) {
|
|
// Input file changed, decide what to re-build
|
|
var ext = path.extname(file);
|
|
if (!ext) return; // ignore files without extension ?
|
|
var isHTML = /\.html?$/i.test(ext);
|
|
|
|
var isSharedTemplate = isHTML && path.basename(file).charAt(0) === '_';
|
|
if (isSharedTemplate || /\.json$/i.test(ext)) {
|
|
regenerateAllNunjucksTemplates();
|
|
} else if (isHTML) {
|
|
var fileRelativeNunjucks = path.relative(OPTS.nunjucks.inputDir, file);
|
|
regenerateTemplate(fileRelativeNunjucks);
|
|
} else {
|
|
// Just assume it's any other file type.
|
|
var parentFolder = file.toLowerCase().indexOf(OPTS.nunjucks.inputDir.toLowerCase()) === 0
|
|
? OPTS.nunjucks.inputDir
|
|
: OPTS.assets.inputDir;
|
|
var fileRelative = path.relative(parentFolder, file);
|
|
var fileOutput = path.join(OPTS.assets.outputDir, fileRelative);
|
|
var fileOutputDir = path.dirname(fileOutput);
|
|
console.log('Copying: %s', fileRelative);
|
|
fsExtra.mkdirp(fileOutputDir, err => {
|
|
if (err) return console.error(err);
|
|
fsExtra.copy(file, fileOutput, err => {
|
|
if (err) console.error(err);
|
|
// This might be useful if you copy over a binary file,
|
|
// SVG, image, or whatever. You could make this only
|
|
// reload on certain file types instead…
|
|
if (!/\.css$/i.test(ext)) app.reload();
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
// Output file changed, trigger LiveReload
|
|
app.reload(file);
|
|
}
|
|
});
|
|
}
|
|
|
|
function initialBuild (cb) {
|
|
fsExtra.remove(OPTS.assets.outputDir, err => {
|
|
if (err) console.error(err);
|
|
fsExtra.remove(OPTS.nunjucks.outputDir, err => {
|
|
if (err) console.error(err);
|
|
fsExtra.copy(OPTS.assets.inputDir, OPTS.assets.outputDir, err => {
|
|
if (err) console.error(err);
|
|
// finally regenerate all templates
|
|
regenerateAllNunjucksTemplates();
|
|
cb(null);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Serves nice URLs (à la GitHub Pages & Surge).
|
|
*
|
|
* For example, `/firefox` will be served from `/firefox.html`, and
|
|
* `/firefox/` will be served from `/firefox/index.html`,
|
|
* preserving the URLs.
|
|
*/
|
|
function staticMiddlewareForFilesWithoutTrailingSlashes (req, res, next) {
|
|
var parsedUrl = urllib.parse(req.url);
|
|
var pathname = parsedUrl.pathname;
|
|
var ext = path.extname(pathname);
|
|
if (ext || pathname.substr(-1) === '/') {
|
|
next();
|
|
return;
|
|
}
|
|
var fileRelative = path.join(OPTS.assets.inputDir, pathname + '.html');
|
|
fsExtra.exists(fileRelative, function (exists) {
|
|
if (exists) {
|
|
req.url = pathname + '.html' + (parsedUrl.search || '') + (parsedUrl.hash || '');
|
|
}
|
|
next();
|
|
});
|
|
}
|
|
|
|
function regenerateAllNunjucksTemplates () {
|
|
return shell.exec(`node ./node_modules/.bin/nunjucks "${OPTS.nunjucks.glob.include}" --path ${OPTS.nunjucks.inputDir} --unsafe --extensions ${OPTS.nunjucks.extensionsFile} --out ${OPTS.nunjucks.outputDir}`);
|
|
}
|
|
|
|
function regenerateTemplate (fileRelative) {
|
|
return shell.exec(`node ./node_modules/.bin/nunjucks ${fileRelative} --path ${OPTS.nunjucks.inputDir} --unsafe --extensions ${OPTS.nunjucks.extensionsFile} --out ${OPTS.nunjucks.outputDir}`);
|
|
}
|