const fs = require("fs-extra"); const TemplatePath = require("./TemplatePath"); const config = require("./Config"); const debug = require("debug")("EleventyServe"); class EleventyServe { constructor() {} get config() { return this.configOverride || config.getConfig(); } set config(config) { this.configOverride = config; } setOutputDir(outputDir) { this.outputDir = outputDir; } getPathPrefix() { return this.config.pathPrefix || "/"; } getRedirectDir(dirName) { return TemplatePath.join(this.outputDir, dirName); } getRedirectDirOverride() { // has a pathPrefix, add a /index.html template to redirect to /pathPrefix/ if (this.getPathPrefix() !== "/") { return "_eleventy_redirect"; } } getRedirectFilename(dirName) { return TemplatePath.join(this.getRedirectDir(dirName), "index.html"); } getOptions(port) { let pathPrefix = this.getPathPrefix(); // TODO customize this in Configuration API? let serverConfig = { baseDir: this.outputDir }; let redirectDirName = this.getRedirectDirOverride(); // has a pathPrefix, add a /index.html template to redirect to /pathPrefix/ if (redirectDirName) { serverConfig.baseDir = this.getRedirectDir(redirectDirName); serverConfig.routes = {}; serverConfig.routes[pathPrefix] = this.outputDir; // if has a savedPathPrefix, use the /savedPathPrefix/index.html template to redirect to /pathPrefix/ if (this.savedPathPrefix) { serverConfig.routes[this.savedPathPrefix] = TemplatePath.join( this.outputDir, this.savedPathPrefix ); } } return Object.assign( { server: serverConfig, port: port || 8080, ignore: ["node_modules"], watch: false, open: false, notify: false, index: "index.html" }, this.config.browserSyncConfig ); } cleanupRedirect(dirName) { if (dirName && dirName !== "/") { let savedPathFilename = this.getRedirectFilename(dirName); setTimeout(function() { if (!fs.existsSync(savedPathFilename)) { debug(`Cleanup redirect: Could not find ${savedPathFilename}`); return; } let savedPathContent = fs.readFileSync(savedPathFilename, "utf-8"); if ( savedPathContent.indexOf("Browsersync pathPrefix Redirect") === -1 ) { debug( `Cleanup redirect: Found ${savedPathFilename} but it wasn’t an eleventy redirect.` ); return; } fs.unlink(savedPathFilename, err => { if (!err) { debug(`Cleanup redirect: Deleted ${savedPathFilename}`); } }); }, 2000); } } serveRedirect(dirName) { fs.outputFile( this.getRedirectFilename(dirName), ` Browsersync pathPrefix Redirect Go to ${this.config.pathPrefix}` ); } serve(port) { // only load on serve—this is pretty expensive const browserSync = require("browser-sync"); this.server = browserSync.create(); let pathPrefix = this.getPathPrefix(); if (this.savedPathPrefix && pathPrefix !== this.savedPathPrefix) { let redirectFilename = this.getRedirectFilename(this.savedPathPrefix); if (!fs.existsSync(redirectFilename)) { debug( `Redirecting BrowserSync from ${ this.savedPathPrefix } to ${pathPrefix}` ); this.serveRedirect(this.savedPathPrefix); } else { debug( `Config updated with a new pathPrefix. Tried to set up a transparent redirect but found a template already existing at ${redirectFilename}. You’ll have to navigate manually.` ); } } let redirectDirName = this.getRedirectDirOverride(); // has a pathPrefix, add a /index.html template to redirect to /pathPrefix/ if (redirectDirName) { this.serveRedirect(redirectDirName); } this.cleanupRedirect(this.savedPathPrefix); this.server.init(this.getOptions(port)); // this needs to happen after `.getOptions` this.savedPathPrefix = pathPrefix; } close() { if (this.server) { this.server.exit(); } } reload(path, isInclude) { if (this.server) { if (this.getPathPrefix() !== this.savedPathPrefix) { this.server.exit(); this.serve(); } else { // Is a CSS input file and is not in the includes folder // TODO check output path file extension of this template (not input path) if (path && path.split(".").pop() === "css" && !isInclude) { this.server.reload("*.css"); } else { this.server.reload(); } } } } } module.exports = EleventyServe;