mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
397 lines
11 KiB
TypeScript
397 lines
11 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { spawn } from 'child-process-promise';
|
|
import { mapWorkspaceToPackages } from '../release/utils/workspace';
|
|
import { projectRoot } from '../utils';
|
|
import fs from 'fs';
|
|
import glob from 'glob';
|
|
import { join } from 'path';
|
|
import * as yargs from 'yargs';
|
|
|
|
/**
|
|
* Add to devsite files to alert anyone trying to make a documentation fix
|
|
* to the generated files.
|
|
*/
|
|
const GOOGLE3_HEADER = `
|
|
{% comment %}
|
|
DO NOT EDIT THIS FILE!
|
|
This is generated by the JS SDK team, and any local changes will be
|
|
overwritten. Changes should be made in the source code at
|
|
https://github.com/firebase/firebase-js-sdk
|
|
{% endcomment %}
|
|
`;
|
|
|
|
const tmpDir = `${projectRoot}/temp`;
|
|
const EXCLUDED_PACKAGES = [
|
|
'app-compat',
|
|
'util',
|
|
'rules-unit-testing',
|
|
'data-connect'
|
|
];
|
|
|
|
/**
|
|
* When ordering functions, will prioritize these first params at
|
|
* the top, in order.
|
|
*/
|
|
const PREFERRED_PARAMS = [
|
|
'app',
|
|
'analyticsInstance',
|
|
'appCheckInstance',
|
|
'db',
|
|
'firestore',
|
|
'functionsInstance',
|
|
'installations',
|
|
'messaging',
|
|
'performance',
|
|
'remoteConfig',
|
|
'storage',
|
|
'ai'
|
|
];
|
|
|
|
let authApiReportOriginal: string;
|
|
let authApiConfigOriginal: string;
|
|
|
|
yargs
|
|
.command(
|
|
'$0',
|
|
'generate standard reference docs',
|
|
{
|
|
skipBuild: {
|
|
type: 'boolean',
|
|
default: false
|
|
}
|
|
},
|
|
_argv => generateDocs(/* forDevsite */ false, _argv.skipBuild)
|
|
)
|
|
.command(
|
|
'devsite',
|
|
'generate reference docs for devsite',
|
|
{
|
|
skipBuild: {
|
|
type: 'boolean',
|
|
default: false
|
|
}
|
|
},
|
|
_argv => generateDocs(/* forDevsite */ true, _argv.skipBuild)
|
|
)
|
|
.command('toc', 'generate devsite TOC', {}, _argv => generateToc())
|
|
.option('skipBuild', {
|
|
describe:
|
|
'Skip yarn build and api-report - only do this if you have already generated the most up to date .api.json files',
|
|
type: 'boolean'
|
|
})
|
|
.demandCommand()
|
|
.help().argv;
|
|
|
|
process.on('exit', cleanup);
|
|
process.on('SIGINT', cleanup);
|
|
|
|
function cleanup() {
|
|
try {
|
|
// Restore original auth api-extractor.json contents.
|
|
if (authApiReportOriginal) {
|
|
console.log(`Restoring original auth api-extractor.json contents.`);
|
|
fs.writeFileSync(
|
|
`${projectRoot}/packages/auth/api-extractor.json`,
|
|
authApiConfigOriginal
|
|
);
|
|
}
|
|
// Restore original auth.api.md
|
|
if (authApiConfigOriginal) {
|
|
console.log(`Restoring original auth.api.md contents.`);
|
|
fs.writeFileSync(
|
|
`${projectRoot}/common/api-review/auth.api.md`,
|
|
authApiReportOriginal
|
|
);
|
|
}
|
|
for (const excludedPackage of EXCLUDED_PACKAGES) {
|
|
if (fs.existsSync(`${projectRoot}/temp/${excludedPackage}.skip`)) {
|
|
console.log(
|
|
`Restoring json files for excluded package: ${excludedPackage}.`
|
|
);
|
|
fs.renameSync(
|
|
`${projectRoot}/temp/${excludedPackage}.skip`,
|
|
`${projectRoot}/temp/${excludedPackage}.api.json`
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(
|
|
'Error cleaning up files on exit - ' +
|
|
'check for temp modifications to md and json files.'
|
|
);
|
|
console.error(e);
|
|
}
|
|
}
|
|
|
|
async function generateToc() {
|
|
console.log(`Temporarily renaming excluded packages' json files.`);
|
|
for (const excludedPackage of EXCLUDED_PACKAGES) {
|
|
if (fs.existsSync(`${projectRoot}/temp/${excludedPackage}.api.json`)) {
|
|
fs.renameSync(
|
|
`${projectRoot}/temp/${excludedPackage}.api.json`,
|
|
`${projectRoot}/temp/${excludedPackage}.skip`
|
|
);
|
|
}
|
|
}
|
|
try {
|
|
await spawn(
|
|
'yarn',
|
|
[
|
|
'api-documenter-devsite',
|
|
'toc',
|
|
'--input',
|
|
'temp',
|
|
'--output',
|
|
'docs-devsite',
|
|
'-p',
|
|
'/docs/reference/js',
|
|
'-j'
|
|
],
|
|
{ stdio: 'inherit' }
|
|
);
|
|
// The toc on the devsite must be named _toc.yaml
|
|
await spawn('mv', ['docs-devsite/toc.yaml', 'docs-devsite/_toc.yaml'], {
|
|
stdio: 'inherit'
|
|
});
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
// create *.api.json files
|
|
async function generateDocs(
|
|
forDevsite: boolean = false,
|
|
skipBuild: boolean = false
|
|
) {
|
|
const outputFolder = forDevsite ? 'docs-devsite' : 'docs';
|
|
const command = forDevsite ? 'api-documenter-devsite' : 'api-documenter';
|
|
|
|
console.log(`Temporarily modifying auth api-extractor.json for docgen.`);
|
|
// Use a special d.ts file for auth for doc gen only.
|
|
authApiConfigOriginal = fs.readFileSync(
|
|
`${projectRoot}/packages/auth/api-extractor.json`,
|
|
'utf8'
|
|
);
|
|
// Save original auth.md as well.
|
|
authApiReportOriginal = fs.readFileSync(
|
|
`${projectRoot}/common/api-review/auth.api.md`,
|
|
'utf8'
|
|
);
|
|
const authApiConfigModified = authApiConfigOriginal.replace(
|
|
`"mainEntryPointFilePath": "<projectFolder>/dist/esm/index.d.ts"`,
|
|
`"mainEntryPointFilePath": "<projectFolder>/dist/esm/index.doc.d.ts"`
|
|
);
|
|
|
|
/**
|
|
* Exclude compat as this script is only for modular docgen.
|
|
*/
|
|
const packageDirectories = (
|
|
await mapWorkspaceToPackages([`${projectRoot}/packages/*`])
|
|
).filter(path => fs.existsSync(path) && !path.includes('-compat'));
|
|
|
|
try {
|
|
console.log(`Deleting old temp directories in each package.`);
|
|
for (const dir of packageDirectories) {
|
|
fs.rmSync(join(dir, 'temp'), { recursive: true, force: true });
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
`${projectRoot}/packages/auth/api-extractor.json`,
|
|
authApiConfigModified
|
|
);
|
|
|
|
if (skipBuild) {
|
|
await spawn('yarn', ['api-report'], {
|
|
stdio: 'inherit'
|
|
});
|
|
} else {
|
|
// api-report is run as part of every build
|
|
await spawn(
|
|
'yarn',
|
|
[
|
|
'lerna',
|
|
'run',
|
|
'--scope',
|
|
'@firebase/*',
|
|
'--ignore',
|
|
'@firebase/*-compat',
|
|
'build'
|
|
],
|
|
{
|
|
stdio: 'inherit'
|
|
}
|
|
);
|
|
}
|
|
} finally {
|
|
console.log(`Restoring original auth api-extractor.json contents.`);
|
|
// Restore original auth api-extractor.json contents.
|
|
fs.writeFileSync(
|
|
`${projectRoot}/packages/auth/api-extractor.json`,
|
|
authApiConfigOriginal
|
|
);
|
|
// Restore original auth.api.md
|
|
fs.writeFileSync(
|
|
`${projectRoot}/common/api-review/auth.api.md`,
|
|
authApiReportOriginal
|
|
);
|
|
}
|
|
|
|
if (fs.existsSync(tmpDir)) {
|
|
console.log(`Removing old json temp dir: ${tmpDir}.`);
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
}
|
|
|
|
fs.mkdirSync(tmpDir);
|
|
|
|
const apiJsonDirectories = packageDirectories
|
|
.map(path => join(path, 'temp'))
|
|
.filter(path => fs.existsSync(path));
|
|
for (const dir of apiJsonDirectories) {
|
|
const paths = await new Promise<string[]>(resolve =>
|
|
glob(`${dir}/*.api.json`, (err, paths) => {
|
|
if (err) throw err;
|
|
resolve(paths);
|
|
})
|
|
);
|
|
|
|
if (paths.length === 0) {
|
|
throw Error(`*.api.json file is missing in ${dir}`);
|
|
}
|
|
|
|
// there will be only 1 api.json file
|
|
const fileName = paths[0].split('/').pop();
|
|
fs.copyFileSync(paths[0], `${tmpDir}/${fileName}`);
|
|
}
|
|
|
|
await spawn(
|
|
'yarn',
|
|
[
|
|
command,
|
|
'markdown',
|
|
'--input',
|
|
'temp',
|
|
'--output',
|
|
outputFolder,
|
|
'--project',
|
|
'js',
|
|
'--sort-functions',
|
|
PREFERRED_PARAMS.join(',')
|
|
],
|
|
{ stdio: 'inherit' }
|
|
);
|
|
|
|
if (forDevsite) {
|
|
const mdFiles = fs.readdirSync(join(projectRoot, outputFolder));
|
|
for (const mdFile of mdFiles) {
|
|
const fullPath = join(projectRoot, outputFolder, mdFile);
|
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
fs.writeFileSync(
|
|
fullPath,
|
|
content.replace('\n# ', `\n${GOOGLE3_HEADER}\n# `)
|
|
);
|
|
}
|
|
}
|
|
|
|
await moveRulesUnitTestingDocs(outputFolder, command);
|
|
await removeExcludedDocs(outputFolder);
|
|
await removeExcludedPackageEntries(outputFolder);
|
|
}
|
|
|
|
/**
|
|
* Remove markdown files generated for excluded packages.
|
|
*/
|
|
async function removeExcludedDocs(mainDocsFolder: string) {
|
|
console.log('Removing excluded docs from', EXCLUDED_PACKAGES.join(', '));
|
|
for (const excludedPackage of EXCLUDED_PACKAGES) {
|
|
const excludedMdFiles = await new Promise<string[]>(resolve =>
|
|
glob(`${mainDocsFolder}/${excludedPackage}.*`, (err, paths) => {
|
|
if (err) throw err;
|
|
resolve(paths);
|
|
})
|
|
);
|
|
for (const excludedMdFile of excludedMdFiles) {
|
|
fs.unlinkSync(excludedMdFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove lines from index.md that link to excluded packages.
|
|
*/
|
|
async function removeExcludedPackageEntries(mainDocsFolder: string) {
|
|
console.log(`Removing ${EXCLUDED_PACKAGES.join(', ')} from index page.`);
|
|
const indexText = fs.readFileSync(`${mainDocsFolder}/index.md`, 'utf-8');
|
|
const indexTextLines = indexText.split('\n');
|
|
const newIndexTextLines = indexTextLines.filter(line => {
|
|
for (const excludedPackage of EXCLUDED_PACKAGES) {
|
|
if (line.includes(`[@firebase/${excludedPackage}]`)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
fs.writeFileSync(
|
|
`${mainDocsFolder}/index.md`,
|
|
newIndexTextLines.join('\n'),
|
|
'utf-8'
|
|
);
|
|
}
|
|
|
|
// Create a docs-rut folder and move rules-unit-testing docs into it.
|
|
async function moveRulesUnitTestingDocs(
|
|
mainDocsFolder: string,
|
|
command: string
|
|
) {
|
|
const rulesOutputFolder = `${projectRoot}/docs-rut`;
|
|
|
|
console.log('Moving RUT docs to their own folder:', rulesOutputFolder);
|
|
|
|
if (!fs.existsSync(rulesOutputFolder)) {
|
|
fs.mkdirSync(rulesOutputFolder);
|
|
}
|
|
|
|
const rulesDocPaths = await new Promise<string[]>(resolve =>
|
|
glob(`${mainDocsFolder}/rules-unit-testing.*`, (err, paths) => {
|
|
if (err) throw err;
|
|
resolve(paths);
|
|
})
|
|
);
|
|
// Move rules-unit-testing docs into the new folder.
|
|
// These paths also need to be adjusted to point to a sibling directory.
|
|
for (const sourcePath of rulesDocPaths) {
|
|
let destinationPath = sourcePath.replace(mainDocsFolder, rulesOutputFolder);
|
|
|
|
const originalText = fs.readFileSync(sourcePath, 'utf-8');
|
|
const jsReferencePath = '/docs/reference/js';
|
|
let alteredPathText = originalText.replace(
|
|
/\.\/database/g,
|
|
`${jsReferencePath}/database`
|
|
);
|
|
alteredPathText = alteredPathText.replace(
|
|
/\.\/storage/g,
|
|
`${jsReferencePath}/storage`
|
|
);
|
|
alteredPathText = alteredPathText.replace(
|
|
/\.\/firestore/g,
|
|
`${jsReferencePath}/firestore`
|
|
);
|
|
fs.writeFileSync(destinationPath, alteredPathText);
|
|
}
|
|
}
|