diff --git a/index.js b/index.js index 01342c54b4..4a8095a977 100755 --- a/index.js +++ b/index.js @@ -2,26 +2,25 @@ // Entry file for Markbind project +const chokidar = require('chokidar'); const clear = require('clear'); const fs = require('fs-extra-promise'); -const path = require('path'); -const Promise = require('bluebird'); -const program = require('commander'); const htmlBeautify = require('js-beautify').html; const liveServer = require('live-server'); -const chokidar = require('chokidar'); +const path = require('path'); +const program = require('commander'); +const Promise = require('bluebird'); const _ = {}; _.isBoolean = require('lodash/isBoolean'); -const logger = require('./lib/util/logger'); const fsUtil = require('./lib/util/fsUtil'); -const Site = require('./lib/Site'); +const logger = require('./lib/util/logger'); const MarkBind = require('./lib/markbind/lib/parser'); - -const CLI_VERSION = require('./package.json').version; +const Site = require('./lib/Site'); const ACCEPTED_COMMANDS = ['version', 'include', 'render', 'init', 'build', 'serve', 'deploy']; +const CLI_VERSION = require('./package.json').version; const markbinder = new MarkBind(); diff --git a/lib/Page.js b/lib/Page.js index e70b4f4363..49b307c032 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -1,14 +1,14 @@ +const cheerio = require('cheerio'); +const fm = require('fastmatter'); +const fs = require('fs-extra-promise'); const htmlBeautify = require('js-beautify').html; const nunjucks = require('nunjucks'); -const fs = require('fs-extra-promise'); const path = require('path'); -const Promise = require('bluebird'); -const logger = require('./util/logger'); -const FsUtil = require('./util/fsUtil'); const pathIsInside = require('path-is-inside'); -const cheerio = require('cheerio'); -const fm = require('fastmatter'); +const Promise = require('bluebird'); +const FsUtil = require('./util/fsUtil'); +const logger = require('./util/logger'); const MarkBind = require('./markbind/lib/parser'); const FRONT_MATTER_FENCE = '---'; @@ -18,31 +18,33 @@ cheerio.prototype.options.xmlMode = true; // Enable xml mode for self-closing ta cheerio.prototype.options.decodeEntities = false; // Don't escape HTML entities function Page(pageConfig) { + this.asset = pageConfig.asset; + this.baseUrl = pageConfig.baseUrl; + this.baseUrlMap = pageConfig.baseUrlMap; this.content = pageConfig.content || ''; - this.title = pageConfig.title || ''; this.rootPath = pageConfig.rootPath; + this.src = pageConfig.src; + this.template = pageConfig.pageTemplate; + this.title = pageConfig.title || ''; + this.titlePrefix = pageConfig.titlePrefix; + this.userDefinedVariablesMap = pageConfig.userDefinedVariablesMap; + // the source file for rendering this page this.sourcePath = pageConfig.sourcePath; // the temp path for writing intermediate result this.tempPath = pageConfig.tempPath; // the output path of this page this.resultPath = pageConfig.resultPath; - this.template = pageConfig.pageTemplate; - this.baseUrl = pageConfig.baseUrl; - this.asset = pageConfig.asset; - this.baseUrlMap = pageConfig.baseUrlMap; - this.userDefinedVariablesMap = pageConfig.userDefinedVariablesMap; - this.src = pageConfig.src; - this.titlePrefix = pageConfig.titlePrefix; - this.includedFiles = {}; + this.frontMatter = {}; + this.includedFiles = {}; } /** * Util Methods */ -function baseUrlFromRoot(filePath, root, lookUp) { +function calculateNewBaseUrl(filePath, root, lookUp) { function calculate(file, result) { if (file === root || !pathIsInside(file, root)) { return undefined; @@ -65,10 +67,10 @@ function unique(array) { Page.prototype.prepareTemplateData = function () { return { + asset: this.asset, baseUrl: this.baseUrl, content: this.content, title: this.title, - asset: this.asset, }; }; @@ -156,7 +158,7 @@ Page.prototype.generate = function (builtFiles) { .then((result) => { this.content = htmlBeautify(result, { indent_size: 2 }); - const newBaseUrl = baseUrlFromRoot(this.sourcePath, this.rootPath, this.baseUrlMap); + const newBaseUrl = calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap); const baseUrl = newBaseUrl ? `${this.baseUrl}/${newBaseUrl}` : this.baseUrl; const hostBaseUrl = this.baseUrl; @@ -238,7 +240,7 @@ Page.prototype.resolveDependency = function (dependency, builtFiles) { })) .then((result) => { // resolve the site base url here - const newBaseUrl = baseUrlFromRoot(file, this.rootPath, this.baseUrlMap); + const newBaseUrl = calculateNewBaseUrl(file, this.rootPath, this.baseUrlMap); const baseUrl = newBaseUrl ? `${this.baseUrl}/${newBaseUrl}` : this.baseUrl; const hostBaseUrl = this.baseUrl; diff --git a/lib/Site.js b/lib/Site.js index c9ee89dcda..dd8c9f8211 100644 --- a/lib/Site.js +++ b/lib/Site.js @@ -1,31 +1,32 @@ /* 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'); const fs = require('fs-extra-promise'); -const walkSync = require('walk-sync'); -const Promise = require('bluebird'); const ghpages = require('gh-pages'); -const logger = require('./util/logger'); +const ignore = require('ignore'); +const path = require('path'); +const Promise = require('bluebird'); +const walkSync = require('walk-sync'); const _ = {}; _.unionWith = require('lodash/unionWith'); _.uniq = require('lodash/uniq'); +const delay = require('./util/delay'); +const logger = require('./util/logger'); const Page = require('./Page'); -const TEMP_FOLDER_NAME = '.temp'; -const SITE_CONFIG_NAME = 'site.json'; -const SITE_DATA_NAME = 'siteData.json'; -const INDEX_MARKDOWN_FILE = 'index.md'; -const PAGE_TEMPLATE_NAME = 'page.ejs'; const BOILERPLATE_FOLDER_NAME = '_markbind/boilerplates'; const SITE_ASSET_FOLDER_NAME = 'asset'; +const TEMP_FOLDER_NAME = '.temp'; const TEMPLATE_ROOT_FOLDER_NAME = 'template'; const TEMPLATE_SITE_ASSET_FOLDER_NAME = 'markbind'; + +const INDEX_MARKDOWN_FILE = 'index.md'; +const PAGE_TEMPLATE_NAME = 'page.ejs'; +const SITE_CONFIG_NAME = 'site.json'; +const SITE_DATA_NAME = 'siteData.json'; const USER_VARIABLES_PATH = '_markbind/variables.md'; const SITE_CONFIG_DEFAULT = { @@ -183,18 +184,18 @@ Site.prototype.createPageData = function (config) { const tempPath = path.join(this.tempPath, config.pageSrc); const resultPath = path.join(this.outputPath, setExtension(config.pageSrc, '.html')); return new Page({ + baseUrl: config.baseUrl, + baseUrlMap: this.baseUrlMap, content: '', - title: config.title || '', + pageTemplate: config.pageTemplate, rootPath: this.rootPath, + src: config.pageSrc, + title: config.title || '', + titlePrefix: config.titlePrefix, + userDefinedVariablesMap: this.userDefinedVariablesMap, sourcePath, tempPath, resultPath, - baseUrl: config.baseUrl, - pageTemplate: config.pageTemplate, - baseUrlMap: this.baseUrlMap, - userDefinedVariablesMap: this.userDefinedVariablesMap, - src: config.pageSrc, - titlePrefix: config.titlePrefix, asset: { bootstrap: path.relative(path.dirname(resultPath), path.join(this.siteAssetsDestPath, 'css', 'bootstrap.min.css')), @@ -202,12 +203,12 @@ Site.prototype.createPageData = function (config) { path.join(this.siteAssetsDestPath, 'css', 'github.min.css')), markbind: path.relative(path.dirname(resultPath), path.join(this.siteAssetsDestPath, 'css', 'markbind.css')), + setup: path.relative(path.dirname(resultPath), + path.join(this.siteAssetsDestPath, 'js', 'setup.js')), vue: path.relative(path.dirname(resultPath), path.join(this.siteAssetsDestPath, 'js', 'vue.min.js')), vueStrap: path.relative(path.dirname(resultPath), path.join(this.siteAssetsDestPath, 'js', 'vue-strap.min.js')), - setup: path.relative(path.dirname(resultPath), - path.join(this.siteAssetsDestPath, 'js', 'setup.js')), }, }); }; diff --git a/lib/markbind/lib/parser.js b/lib/markbind/lib/parser.js index 8156aed73b..5caa640185 100644 --- a/lib/markbind/lib/parser.js +++ b/lib/markbind/lib/parser.js @@ -1,7 +1,13 @@ /* eslint-disable no-underscore-dangle */ +const cheerio = require('cheerio'); +const fs = require('fs'); const htmlparser = require('htmlparser2'); -const md = require('./markdown-it'); +const nunjucks = require('nunjucks'); +const path = require('path'); +const pathIsInside = require('path-is-inside'); +const Promise = require('bluebird'); +const url = require('url'); const _ = {}; _.clone = require('lodash/clone'); @@ -11,21 +17,12 @@ _.isArray = require('lodash/isArray'); _.isEmpty = require('lodash/isEmpty'); _.pick = require('lodash/pick'); -const Promise = require('bluebird'); - -const cheerio = require('cheerio'); +const md = require('./markdown-it'); +const utils = require('./utils'); cheerio.prototype.options.xmlMode = true; // Enable xml mode for self-closing tag cheerio.prototype.options.decodeEntities = false; // Don't escape HTML entities -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'); - const ATTRIB_INCLUDE_PATH = 'include-path'; const ATTRIB_CWF = 'cwf'; @@ -38,9 +35,9 @@ const BOILERPLATE_FOLDER_NAME = '_markbind/boilerplates'; /** * @throws Will throw an error if a non-absolute path or path outside the root is given */ -function calculateNewBaseUrl(filePath, root, lookUp) { +function calculateNewBaseUrls(filePath, root, lookUp) { if (!path.isAbsolute(filePath)) { - throw new Error(`Non-absolute path given to calculateNewBaseUrl: '${filePath}'`); + throw new Error(`Non-absolute path given to calculateNewBaseUrls: '${filePath}'`); } if (!pathIsInside(filePath, root)) { throw new Error(`Path given '${filePath}' is not in root '${root}'`); @@ -62,8 +59,8 @@ function calculateNewBaseUrl(filePath, root, lookUp) { } function calculateBoilerplateFilePath(pathInBoilerplates, asIfAt, config) { - const fileBase = calculateNewBaseUrl(asIfAt, config.rootPath, config.baseUrlMap).relative; - return path.resolve(fileBase, BOILERPLATE_FOLDER_NAME, pathInBoilerplates); + const fileBase = calculateNewBaseUrls(asIfAt, config.rootPath, config.baseUrlMap); + return path.resolve(fileBase.relative, BOILERPLATE_FOLDER_NAME, pathInBoilerplates); } function createErrorNode(element, error) { @@ -186,8 +183,8 @@ Parser.prototype._preprocess = function (node, context, config) { } let fileContent = self._fileCache[actualFilePath]; // cache the file contents to save some I/O - const fileBase = path.resolve(calculateNewBaseUrl(filePath, config.rootPath, config.baseUrlMap).relative); - const userDefinedVariables = config.userDefinedVariablesMap[fileBase]; + const fileBase = calculateNewBaseUrls(filePath, config.rootPath, config.baseUrlMap); + const userDefinedVariables = config.userDefinedVariablesMap[path.resolve(fileBase.relative)]; fileContent = nunjucks.renderString(fileContent, userDefinedVariables); delete element.attribs.boilerplate; delete element.attribs.src; @@ -408,8 +405,8 @@ Parser.prototype.includeFile = function (file, config) { reject(err); return; } - const fileBase = path.resolve(calculateNewBaseUrl(file, config.rootPath, config.baseUrlMap).relative); - const userDefinedVariables = config.userDefinedVariablesMap[fileBase]; + const fileBase = calculateNewBaseUrls(file, config.rootPath, config.baseUrlMap); + const userDefinedVariables = config.userDefinedVariablesMap[path.resolve(fileBase.relative)]; const fileContent = nunjucks.renderString(data, userDefinedVariables); const fileExt = utils.getExtName(file); if (fileExt === 'md') { @@ -540,7 +537,7 @@ Parser.prototype._rebaseReference = function (node, foundBase) { // rebase current element if (element.attribs[ATTRIB_CWF]) { const filePath = element.attribs[ATTRIB_CWF]; - let newBase = calculateNewBaseUrl(filePath, this.rootPath, this.baseUrlMap); + let newBase = calculateNewBaseUrls(filePath, this.rootPath, this.baseUrlMap); if (newBase) { const { relative, parent } = newBase; // eslint-disable-next-line no-param-reassign @@ -553,7 +550,7 @@ Parser.prototype._rebaseReference = function (node, foundBase) { newBase = childrenBase[bases[0]]; const { children } = element; if (children) { - const currentBase = calculateNewBaseUrl(element.attribs[ATTRIB_CWF], this.rootPath, this.baseUrlMap); + const currentBase = calculateNewBaseUrls(element.attribs[ATTRIB_CWF], this.rootPath, this.baseUrlMap); if (currentBase) { if (currentBase.relative !== newBase) { cheerio.prototype.options.xmlMode = false; @@ -581,13 +578,13 @@ Parser.prototype._rebaseReferenceForStaticIncludes = function (pageData, element } const filePath = element.attribs[ATTRIB_INCLUDE_PATH]; - const fileBase = calculateNewBaseUrl(filePath, config.rootPath, config.baseUrlMap); + const fileBase = calculateNewBaseUrls(filePath, config.rootPath, config.baseUrlMap); if (!fileBase.relative) { return pageData; } const currentPath = element.attribs[ATTRIB_CWF]; - const currentBase = calculateNewBaseUrl(currentPath, config.rootPath, config.baseUrlMap); + const currentBase = calculateNewBaseUrls(currentPath, config.rootPath, config.baseUrlMap); if (currentBase.relative === fileBase.relative) { return pageData; } diff --git a/lib/util/delay.js b/lib/util/delay.js index 4b6c476761..5b5f733d72 100644 --- a/lib/util/delay.js +++ b/lib/util/delay.js @@ -11,8 +11,8 @@ const Promise = require('bluebird'); module.exports = function delay(func, wait) { let context; let pendingArgs = []; - let waitingPromise = null; let runningPromise = Promise.resolve(); + let waitingPromise = null; return function (arg) { context = this; diff --git a/lib/util/fsUtil.js b/lib/util/fsUtil.js index a6977bd93b..d900df4954 100644 --- a/lib/util/fsUtil.js +++ b/lib/util/fsUtil.js @@ -1,31 +1,31 @@ const path = require('path'); module.exports = { + isHtml: filePath => path.extname(filePath) === '.html', + isMarkdown: filePath => path.extname(filePath) === '.md', + isSourceFile(filePath) { + return this.isMarkdown(filePath) || this.isHtml(filePath); + }, + isInRoot: (root, fileName) => { let normalizedRoot = path.normalize(root); - const normalizedFilename = path.normalize(fileName); if (normalizedRoot === '.') { return true; } if (normalizedRoot[normalizedRoot.length - 1] !== path.sep) { normalizedRoot += path.sep; } + const normalizedFilename = path.normalize(fileName); return (normalizedFilename.substr(0, normalizedRoot.length) === normalizedRoot); }, - setExtension: (normalizedFilename, ext) => path.join( - path.dirname(normalizedFilename), - path.basename(normalizedFilename, path.extname(normalizedFilename)) + ext, - ), - isUrl: (unknownPath) => { const r = new RegExp('^(?:[a-z]+:)?//', 'i'); return r.test(unknownPath); }, - isMarkdown: filePath => path.extname(filePath) === '.md', - isHtml: filePath => path.extname(filePath) === '.html', - isSourceFile(filePath) { - return this.isMarkdown(filePath) || this.isHtml(filePath); - }, + setExtension: (normalizedFilename, ext) => path.join( + path.dirname(normalizedFilename), + path.basename(normalizedFilename, path.extname(normalizedFilename)) + ext, + ), }; diff --git a/lib/util/logger.js b/lib/util/logger.js index 209a0e6cbd..64a1e8a54d 100644 --- a/lib/util/logger.js +++ b/lib/util/logger.js @@ -9,14 +9,14 @@ 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', + filename: 'markbind-%DATE%.log', + handleExceptions: true, + humanReadableUnhandledException: true, + level: 'debug', maxFiles: '2d', + showLevel: true, }), ], });