Skip to content

Commit

Permalink
Merge pull request MarkBind#31 from rachx/live-reload
Browse files Browse the repository at this point in the history
Ignore updates to source files while live-reloading is in progress MarkBind#155
  • Loading branch information
rachx authored Mar 18, 2018
2 parents 5d01c70 + 5f10462 commit 737b146
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 41 deletions.
14 changes: 9 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ program
logger.info(`Reload for file add: ${filePath}`);
Promise.resolve('').then(() => {
if (fsUtil.isSourceFile(filePath)) {
return site.buildSourceFiles();
return site.rebuildAffectedSourceFiles(filePath);
}
return site.buildAsset(filePath);
}).catch((err) => {
Expand All @@ -124,7 +124,7 @@ program
logger.info(`Reload for file change: ${filePath}`);
Promise.resolve('').then(() => {
if (fsUtil.isSourceFile(filePath)) {
return site.rebuildSourceFiles(filePath);
return site.rebuildAffectedSourceFiles(filePath);
}
return site.buildAsset(filePath);
}).catch((err) => {
Expand All @@ -136,15 +136,15 @@ program
logger.info(`Reload for file deletion: ${filePath}`);
Promise.resolve('').then(() => {
if (fsUtil.isSourceFile(filePath)) {
return site.rebuildSourceFiles(filePath);
return site.rebuildAffectedSourceFiles(filePath);
}
return site.removeAsset(filePath);
}).catch((err) => {
logger.error(err.message);
});
};

// server conifg
// server config
const serverConfig = {
open: options.open,
logLevel: 0,
Expand All @@ -163,7 +163,11 @@ program
})
.then(() => {
const watcher = chokidar.watch(rootFolder, {
ignored: [outputFolder, /(^|[/\\])\../],
ignored: [
outputFolder,
/(^|[/\\])\../,
x => x.endsWith('___jb_tmp___'), x => x.endsWith('___jb_old___'), // IDE temp files
],
ignoreInitial: true,
});
watcher
Expand Down
2 changes: 2 additions & 0 deletions lib/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ Page.prototype.generate = function (builtFiles) {
this.collectIncludedFiles(markbinder.getDynamicIncludeSrc());
this.collectIncludedFiles(markbinder.getStaticIncludeSrc());
this.collectIncludedFiles(markbinder.getBoilerplateIncludeSrc());
this.collectIncludedFiles(markbinder.getMissingIncludeSrc());
})
.then(resolve)
.catch(reject);
Expand Down Expand Up @@ -194,6 +195,7 @@ Page.prototype.resolveDependency = function (dependency, builtFiles) {
this.collectIncludedFiles(markbinder.getDynamicIncludeSrc());
this.collectIncludedFiles(markbinder.getStaticIncludeSrc());
this.collectIncludedFiles(markbinder.getBoilerplateIncludeSrc());
this.collectIncludedFiles(markbinder.getMissingIncludeSrc());
})
.then(resolve)
.catch(reject);
Expand Down
85 changes: 49 additions & 36 deletions lib/Site.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* eslint-disable no-underscore-dangle */

const cheerio = require('cheerio');
const delay = require('./util/delay');
const path = require('path');
const ignore = require('ignore');
const ejs = require('ejs');
Expand All @@ -7,6 +10,7 @@ const walkSync = require('walk-sync');
const Promise = require('bluebird');
const ghpages = require('gh-pages');
const logger = require('./util/logger');
const _ = require('lodash');

const Page = require('./Page');

Expand Down Expand Up @@ -259,6 +263,7 @@ Site.prototype.buildSourceFiles = function () {
return new Promise((resolve, reject) => {
this.generatePages()
.then(() => fs.removeAsync(this.tempPath))
.then(() => logger.info('Pages built'))
.then(resolve)
.catch((error) => {
// if error, remove the site and temp folders
Expand All @@ -267,13 +272,10 @@ Site.prototype.buildSourceFiles = function () {
});
};

/**
* Rebuild pages that are affected by change in filePath
* @param filePath path of file changed
*/
Site.prototype.rebuildSourceFiles = function (filePath) {
Site.prototype._rebuildAffectedSourceFiles = function (filePaths) {
const uniquePaths = _.uniq(filePaths);
return new Promise((resolve, reject) => {
this.regenerateAffectedPages(filePath)
this.regenerateAffectedPages(uniquePaths)
.then(() => fs.removeAsync(this.tempPath))
.then(resolve)
.catch((error) => {
Expand All @@ -283,37 +285,48 @@ Site.prototype.rebuildSourceFiles = function (filePath) {
});
};

Site.prototype.buildAsset = function (filePath) {
return new Promise((resolve, reject) => {
// if the file is an ignored file, resolve
// Else, copy it to its destination
const ignoreConfig = this.siteConfig.ignore || [];
const fileRelative = path.relative(this.rootPath, filePath);
const fileIgnore = ignore().add(ignoreConfig);
if (fileIgnore.filter([fileRelative]).length === 0) {
resolve();
} else {
fs.copyAsync(filePath, path.join(this.outputPath, fileRelative))
.then(resolve)
.catch((error) => {
rejectHandler(reject, error, []); // assets won't affect deletion
});
}
});
/**
* Rebuild pages that are affected by changes in filePaths
* @param filePaths a single path or an array of paths corresponding to the files that have changed
*/
Site.prototype.rebuildAffectedSourceFiles
= delay(Site.prototype._rebuildAffectedSourceFiles, 1000);

Site.prototype._buildMultipleAssets = function (filePaths) {
const uniquePaths = _.uniq(filePaths);
const ignoreConfig = this.siteConfig.ignore || [];
const fileIgnore = ignore().add(ignoreConfig);
const fileRelativePaths = uniquePaths.map(filePath => path.relative(this.rootPath, filePath));
const copyAssets = fileIgnore.filter(fileRelativePaths)
.map(asset => fs.copyAsync(path.join(this.rootPath, asset), path.join(this.outputPath, asset)));
return Promise.all(copyAssets)
.then(() => logger.info('Assets built'));
};

Site.prototype.removeAsset = function (filePath) {
return new Promise((resolve, reject) => {
const fileRelative = path.relative(this.rootPath, filePath);
const fileToRemove = path.join(this.outputPath, fileRelative);
fs.removeAsync(fileToRemove)
.then(resolve)
.catch((error) => {
rejectHandler(reject, error, []); // assets won't affect deletion
});
});
/**
* Build/copy assets that are specified in filePaths
* @param filePaths a single path or an array of paths corresponding to the assets to build
*/
Site.prototype.buildAsset
= delay(Site.prototype._buildMultipleAssets, 1000);

Site.prototype._removeMultipleAssets = function (filePaths) {
const uniquePaths = _.uniq(filePaths);
const fileRelativePaths = uniquePaths.map(filePath => path.relative(this.rootPath, filePath));
const filesToRemove = fileRelativePaths.map(
fileRelativePath => path.join(this.outputPath, fileRelativePath));
const removeFiles = filesToRemove.map(asset => fs.removeAsync(asset));
return Promise.all(removeFiles)
.then(() => logger.info('Assets removed'));
};

/**
* Remove assets that are specified in filePaths
* @param filePaths a single path or an array of paths corresponding to the assets to remove
*/
Site.prototype.removeAsset
= delay(Site.prototype._removeMultipleAssets, 1000);

Site.prototype.buildAssets = function () {
return new Promise((resolve, reject) => {
const ignoreConfig = this.siteConfig.ignore || [];
Expand Down Expand Up @@ -362,13 +375,13 @@ Site.prototype.generatePages = function () {
/**
* Re-renders pages that contain the original file path
* as the source file or as a static/dynamic included file
* @param filePath path of file changed
* @param filePaths array of paths corresponding to files that have changed
*/
Site.prototype.regenerateAffectedPages = function (filePath) {
Site.prototype.regenerateAffectedPages = function (filePaths) {
const builtFiles = {};
const processingFiles = [];
this.pageModels.forEach((page) => {
if (page.includedFiles[filePath]) {
if (filePaths.some(filePath => page.includedFiles[filePath])) {
processingFiles.push(page.generate(builtFiles));
}
});
Expand Down
38 changes: 38 additions & 0 deletions lib/util/delay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const Promise = require('bluebird');

/**
* Creates a function that delays invoking `func` until after `wait` milliseconds have elapsed
* and the running `func` has resolved/rejected.
* @param func the promise-returning function to delay,
* func should take in a single array
* @param wait the number of milliseconds to delay
* @returns delayedFunc that takes in a single argument (either an array or a single value)
*/
module.exports = function delay(func, wait) {
let context;
let pendingArgs = [];
let waitingPromise = null;
let runningPromise = Promise.resolve();

return function (arg) {
context = this;
if (Array.isArray(arg)) {
pendingArgs = pendingArgs.concat(arg);
} else {
pendingArgs.push(arg);
}

if (waitingPromise === null) {
waitingPromise = Promise.all([Promise.delay(wait), runningPromise])
.finally(() => {
runningPromise = waitingPromise || Promise.resolve();
waitingPromise = null;
const funcPromise = func.apply(context, [pendingArgs]);
pendingArgs = [];
return funcPromise;
});
}

return waitingPromise;
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"ignore": "^3.2.0",
"js-beautify": "^1.6.12",
"live-server": "^1.2.0",
"lodash": "^4.17.5",
"markbind": "^1.3.0",
"nunjucks": "^3.0.0",
"path-is-inside": "^1.0.2",
Expand Down

0 comments on commit 737b146

Please sign in to comment.