From 0ec4c20a66c6c25d17366a3dab33853ca8e513d1 Mon Sep 17 00:00:00 2001 From: rachael Date: Thu, 5 Apr 2018 13:23:09 +0800 Subject: [PATCH 1/2] Refactor: extract markbind config --- lib/Page.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/Page.js b/lib/Page.js index afa7c7b602..067237103d 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -78,21 +78,16 @@ Page.prototype.generate = function (builtFiles) { const markbinder = new MarkBind({ errorHandler: logger.error, }); + const fileConfig = { + baseUrlMap: this.baseUrlMap, + rootPath: this.rootPath, + userDefinedVariablesMap: this.userDefinedVariablesMap, + }; return new Promise((resolve, reject) => { - markbinder.includeFile(this.sourcePath, { - baseUrlMap: this.baseUrlMap, - userDefinedVariablesMap: this.userDefinedVariablesMap, - rootPath: this.rootPath, - }) - .then(result => markbinder.resolveBaseUrl(result, { - baseUrlMap: this.baseUrlMap, - rootPath: this.rootPath, - })) + markbinder.includeFile(this.sourcePath, fileConfig) + .then(result => markbinder.resolveBaseUrl(result, fileConfig)) .then(result => fs.outputFileAsync(this.tempPath, result)) - .then(() => markbinder.renderFile(this.tempPath, { - baseUrlMap: this.baseUrlMap, - rootPath: this.rootPath, - })) + .then(() => markbinder.renderFile(this.tempPath, fileConfig)) .then((result) => { this.content = htmlBeautify(result, { indent_size: 2 }); From 2ed174a543c67d4512e67b1e9fed136d67f61bc1 Mon Sep 17 00:00:00 2001 From: rachael Date: Thu, 12 Apr 2018 10:24:21 +0800 Subject: [PATCH 2/2] Add basic log to file --- index.js | 2 ++ lib/Site.js | 21 ++++++++++++++--- lib/markbind/lib/parser.js | 34 ++++++++++++++++++++++++++-- lib/util/logger.js | 46 ++++++++++++++++++++++++++++++++++---- package.json | 4 +++- test/test_site/site.json | 1 + 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index 0037968a58..01342c54b4 100755 --- a/index.js +++ b/index.js @@ -107,6 +107,7 @@ program .option('--no-open', 'do not automatically open the site in browser') .action((root, options) => { const rootFolder = path.resolve(root || process.cwd()); + const logsFolder = path.join(rootFolder, '_markbind/logs'); const outputFolder = path.join(rootFolder, '_site'); const site = new Site(rootFolder, outputFolder); @@ -167,6 +168,7 @@ program .then(() => { const watcher = chokidar.watch(rootFolder, { ignored: [ + logsFolder, outputFolder, /(^|[/\\])\../, x => x.endsWith('___jb_tmp___'), x => x.endsWith('___jb_old___'), // IDE temp files diff --git a/lib/Site.js b/lib/Site.js index edcbf294d6..c919d9a6cf 100644 --- a/lib/Site.js +++ b/lib/Site.js @@ -35,6 +35,7 @@ const SITE_CONFIG_DEFAULT = { }, ], ignore: [ + '_markbind/logs/*', '_site/*', '*.json', '*.md', @@ -51,6 +52,8 @@ const USER_VARIABLES_DEFAULT = '\n' + 'More generally, surround the segment\'s id with double curly braces.\n' + ''; +const GENERATE_SITE_LOGGING_KEY = 'Generate Site'; + function Site(rootPath, outputPath) { this.rootPath = rootPath; this.outputPath = outputPath; @@ -71,6 +74,7 @@ function Site(rootPath, outputPath) { */ function rejectHandler(reject, error, removeFolders) { + logger.warn(error); Promise.all(removeFolders.map(folder => fs.removeAsync(folder))) .then(() => { reject(error); @@ -242,6 +246,7 @@ Site.prototype.collectUserDefinedVariablesMap = function () { Site.prototype.generate = function (baseUrl) { // Create the .tmp folder for storing intermediate results. + logger.profile(GENERATE_SITE_LOGGING_KEY); fs.emptydirSync(this.tempPath); // Clean the output folder; create it if not exist. fs.emptydirSync(this.outputPath); @@ -255,7 +260,8 @@ Site.prototype.generate = function (baseUrl) { .then(resolve) .catch((error) => { rejectHandler(reject, error, [this.tempPath, this.outputPath]); - }); + }) + .finally(() => logger.profile(GENERATE_SITE_LOGGING_KEY)); }); }; @@ -277,6 +283,7 @@ Site.prototype.buildSourceFiles = function () { Site.prototype._rebuildAffectedSourceFiles = function (filePaths) { const uniquePaths = _.uniq(filePaths); + logger.verbose(`Rebuild affected paths: ${uniquePaths}`); return new Promise((resolve, reject) => { this.regenerateAffectedPages(uniquePaths) .then(() => fs.removeAsync(this.tempPath)) @@ -366,7 +373,11 @@ Site.prototype.generatePages = function () { pageTemplate: this.pageTemplate, })); this.pageModels.forEach((page) => { - processingFiles.push(page.generate(builtFiles)); + processingFiles.push(page.generate(builtFiles) + .catch((err) => { + logger.error(err); + return Promise.reject(new Error(`Error while generating ${page.sourcePath}`)); + })); }); return new Promise((resolve, reject) => { Promise.all(processingFiles) @@ -385,7 +396,11 @@ Site.prototype.regenerateAffectedPages = function (filePaths) { const processingFiles = []; this.pageModels.forEach((page) => { if (filePaths.some(filePath => page.includedFiles[filePath])) { - processingFiles.push(page.generate(builtFiles)); + processingFiles.push(page.generate(builtFiles) + .catch((err) => { + logger.error(err); + return Promise.reject(new Error(`Error while generating ${page.sourcePath}`)); + })); } }); diff --git a/lib/markbind/lib/parser.js b/lib/markbind/lib/parser.js index 37923013b9..8156aed73b 100644 --- a/lib/markbind/lib/parser.js +++ b/lib/markbind/lib/parser.js @@ -22,6 +22,7 @@ const utils = require('./utils'); const fs = require('fs'); const path = require('path'); +const pathIsInside = require('path-is-inside'); const url = require('url'); const nunjucks = require('nunjucks'); @@ -34,7 +35,16 @@ const BOILERPLATE_FOLDER_NAME = '_markbind/boilerplates'; * Utils */ +/** +* @throws Will throw an error if a non-absolute path or path outside the root is given +*/ function calculateNewBaseUrl(filePath, root, lookUp) { + if (!path.isAbsolute(filePath)) { + throw new Error(`Non-absolute path given to calculateNewBaseUrl: '${filePath}'`); + } + if (!pathIsInside(filePath, root)) { + throw new Error(`Path given '${filePath}' is not in root '${root}'`); + } function calculate(file, result) { if (file === root) { return { relative: path.relative(root, root), parent: root }; @@ -365,7 +375,17 @@ Parser.prototype.includeFile = function (file, config) { reject(error); return; } - const nodes = dom.map(d => this._preprocess(d, context, config)); + const nodes = dom.map((d) => { + let processed; + try { + processed = this._preprocess(d, context, config); + } catch (err) { + err.message += `\nError while preprocessing '${file}'`; + this._onError(err); + processed = createErrorNode(d, err); + } + return processed; + }); resolve(cheerio.html(nodes)); }); @@ -416,7 +436,17 @@ Parser.prototype.renderFile = function (file, config) { reject(error); return; } - const nodes = dom.map(d => this._parse(d, context, config)); + const nodes = dom.map((d) => { + let parsed; + try { + parsed = this._parse(d, context, config); + } catch (err) { + err.message += `\nError while rendering '${file}'`; + this._onError(err); + parsed = createErrorNode(d, err); + } + return parsed; + }); nodes.forEach((d) => { this._trimNodes(d); }); diff --git a/lib/util/logger.js b/lib/util/logger.js index 8ec48c4f98..209a0e6cbd 100644 --- a/lib/util/logger.js +++ b/lib/util/logger.js @@ -2,11 +2,49 @@ const chalk = require('chalk'); const figlet = require('figlet'); +const winston = require('winston'); +const DailyRotateFile = require('winston-daily-rotate-file'); + +winston.configure({ + exitOnError: false, + transports: [ + new DailyRotateFile({ + level: 'debug', + showLevel: true, + handleExceptions: true, + humanReadableUnhandledException: true, + filename: 'markbind-%DATE%.log', + datePattern: 'YYYY-MM-DD', + dirname: '_markbind/logs', + maxFiles: '2d', + }), + ], +}); module.exports = { - info: text => console.log(chalk.cyan('info: ') + text), - warn: text => console.log(chalk.yellow(`warning: ${text}`)), - error: text => console.log(chalk.red(`error: ${text}`)), - log: text => console.log(text), + error: (text) => { + console.log(chalk.red(`error: ${text}`)); + winston.error(text); + }, + warn: (text) => { + console.log(chalk.yellow(`warning: ${text}`)); + winston.warn(text); + }, + info: (text) => { + console.log(chalk.cyan('info: ') + text); + winston.info(text); + }, + verbose: (text) => { + winston.verbose(text); + }, + debug: (text) => { + winston.debug(text); + }, + log: (text) => { + console.log(text); + }, logo: () => console.log(chalk.green(figlet.textSync('MarkBind', { horizontalLayout: 'full' }))), + profile: (key) => { + winston.profile(key); + }, }; diff --git a/package.json b/package.json index 4d37176735..a80cb04def 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,9 @@ "markdown-it-video": "^0.4.0", "nunjucks": "^3.0.0", "path-is-inside": "^1.0.2", - "walk-sync": "^0.3.1" + "walk-sync": "^0.3.1", + "winston": "^2.4.1", + "winston-daily-rotate-file": "^3.0.1" }, "repository": { "type": "git", diff --git a/test/test_site/site.json b/test/test_site/site.json index 6c7bfde6f7..936508e5a9 100644 --- a/test/test_site/site.json +++ b/test/test_site/site.json @@ -11,6 +11,7 @@ } ], "ignore": [ + "_markbind/logs/*", "_site/*", "site.json", "*.md",