diff --git a/src/Page.js b/src/Page.js index 9b4162e855..4e108b29c4 100644 --- a/src/Page.js +++ b/src/Page.js @@ -566,11 +566,9 @@ class Page { * Produces expressive layouts by inserting page data into pre-specified layout * @param pageData a page with its front matter collected * @param {FileConfig} fileConfig + * @param {Parser} markbinder instance from the caller, for adding the seen sources. */ - generateExpressiveLayout(pageData, fileConfig) { - const markbinder = new MarkBind(); - const template = {}; - template[LAYOUT_PAGE_BODY_VARIABLE] = pageData; + generateExpressiveLayout(pageData, fileConfig, markbinder) { const { layout } = this.frontMatter; const layoutPath = path.join(this.rootPath, LAYOUT_FOLDER_PATH, layout); const layoutPagePath = path.join(layoutPath, LAYOUT_PAGE); @@ -578,30 +576,22 @@ class Page { if (!fs.existsSync(layoutPagePath)) { return pageData; } - const layoutFileConfig = { - ...fileConfig, - cwf: layoutPagePath, - additionalVariables: {}, - }; - - layoutFileConfig.additionalVariables[LAYOUT_PAGE_BODY_VARIABLE] = `{{${LAYOUT_PAGE_BODY_VARIABLE}}}`; // Set expressive layout file as an includedFile this.includedFiles.add(layoutPagePath); - return new Promise((resolve, reject) => { - // Retrieve Expressive Layouts page and insert content - fs.readFileAsync(layoutPagePath, 'utf8') - .then(result => markbinder.includeData(layoutPagePath, result, layoutFileConfig)) - .then(result => njUtil.renderRaw(result, template, {}, false)) - .then((result) => { - this.collectIncludedFiles(markbinder.getDynamicIncludeSrc()); - this.collectIncludedFiles(markbinder.getStaticIncludeSrc()); - this.collectIncludedFiles(markbinder.getMissingIncludeSrc()); - return result; - }) - .then(resolve) - .catch(reject); - }); + return fs.readFileAsync(layoutPagePath, 'utf8') + // Include file but with altered cwf (the layout page) + // Also render MAIN_CONTENT_BODY back to itself + .then(result => markbinder.includeFile(layoutPagePath, result, { + ...fileConfig, + cwf: layoutPagePath, + }, { + [LAYOUT_PAGE_BODY_VARIABLE]: `{{${LAYOUT_PAGE_BODY_VARIABLE}}}`, + })) + // Insert content + .then(result => njUtil.renderRaw(result, { + [LAYOUT_PAGE_BODY_VARIABLE]: pageData, + }, {}, false)); } @@ -924,70 +914,67 @@ class Page { headerIdMap: this.headerIdMap, fixedHeader: this.fixedHeader, }; - return new Promise((resolve, reject) => { - markbinder.includeFile(this.sourcePath, fileConfig) - .then((result) => { - this.collectFrontMatter(result); - return Page.removeFrontMatter(result); - }) - .then(result => this.generateExpressiveLayout(result, fileConfig)) - .then(result => Page.removePageHeaderAndFooter(result)) - .then(result => Page.addContentWrapper(result)) - .then(result => this.collectPluginSources(result)) - .then(result => this.preRender(result)) - .then(result => this.insertSiteNav((result))) - .then(result => this.insertHeaderFile(result, fileConfig)) - .then(result => this.insertFooterFile(result)) - .then(result => Page.insertTemporaryStyles(result)) - .then(result => markbinder.resolveBaseUrl(result, fileConfig)) - .then(result => markbinder.render(result, this.sourcePath, fileConfig)) - .then(result => this.postRender(result)) - .then(result => this.collectPluginsAssets(result)) - .then(result => markbinder.processDynamicResources(this.sourcePath, result)) - .then(result => MarkBind.unwrapIncludeSrc(result)) - .then((result) => { - this.content = result; - - const { relative } = urlUtils.getParentSiteAbsoluteAndRelativePaths(this.sourcePath, this.rootPath, - this.baseUrlMap); - const baseUrl = relative ? `${this.baseUrl}/${utils.ensurePosix(relative)}` : this.baseUrl; - const hostBaseUrl = this.baseUrl; - - this.addLayoutFiles(); - this.collectHeadFiles(baseUrl, hostBaseUrl); - - this.content = njUtil.renderString(this.content, { - baseUrl, - hostBaseUrl, - }); + return fs.readFileAsync(this.sourcePath, 'utf-8') + .then(result => markbinder.includeFile(this.sourcePath, result, fileConfig)) + .then((result) => { + this.collectFrontMatter(result); + return Page.removeFrontMatter(result); + }) + .then(result => this.generateExpressiveLayout(result, fileConfig, markbinder)) + .then(result => Page.removePageHeaderAndFooter(result)) + .then(result => Page.addContentWrapper(result)) + .then(result => this.collectPluginSources(result)) + .then(result => this.preRender(result)) + .then(result => this.insertSiteNav((result))) + .then(result => this.insertHeaderFile(result, fileConfig)) + .then(result => this.insertFooterFile(result)) + .then(result => Page.insertTemporaryStyles(result)) + .then(result => markbinder.resolveBaseUrl(result, fileConfig)) + .then(result => markbinder.render(result, this.sourcePath, fileConfig)) + .then(result => this.postRender(result)) + .then(result => this.collectPluginsAssets(result)) + .then(result => markbinder.processDynamicResources(this.sourcePath, result)) + .then(result => MarkBind.unwrapIncludeSrc(result)) + .then((result) => { + this.content = result; + + const { relative } = urlUtils.getParentSiteAbsoluteAndRelativePaths(this.sourcePath, this.rootPath, + this.baseUrlMap); + const baseUrl = relative ? `${this.baseUrl}/${utils.ensurePosix(relative)}` : this.baseUrl; + const hostBaseUrl = this.baseUrl; + + this.addLayoutFiles(); + this.collectHeadFiles(baseUrl, hostBaseUrl); + + this.content = njUtil.renderString(this.content, { + baseUrl, + hostBaseUrl, + }); - this.collectAllPageSections(); - this.buildPageNav(); + this.collectAllPageSections(); + this.buildPageNav(); - const renderedTemplate = this.template.render(this.prepareTemplateData()); - const outputTemplateHTML = this.disableHtmlBeautify - ? renderedTemplate - : htmlBeautify(renderedTemplate, Page.htmlBeautifyOptions); + const renderedTemplate = this.template.render(this.prepareTemplateData()); + const outputTemplateHTML = this.disableHtmlBeautify + ? renderedTemplate + : htmlBeautify(renderedTemplate, Page.htmlBeautifyOptions); - return fs.outputFileAsync(this.resultPath, outputTemplateHTML); - }) - .then(() => { - const resolvingFiles = []; - Page.unique(markbinder.getDynamicIncludeSrc()).forEach((source) => { - if (!FsUtil.isUrl(source.to)) { - resolvingFiles.push(this.resolveDependency(source, builtFiles)); - } - }); - return Promise.all(resolvingFiles); - }) - .then(() => { - this.collectIncludedFiles(markbinder.getDynamicIncludeSrc()); - this.collectIncludedFiles(markbinder.getStaticIncludeSrc()); - this.collectIncludedFiles(markbinder.getMissingIncludeSrc()); - }) - .then(resolve) - .catch(reject); - }); + return fs.outputFileAsync(this.resultPath, outputTemplateHTML); + }) + .then(() => { + const resolvingFiles = []; + Page.unique(markbinder.getDynamicIncludeSrc()).forEach((source) => { + if (!FsUtil.isUrl(source.to)) { + resolvingFiles.push(this.resolveDependency(source, builtFiles)); + } + }); + return Promise.all(resolvingFiles); + }) + .then(() => { + this.collectIncludedFiles(markbinder.getDynamicIncludeSrc()); + this.collectIncludedFiles(markbinder.getStaticIncludeSrc()); + this.collectIncludedFiles(markbinder.getMissingIncludeSrc()); + }); } /** @@ -1231,12 +1218,14 @@ class Page { * so that we only recursively rebuild the file's included content */ const markbinder = new MarkBind(); - return markbinder.includeFile(dependency.to, { - baseUrlMap: this.baseUrlMap, - userDefinedVariablesMap: this.userDefinedVariablesMap, - rootPath: this.rootPath, - cwf: file, - }).then(result => Page.removeFrontMatter(result)) + return fs.readFileAsync(dependency.to, 'utf-8') + .then(result => markbinder.includeFile(dependency.to, result, { + baseUrlMap: this.baseUrlMap, + userDefinedVariablesMap: this.userDefinedVariablesMap, + rootPath: this.rootPath, + cwf: file, + })) + .then(result => Page.removeFrontMatter(result)) .then(result => this.collectPluginSources(result)) .then(result => this.preRender(result)) .then(result => markbinder.resolveBaseUrl(result, { diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index 6ef6bda11c..902185601a 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -373,7 +373,7 @@ class Parser { } } - includeFile(file, config) { + includeFile(file, content, config, additionalVariables = {}) { const context = {}; context.cwf = config.cwf || file; // current working file context.callStack = []; @@ -396,87 +396,6 @@ class Parser { }); resolve(cheerio.html(nodes)); }); - const parser = new htmlparser.Parser(handler, { - xmlMode: true, - decodeEntities: true, - }); - let actualFilePath = file; - if (!utils.fileExists(file)) { - const boilerplateFilePath = urlUtils.calculateBoilerplateFilePath(path.basename(file), file, config); - if (utils.fileExists(boilerplateFilePath)) { - actualFilePath = boilerplateFilePath; - } - } - // Read files - fs.readFile(actualFilePath, 'utf-8', (err, data) => { - if (err) { - reject(err); - return; - } - const parentSitePath = urlUtils.getParentSiteAbsolutePath(file, config.rootPath, config.baseUrlMap); - const userDefinedVariables = config.userDefinedVariablesMap[parentSitePath]; - const pageVariables = Parser.extractPageVariables(file, data, userDefinedVariables, {}); - let fileContent = njUtil.renderRaw(data, { - ...pageVariables, - ...userDefinedVariables, - }, { - path: actualFilePath, - }); - this._extractInnerVariables(fileContent, context, config); - const innerVariables = this.getImportedVariableMap(context.cwf); - fileContent = njUtil.renderRaw(fileContent, { - ...userDefinedVariables, ...innerVariables, - }); - const fileExt = utils.getExt(file); - if (utils.isMarkdownFileExt(fileExt)) { - context.source = 'md'; - parser.parseComplete(fileContent.toString()); - } else if (fileExt === 'html') { - context.source = 'html'; - parser.parseComplete(fileContent); - } else { - const error = new Error(`Unsupported File Extension: '${fileExt}'`); - reject(error); - } - }); - }); - } - - includeData(file, pageData, config) { - const context = {}; - context.cwf = config.cwf || file; // current working file - - return new Promise((resolve, reject) => { - let actualFilePath = file; - if (!utils.fileExists(file)) { - const boilerplateFilePath = urlUtils.calculateBoilerplateFilePath(path.basename(file), file, config); - if (utils.fileExists(boilerplateFilePath)) { - actualFilePath = boilerplateFilePath; - } - } - - const currentContext = context; - currentContext.callStack = []; - - const handler = new htmlparser.DomHandler((error, dom) => { - if (error) { - reject(error); - return; - } - const nodes = dom.map((d) => { - let processed; - try { - processed = componentPreprocessor.preProcessComponent(d, currentContext, config, this); - } catch (err) { - err.message += `\nError while preprocessing '${actualFilePath}'`; - logger.error(err); - processed = utils.createErrorNode(d, err); - } - return processed; - }); - resolve(cheerio.html(nodes)); - }); - const parser = new htmlparser.Parser(handler, { xmlMode: true, decodeEntities: true, @@ -484,29 +403,26 @@ class Parser { const parentSitePath = urlUtils.getParentSiteAbsolutePath(file, config.rootPath, config.baseUrlMap); const userDefinedVariables = config.userDefinedVariablesMap[parentSitePath]; - const { additionalVariables } = config; - const pageVariables = Parser.extractPageVariables(actualFilePath, pageData, userDefinedVariables, {}); - - let fileContent = njUtil.renderRaw(pageData, { + const pageVariables = Parser.extractPageVariables(file, content, userDefinedVariables, {}); + let fileContent = njUtil.renderRaw(content, { ...pageVariables, ...userDefinedVariables, ...additionalVariables, - }, { - path: actualFilePath, }); - this._extractInnerVariables(fileContent, currentContext, config); - const innerVariables = this.getImportedVariableMap(currentContext.cwf); + this._extractInnerVariables(fileContent, context, config); + const innerVariables = this.getImportedVariableMap(context.cwf); fileContent = njUtil.renderRaw(fileContent, { ...userDefinedVariables, ...additionalVariables, ...innerVariables, }); - const fileExt = utils.getExt(actualFilePath); + + const fileExt = utils.getExt(file); if (utils.isMarkdownFileExt(fileExt)) { - currentContext.source = 'md'; + context.source = 'md'; parser.parseComplete(fileContent.toString()); } else if (fileExt === 'html') { - currentContext.source = 'html'; + context.source = 'html'; parser.parseComplete(fileContent); } else { const error = new Error(`Unsupported File Extension: '${fileExt}'`); diff --git a/test/unit/parser.test.js b/test/unit/parser.test.js index b51e04a5d0..d03f261a47 100644 --- a/test/unit/parser.test.js +++ b/test/unit/parser.test.js @@ -37,7 +37,7 @@ test('includeFile replaces with
', async () => { const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -77,7 +77,7 @@ test('includeFile replaces with
', async const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -113,7 +113,7 @@ test('includeFile replaces with empty < const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -152,7 +152,7 @@ test('includeFile replaces with
', async const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -196,7 +196,7 @@ test('includeFile replaces with inline const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -236,7 +236,7 @@ test('includeFile replaces with trimmed c const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -283,7 +283,7 @@ test('includeFile replaces with error with
const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -362,7 +362,7 @@ test('includeFile replaces with const baseUrlMap = new Set([ROOT_PATH]); const markbinder = new MarkBind(); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP, @@ -420,7 +420,7 @@ test('includeFile detects cyclic references for static cyclic includes', async ( expect(e.message).toEqual(expectedErrorMessage); }, }); - const result = await markbinder.includeFile(indexPath, { + const result = await markbinder.includeFile(indexPath, index, { baseUrlMap, rootPath: ROOT_PATH, userDefinedVariablesMap: DEFAULT_USER_DEFINED_VARIABLES_MAP,