mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-12 04:25:31 +08:00
a7686bb35b
Summary: Grunt is hardcoded to use paths relative to wherever the Gruntfile is located. Unfortunately it also expects the grunt packages to be siblings of that gruntfile. We can get around this by changing the relative base path, but then the cwd is different for each tasks. This is okay as long as we use absolute paths for various files in each of our tasks. This updates our grunt tasks to use absolute paths Test Plan: `npm run build-client` Reviewers: spang, halla, jerm, juan Reviewed By: juan Differential Revision: https://phab.nylas.com/D3987
232 lines
6.9 KiB
JavaScript
232 lines
6.9 KiB
JavaScript
const path = require('path');
|
|
const Handlebars = require('handlebars');
|
|
const marked = require('meta-marked');
|
|
const fs = require('fs-plus');
|
|
const _ = require('underscore');
|
|
|
|
marked.setOptions({
|
|
highlight(code) {
|
|
return require('highlight.js').highlightAuto(code).value;
|
|
}
|
|
});
|
|
|
|
let standardClassURLRoot = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/';
|
|
|
|
let standardClasses = [
|
|
'string',
|
|
'object',
|
|
'array',
|
|
'function',
|
|
'number',
|
|
'date',
|
|
'error',
|
|
'boolean',
|
|
'null',
|
|
'undefined',
|
|
'json',
|
|
'set',
|
|
'map',
|
|
'typeerror',
|
|
'syntaxerror',
|
|
'referenceerror',
|
|
'rangeerror'
|
|
];
|
|
|
|
let thirdPartyClasses = {
|
|
'react.component': 'https://facebook.github.io/react/docs/component-api.html',
|
|
'promise': 'https://github.com/petkaantonov/bluebird/blob/master/API.md',
|
|
'range': 'https://developer.mozilla.org/en-US/docs/Web/API/Range',
|
|
'selection': 'https://developer.mozilla.org/en-US/docs/Web/API/Selection',
|
|
'node': 'https://developer.mozilla.org/en-US/docs/Web/API/Node',
|
|
};
|
|
|
|
module.exports = function(grunt) {
|
|
|
|
let {cp, mkdir, rm} = grunt.config('taskHelpers');
|
|
|
|
let relativePathForClass = classname => classname+'.html';
|
|
|
|
let outputPathFor = function(relativePath) {
|
|
let classDocsOutputDir = grunt.config.get('classDocsOutputDir');
|
|
return path.join(classDocsOutputDir, relativePath);
|
|
};
|
|
|
|
var processFields = function(json, fields, tasks) {
|
|
let val;
|
|
if (fields == null) { fields = []; }
|
|
if (tasks == null) { tasks = []; }
|
|
if (json instanceof Array) {
|
|
return (() => {
|
|
let result = [];
|
|
for (val of Array.from(json)) {
|
|
result.push(processFields(val, fields, tasks));
|
|
}
|
|
return result;
|
|
})();
|
|
} else {
|
|
return (() => {
|
|
let result1 = [];
|
|
for (let key in json) {
|
|
val = json[key];
|
|
let item;
|
|
if (Array.from(fields).includes(key)) {
|
|
for (let task of Array.from(tasks)) {
|
|
val = task(val);
|
|
}
|
|
json[key] = val;
|
|
}
|
|
if (_.isObject(val)) {
|
|
item = processFields(val, fields, tasks);
|
|
}
|
|
result1.push(item);
|
|
}
|
|
return result1;
|
|
})();
|
|
}
|
|
};
|
|
|
|
return grunt.registerTask('docs-render', 'Builds html from the API docs', function() {
|
|
|
|
let documentation, filename, html, match, meta, name, result, section, val;
|
|
let classDocsOutputDir = grunt.config.get('classDocsOutputDir');
|
|
|
|
// Parse API reference Markdown
|
|
|
|
let classes = [];
|
|
let apiJsonPath = path.join(classDocsOutputDir, 'api.json');
|
|
let apiJSON = JSON.parse(grunt.file.read(apiJsonPath));
|
|
|
|
|
|
for (var classname in apiJSON.classes) {
|
|
// Parse a "@Section" out of the description if one is present
|
|
let contents = apiJSON.classes[classname];
|
|
let sectionRegex = /Section: ?([\w ]*)(?:$|\n)/;
|
|
section = 'General';
|
|
|
|
match = sectionRegex.exec(contents.description);
|
|
if (match) {
|
|
contents.description = contents.description.replace(match[0], '');
|
|
section = match[1].trim();
|
|
}
|
|
|
|
// Replace superClass "React" with "React.Component". The Coffeescript Lexer
|
|
// is so bad.
|
|
if (contents.superClass === "React") {
|
|
contents.superClass = "React.Component";
|
|
}
|
|
|
|
classes.push({
|
|
name: classname,
|
|
documentation: contents,
|
|
section
|
|
});
|
|
}
|
|
|
|
|
|
// Build Sidebar metadata we can hand off to each of the templates to
|
|
// generate the sidebar
|
|
let sidebar = {};
|
|
for (var i = 0; i < classes.length; i++) {
|
|
var current_class = classes[i];
|
|
console.log(current_class.name + ' ' + current_class.section)
|
|
|
|
if (!(current_class.section in sidebar)) {
|
|
sidebar[current_class.section] = []
|
|
}
|
|
sidebar[current_class.section].push(current_class.name)
|
|
}
|
|
|
|
|
|
// Prepare to render by loading handlebars partials
|
|
let templatesPath = path.resolve(grunt.config('buildDir'), 'docs_templates');
|
|
grunt.file.recurse(templatesPath, function(abspath, root, subdir, filename) {
|
|
if ((filename[0] === '_') && (path.extname(filename) === '.html')) {
|
|
return Handlebars.registerPartial(filename, grunt.file.read(abspath));
|
|
}
|
|
});
|
|
|
|
// Render Helpers
|
|
|
|
let knownClassnames = {};
|
|
for (classname in apiJSON.classes) {
|
|
val = apiJSON.classes[classname];
|
|
knownClassnames[classname.toLowerCase()] = val;
|
|
}
|
|
|
|
|
|
let expandTypeReferences = function(val) {
|
|
let refRegex = /{([\w.]*)}/g;
|
|
while ((match = refRegex.exec(val)) !== null) {
|
|
let term = match[1].toLowerCase();
|
|
let label = match[1];
|
|
let url = false;
|
|
if (Array.from(standardClasses).includes(term)) {
|
|
url = standardClassURLRoot+term;
|
|
} else if (thirdPartyClasses[term]) {
|
|
url = thirdPartyClasses[term];
|
|
} else if (knownClassnames[term]) {
|
|
url = relativePathForClass(knownClassnames[term].name);
|
|
grunt.log.ok("Found: " + term)
|
|
} else {
|
|
console.warn(`Cannot find class named ${term}`);
|
|
}
|
|
|
|
if (url) {
|
|
val = val.replace(match[0], `<a href='${url}'>${label}</a>`);
|
|
}
|
|
}
|
|
return val;
|
|
};
|
|
|
|
let expandFuncReferences = function(val) {
|
|
let refRegex = /{([\w]*)?::([\w]*)}/g;
|
|
while ((match = refRegex.exec(val)) !== null) {
|
|
var label;
|
|
let [text, a, b] = Array.from(match);
|
|
let url = false;
|
|
if (a && b) {
|
|
url = `${relativePathForClass(a)}#${b}`;
|
|
label = `${a}::${b}`;
|
|
} else {
|
|
url = `#${b}`;
|
|
label = `${b}`;
|
|
}
|
|
if (url) {
|
|
val = val.replace(text, `<a href='${url}'>${label}</a>`);
|
|
}
|
|
}
|
|
return val;
|
|
};
|
|
|
|
// DEBUG Render sidebar json
|
|
// grunt.file.write(outputPathFor('sidebar.json'), JSON.stringify(sidebar, null, 2));
|
|
|
|
// Render Class Pages
|
|
let classTemplatePath = path.join(templatesPath, 'class.md');
|
|
let classTemplate = Handlebars.compile(grunt.file.read(classTemplatePath));
|
|
|
|
for ({name, documentation, section} of Array.from(classes)) {
|
|
// Recursively process `description` and `type` fields to process markdown,
|
|
// expand references to types, functions and other files.
|
|
processFields(documentation, ['description'], [expandFuncReferences]);
|
|
processFields(documentation, ['type'], [expandTypeReferences]);
|
|
|
|
result = classTemplate({name, documentation, section});
|
|
grunt.file.write(outputPathFor(name + '.md'), result);
|
|
}
|
|
|
|
let sidebarTemplatePath = path.join(templatesPath, 'sidebar.md');
|
|
let sidebarTemplate = Handlebars.compile(grunt.file.read(sidebarTemplatePath));
|
|
|
|
grunt.file.write(outputPathFor('Sidebar.md'),
|
|
sidebarTemplate({sidebar}));
|
|
|
|
|
|
// Remove temp cjsx output
|
|
return fs.removeSync(outputPathFor("temp-cjsx"));
|
|
});
|
|
};
|
|
|
|
function __guard__(value, transform) {
|
|
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
|
|
}
|