mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
410 lines
11 KiB
JavaScript
410 lines
11 KiB
JavaScript
/**
|
|
* Copyright (c) 2017-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
'use strict';
|
|
|
|
const fetch = require('node-fetch');
|
|
const filepath = require('filepath');
|
|
const fm = require('front-matter');
|
|
const fs = require('fs-extra');
|
|
const glob = require('glob-promise');
|
|
const jsdom = require('jsdom');
|
|
const mkdirp = require('mkdirp');
|
|
const Promise = require('bluebird');
|
|
const shell = require('shelljs');
|
|
|
|
// const convert = require('./convert.js');
|
|
const slugify = require('../core/slugify');
|
|
|
|
const CWD = process.cwd();
|
|
|
|
const AUTODOCS_PREFIX = 'autogen_';
|
|
const MARKDOWN_EXTENSION = 'md';
|
|
|
|
const GIT_USER = 'hramos'; //process.env.GIT_USER;
|
|
const GITHUB_USERNAME = 'facebook'; // process.env.GITHUB_USERNAME;
|
|
const GITHUB_REPONAME = 'react-native'; // process.env.GITHUB_REPONAME;
|
|
const remoteBranch = `https://${GIT_USER}@github.com/${GITHUB_USERNAME}/${GITHUB_REPONAME}.git`;
|
|
|
|
const CHECKOUT_DIR = `${GITHUB_REPONAME}-docs`;
|
|
const BUILD_DIR = 'build';
|
|
const DOCS_DIR = 'versioned_docs';
|
|
const SIDEBAR_DIR = 'versioned_sidebars';
|
|
|
|
const { JSDOM } = jsdom;
|
|
|
|
// Start up a server. Don't forget to close the connection when done.
|
|
// const server = require('./server.js');
|
|
// server.noconvert = true;
|
|
|
|
const argv = require('minimist')(process.argv.slice(2), {
|
|
alias: {
|
|
'c': 'clean'
|
|
},
|
|
default: {
|
|
'autodocs': true
|
|
}
|
|
});
|
|
|
|
function runChecks() {
|
|
if (!shell.which('git')) {
|
|
shell.echo('Sorry, this script requires git');
|
|
shell.exit(1);
|
|
}
|
|
|
|
if (!GIT_USER) {
|
|
shell.echo('GIT_USER undefined.');
|
|
shell.exit(1);
|
|
}
|
|
if (!GITHUB_USERNAME) {
|
|
shell.echo('GITHUB_USERNAME undefined.');
|
|
shell.exit(1);
|
|
}
|
|
if (!GITHUB_REPONAME) {
|
|
shell.echo('GITHUB_REPONAME undefined.');
|
|
shell.exit(1);
|
|
}
|
|
}
|
|
|
|
function cleanFiles() {
|
|
return fs.remove(BUILD_DIR);
|
|
}
|
|
|
|
/**
|
|
* Generates documentation for a given file.
|
|
*
|
|
* @param {*} file
|
|
* @param {*} options
|
|
*/
|
|
function generateAutodocForFile(file, options) {
|
|
if (file.match(/src\/react-native\/js/)) {
|
|
// Ensure we're only processing extracted docs
|
|
console.log(`Skipping ${file}`);
|
|
return;
|
|
}
|
|
|
|
const pathToOutputDir = filepath.create(BUILD_DIR, DOCS_DIR, options.version);
|
|
|
|
console.log(`Processing ${file}`);
|
|
const url = 'http://localhost:8079/' + file
|
|
.replace(/^src/, '')
|
|
.replace(/\.js$/, '.html');
|
|
|
|
return fetch(url)
|
|
.then(response => {
|
|
if(response.ok) {
|
|
return response.text();
|
|
}
|
|
throw new Error('Network response was not ok.');
|
|
})
|
|
.then(body => {
|
|
const dom = new JSDOM(body);
|
|
|
|
|
|
const markdown = generateMarkdownFromDOM(dom);
|
|
const frontmatter = fm(markdown);
|
|
|
|
const pathToOutputFile = pathToOutputDir.append(`${AUTODOCS_PREFIX}${frontmatter.attributes.original_id}.${MARKDOWN_EXTENSION}`);
|
|
|
|
return fs.outputFile(pathToOutputFile.toString(), markdown);
|
|
})
|
|
.catch(error => {
|
|
console.log(error);
|
|
reject(error);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generates Markdown documentation. Uses the convert script to extract docs from source files.
|
|
*
|
|
* STATUS FRIDAY 6:
|
|
* Checking out tags and runnign autodocs for each tag not working out.
|
|
* Lets instead check out thw tags and parse out the docs from the existing html, much easier. No need to re-generate when they already exist.
|
|
* we can use filename as key
|
|
*
|
|
* we can then copy ecisting md files from there. solves a lot ofg issues
|
|
*/
|
|
function generateAutodocs(options = { version: 'next' }) {
|
|
console.log(`Generating Markdown files from JavaScript sources for version ${options.version}.`);
|
|
convert({extractDocs: true});
|
|
let queue = Promise.resolve();
|
|
|
|
return Promise.resolve().then(function() {
|
|
return glob('src/**/*.js');
|
|
}).then(function(files) {
|
|
let p = Promise.resolve();
|
|
files.forEach(function(file) {
|
|
p = p.then(function() {
|
|
return generateAutodocForFile(file, options);
|
|
});
|
|
});
|
|
|
|
return p;
|
|
}).then(function() {
|
|
console.log(`Generated Markdown files from JavaScript sources for version ${options.version}.`);
|
|
}).catch(function(e) {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
// Check out gh-pages branch
|
|
function checkOutDocs() {
|
|
const pathToGitCheckout = filepath.create(BUILD_DIR, CHECKOUT_DIR);
|
|
|
|
let sidebarMetadata = {};
|
|
const p = Promise.resolve();
|
|
return p
|
|
.then(() => {
|
|
return fs.ensureDir(pathToGitCheckout.toString());
|
|
})
|
|
.then(() => {
|
|
shell.cd(CWD);
|
|
shell.cd(BUILD_DIR);
|
|
shell.cd(CHECKOUT_DIR);
|
|
return fs.exists(pathToGitCheckout.append(`.git`).toString())
|
|
}).then(gitCheckoutExists => {
|
|
if (!gitCheckoutExists) {
|
|
shell.exec(`git init`).code !== 0;
|
|
|
|
if (shell.exec(`git remote add origin ${remoteBranch}`).code !== 0) {
|
|
throw new Error('Error: git remote failed');
|
|
}
|
|
}
|
|
return;
|
|
})
|
|
.then(() => {
|
|
// shell.exec(`git config core.sparsecheckout true`).code !== 0;
|
|
// shell.exec(`echo "docs/*" >> .git/info/sparse-checkout`).code !== 0;
|
|
// shell.exec(`echo "Libraries/*" >> .git/info/sparse-checkout`).code !== 0;
|
|
if (shell.exec(`git fetch`).code !== 0) {
|
|
throw new Error('Error: git fetch failed');
|
|
}
|
|
|
|
if (shell.exec(`git checkout gh-pages`).code !== 0) {
|
|
throw new Error('Error: git checkout failed');
|
|
}
|
|
|
|
return glob('releases/**/*.html');
|
|
}).then(files => {
|
|
let seq = Promise.resolve();
|
|
files.forEach(function(file) {
|
|
seq = seq.then(() => {
|
|
return extractMarkdownFromHTMLDocs(file);
|
|
}).then((res) => {
|
|
// console.log(res);
|
|
if (res.markdown === undefined) {
|
|
return;
|
|
}
|
|
const { frontmatter, markdown } = res;
|
|
const version = extractDocVersionFromFilename(file);
|
|
|
|
if (!version) {
|
|
return;
|
|
}
|
|
|
|
const pathToOutputFile = filepath.create(CWD, '..', 'website', DOCS_DIR, version, `${frontmatter.attributes.original_id}.${MARKDOWN_EXTENSION}`);
|
|
|
|
if (sidebarMetadata[version] === undefined) {
|
|
sidebarMetadata[version] = { "docs": { "APIs": [] } };
|
|
}
|
|
|
|
if (frontmatter.attributes.original_id !== "404"
|
|
&& frontmatter.attributes.original_id !== "index"
|
|
&& frontmatter.attributes.original_id !== "help"
|
|
&& frontmatter.attributes.original_id !== "users"
|
|
&& frontmatter.attributes.original_id !== "showcase"
|
|
&& frontmatter.attributes.original_id !== "support"
|
|
&& frontmatter.attributes.original_id !== "versions") {
|
|
sidebarMetadata[version]["docs"]["APIs"].push(frontmatter.attributes.original_id);
|
|
return fs.outputFile(pathToOutputFile.toString(), markdown).then(() => {return;}).catch((e) => {console.error(e)});
|
|
}
|
|
|
|
return;
|
|
});
|
|
});
|
|
return seq;
|
|
}).then(() => {
|
|
console.log(sidebarMetadata)
|
|
const pathToSidebarMetadataFile = filepath.create(CWD, BUILD_DIR, SIDEBAR_DIR, `sidebars-metadata.json`);
|
|
return fs.outputFile(pathToSidebarMetadataFile.toString(), JSON.stringify(sidebarMetadata));
|
|
}).then(() => {
|
|
let seq = Promise.resolve();
|
|
filepath.create(CWD, '..', 'website', SIDEBAR_DIR);
|
|
for (var version in sidebarMetadata) {
|
|
if (sidebarMetadata.hasOwnProperty(version)) {
|
|
// TODO: Problem: this series of promises is just wiritng the same version over and over. Figure out jhow to serialize this correctly.
|
|
seq = seq.then(() => {
|
|
var sidebar = sidebarMetadata[version];
|
|
|
|
const pathToSidebarFile = filepath.create(CWD, '..', 'website', SIDEBAR_DIR, `version-${version}-sidebars.json`);
|
|
console.log(`Writing ${pathToSidebarFile}: ${sidebar}`);
|
|
|
|
return fs.outputFile(pathToSidebarFile.toString(), JSON.stringify(sidebar));
|
|
});
|
|
}
|
|
}
|
|
return seq;
|
|
}).then(() => {
|
|
filepath.create(CWD, BUILD_DIR, SIDEBAR_DIR);
|
|
const versions = Object.keys(sidebarMetadata);
|
|
|
|
const pathToVersionsFile = filepath.create(CWD, BUILD_DIR, `versions.json`);
|
|
return fs.outputFile(pathToVersionsFile.toString(), JSON.stringify(versions.reverse()));
|
|
});
|
|
}
|
|
|
|
function extractDocVersionFromFilename(file) {
|
|
const re = new RegExp('\/([0-9]*[.]?[0-9]*)');
|
|
return file.match(re)[1];
|
|
}
|
|
|
|
|
|
function extractComponentNameFromFilename(file) {
|
|
const re = new RegExp('([A-Za-z-]*).html');
|
|
return file.match(re)[1];
|
|
}
|
|
|
|
|
|
function extractMarkdownFromHTMLDocs(file) {
|
|
if (file.indexOf("404") !== -1) {
|
|
return { };
|
|
}
|
|
// console.log(`Processing ${file}`);
|
|
return JSDOM.fromFile(filepath.create(file).toString())
|
|
.then((dom) => {
|
|
const body = bodyContentFromDOM(dom);
|
|
|
|
if (!body) {
|
|
return {};
|
|
}
|
|
const componentName = extractComponentNameFromFilename(file);
|
|
const version = extractDocVersionFromFilename(file);
|
|
const markdown = generateMarkdown(componentName, body, version);
|
|
const frontmatter = fm(markdown);
|
|
return { frontmatter, markdown };
|
|
})
|
|
}
|
|
|
|
// DOM FORMATTING FUNCS
|
|
|
|
function bodyContentFromDOM(dom) {
|
|
const el = dom.window.document.querySelector('.inner-content');
|
|
if (el) {
|
|
return el.innerHTML;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function componentNameFromDOM(dom) {
|
|
const el = dom.window.document.querySelector('h1');
|
|
if (el) {
|
|
let componentName = el.innerHTML;
|
|
const re = new RegExp('docs/([A-Za-z]*)(.html)');
|
|
const parsedTitle = el.innerHTML.match(re);
|
|
if (parsedTitle) {
|
|
componentName = parsedTitle[1];
|
|
}
|
|
return componentName;
|
|
} else {
|
|
return 'Component';
|
|
}
|
|
}
|
|
|
|
function componentCategoryFromDOM(dom) {
|
|
const el = dom.window.document.querySelector('meta[property="rn:category"]');
|
|
if (el) {
|
|
return el.content;
|
|
} else {
|
|
return 'Components';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a markdown formatted file, including frontmatter.
|
|
*
|
|
* @param {*} dom
|
|
*/
|
|
function generateMarkdownFromDOM(dom) {
|
|
const body = bodyContentFromDOM(dom);
|
|
if (!body) {
|
|
return;
|
|
}
|
|
const componentName = componentNameFromDOM(dom);
|
|
const slug = slugify(componentName);
|
|
const markdown = [
|
|
'---',
|
|
'id: ' + slug,
|
|
'title: ' + componentName,
|
|
'---',
|
|
body
|
|
]
|
|
.filter(function(line) {
|
|
return line;
|
|
})
|
|
.join('\n');
|
|
|
|
return markdown;
|
|
}
|
|
|
|
function generateMarkdown(componentName, body, version) {
|
|
const slug = slugify(componentName);
|
|
|
|
let markdown = [
|
|
'---',
|
|
'id: ' + slug,
|
|
'title: ' + componentName,
|
|
'---',
|
|
body
|
|
];
|
|
if (version) {
|
|
markdown = [
|
|
'---',
|
|
'id: version-' + version + '-' + slug,
|
|
'title: ' + componentName,
|
|
'original_id: ' + slug,
|
|
'---',
|
|
body
|
|
];
|
|
}
|
|
return markdown.filter(function(line) {
|
|
return line;
|
|
}).join('\n');
|
|
}
|
|
|
|
function generateMetatadaFile(categories) {
|
|
const categoriesMetadataFile = `${BUILD_DIR}/sidebar-metadata.json`;
|
|
return fs.outputFile(categoriesMetadataFile, JSON.stringify(categories));
|
|
}
|
|
// END DOM
|
|
|
|
|
|
if (argv.clean) {
|
|
cleanFiles();
|
|
}
|
|
|
|
if (argv.autodocs) {
|
|
runChecks();
|
|
checkOutDocs();
|
|
}
|
|
|
|
/**
|
|
* Check out gh-pages branch, cd releases/
|
|
* For each version,
|
|
* For each HTML,
|
|
* Parse out jsdom
|
|
* Get body contents
|
|
* Generate frontmatter
|
|
* Save into versioned_docs
|
|
* Then finally generate sidebar files for each version
|
|
*
|
|
*/
|
|
|
|
module.exports = generateAutodocs;
|