Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -30,6 +30,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