Skip to content
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
173 changes: 81 additions & 92 deletions src/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -566,42 +566,32 @@ 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) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing the parser (markbinder) isn't very clean, but will be dealt with in #1189 in full (making it an instance variable)

const { layout } = this.frontMatter;
const layoutPath = path.join(this.rootPath, LAYOUT_FOLDER_PATH, layout);
const layoutPagePath = path.join(layoutPath, LAYOUT_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));
}


Expand Down Expand Up @@ -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());
});
}

/**
Expand Down Expand Up @@ -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, {
Expand Down
102 changes: 9 additions & 93 deletions src/lib/markbind/src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand All @@ -396,117 +396,33 @@ 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,
});

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}'`);
Expand Down
Loading