Skip to content

Commit

Permalink
Remove expressive layouts code duplication (#1225)
Browse files Browse the repository at this point in the history
  • Loading branch information
ang-zeyu committed May 21, 2020
1 parent 245eabb commit 3f4aa36
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 195 deletions.
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) {
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

0 comments on commit 3f4aa36

Please sign in to comment.