Skip to content

Commit ef1aba9

Browse files
authored
Support for multiple API docs repos (#590)
Misc changes to support analyzing multiple repos/versions when generating API docs.
1 parent fd9b1a6 commit ef1aba9

File tree

14 files changed

+215
-101
lines changed

14 files changed

+215
-101
lines changed

.dockerignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ Dockerfile
33
**/node_modules
44
npm-debug.log
55
**/tsconfig.tsbuildinfo
6-
/packages/lit-dev-api/lit/
6+
/packages/lit-dev-api/api-data/*/repo/
77
/packages/lit-dev-tests/

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ packages/lit-dev-content/site/fonts
1111
packages/lit-dev-content/temp
1212
packages/lit-dev-content/samples/js
1313

14-
packages/lit-dev-api/lit/
14+
packages/lit-dev-api/api-data/*/repo/
1515

1616
# We only want to keep '-linux' screenshots which are tested by Github Actions.
1717
packages/lit-dev-tests/src/playwright/**/*.png

README.md

+3-7
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,9 @@ code packages/lit-dev-api/lit/
7979

8080
The `lit` directory is a regular cloned git repo, so you can make changes
8181
directly here, and push PRs from it as normal. It's configured to track the
82-
`main` branch, but is pinned to a particular commit via the `lit.sha` file. To
83-
update the current commit, run:
84-
85-
```sh
86-
cd packages/lit-dev-tools
87-
npm run monorepo:update
88-
```
82+
`main` branch, but is pinned to a particular commit. To update the current
83+
commit, update the `sha` field in
84+
[`packages/lit-dev-tools-cjs/src/api-docs/configs/lit-2.ts`](https://github.com/lit/lit.dev/blob/main/packages/lit-dev-tools-cjs/src/api-docs/configs/lit-2.ts).
8985

9086
### Serve production mode
9187

packages/lit-dev-api/api-data/.gitkeep

Whitespace-only changes.

packages/lit-dev-api/api-data/pages.json packages/lit-dev-api/api-data/lit-2/pages.json

+41-41
Large diffs are not rendered by default.

packages/lit-dev-api/lit.sha

-1
This file was deleted.

packages/lit-dev-api/package.json

+2-8
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@
66
"author": "Google LLC",
77
"license": "BSD-3-Clause",
88
"scripts": {
9-
"build": "(ls lit 2>/dev/null || npm run monorepo:init) && npm run build:generate",
10-
"build:generate": "node ../lit-dev-tools-cjs/lib/generate-api-docs-data.js",
11-
"build:watch": "chokidar 'lit/packages/*/src/**/*.ts' '../lit-dev-tools-cjs/lib/generate-api-docs-data.js' -c 'npm run monorepo:build && npm run build:generate' --initial",
12-
"monorepo:init": "npm run monorepo:clone && npm run monorepo:install && npm run monorepo:build",
13-
"monorepo:clone": "git clone https://github.com/lit/lit --branch main lit && cd lit && git checkout $(cat ../lit.sha)",
14-
"monorepo:update": "cd lit && git checkout main && git pull && git rev-parse HEAD > ../lit.sha && cd .. && npm run monorepo:install && npm run monorepo:build",
15-
"monorepo:install": "cd lit && npm ci && npm run bootstrap",
16-
"monorepo:build": "cd lit && npx lerna run build:ts --scope lit --include-dependencies"
9+
"build": "node ../lit-dev-tools-cjs/lib/api-docs/generate.js",
10+
"build:watch": "chokidar 'lit/packages/*/src/**/*.ts' '../lit-dev-tools-cjs/lib/api-docs/**/*.js' -c 'npm run build' --initial"
1711
},
1812
"dependencies": {
1913
"chokidar-cli": "^2.1.0",

packages/lit-dev-content/.eleventy.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ module.exports = function (eleventyConfig) {
6969
}
7070
eleventyConfig.addPassthroughCopy('api/**/*');
7171

72-
eleventyConfig.addWatchTarget('../lit-dev-api/api-data');
72+
eleventyConfig.addWatchTarget('../lit-dev-api/api-data/*/*.json');
7373

7474
// Placeholder shortcode for TODOs
7575
// Formatting is intentional: outdenting the HTML causes the
@@ -246,7 +246,7 @@ ${content}
246246

247247
// Don't use require() because of Node caching in watch mode.
248248
const apiSymbolMap = JSON.parse(
249-
fsSync.readFileSync('../lit-dev-api/api-data/symbols.json', 'utf8')
249+
fsSync.readFileSync('../lit-dev-api/api-data/lit-2/symbols.json', 'utf8')
250250
);
251251

252252
/**

packages/lit-dev-content/site/_data/api-docs-pages.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = async () =>
1818
'..',
1919
'lit-dev-api',
2020
'api-data',
21+
'lit-2',
2122
'pages.json'
2223
),
2324
'utf8'

packages/lit-dev-tools-cjs/src/api-docs/configs/lit-2.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import * as pathlib from 'path';
99
import type {ApiDocsConfig} from '../types.js';
1010

1111
const root = pathlib.resolve(__dirname, '..', '..', '..', '..', '..');
12-
const workDir = pathlib.join(root, 'packages', 'lit-dev-api');
13-
const gitDir = pathlib.join(workDir, 'lit');
12+
const dataDir = pathlib.join(root, 'packages', 'lit-dev-api', 'api-data');
13+
const workDir = pathlib.join(dataDir, 'lit-2');
14+
const gitDir = pathlib.join(workDir, 'repo');
1415
const litDir = pathlib.join(gitDir, 'packages', 'lit');
1516
const srcDir = pathlib.join(litDir, 'src');
16-
const outDir = pathlib.join(workDir, 'api-data');
1717

1818
/**
1919
* lit.dev API docs configuration for Lit 2.x
@@ -23,10 +23,25 @@ export const lit2Config: ApiDocsConfig = {
2323
commit: 'f8ee010bc515e4bb319e98408d38ef3d971cc08b',
2424
gitDir,
2525
tsConfigPath: pathlib.join(litDir, 'tsconfig.json'),
26-
pagesOutPath: pathlib.resolve(outDir, 'pages.json'),
27-
symbolsOutPath: pathlib.resolve(outDir, 'symbols.json'),
26+
pagesOutPath: pathlib.resolve(workDir, 'pages.json'),
27+
symbolsOutPath: pathlib.resolve(workDir, 'symbols.json'),
2828
typedocRoot: pathlib.join(root, 'packages'),
2929

30+
extraSetupCommands: [
31+
{cmd: 'npm', args: ['run', 'bootstrap']},
32+
{
33+
cmd: 'npx',
34+
args: [
35+
'lerna',
36+
'run',
37+
'build:ts',
38+
'--scope',
39+
'lit',
40+
'--include-dependencies',
41+
],
42+
},
43+
],
44+
3045
entrypointModules: [
3146
pathlib.join(srcDir, 'async-directive.ts'),
3247
pathlib.join(srcDir, 'decorators.ts'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
import * as typedoc from 'typedoc';
8+
import * as fs from 'fs/promises';
9+
import * as pathlib from 'path';
10+
import {execFile} from 'child_process';
11+
import {promisify} from 'util';
12+
import {ApiDocsTransformer} from './transformer.js';
13+
import {lit2Config} from './configs/lit-2.js';
14+
15+
import type {ApiDocsConfig} from './types.js';
16+
17+
const execFileAsync = promisify(execFile);
18+
19+
const configs = [lit2Config];
20+
21+
/**
22+
* Check whether the given file path exists.
23+
*/
24+
const fileExists = async (path: string): Promise<boolean> => {
25+
try {
26+
await fs.stat(path);
27+
return true;
28+
} catch (err) {
29+
if ((err as {code?: string}).code !== 'ENOENT') {
30+
throw err;
31+
}
32+
}
33+
return false;
34+
};
35+
36+
/**
37+
* Return the SHA of the given commit reference.
38+
*/
39+
const shaOfReference = async (
40+
gitDir: string,
41+
reference: string
42+
): Promise<string> => {
43+
const {stdout} = await execFileAsync(
44+
'git',
45+
['show', '-s', '--format=%H', reference],
46+
{
47+
cwd: gitDir,
48+
}
49+
);
50+
return stdout.trim();
51+
};
52+
53+
/**
54+
* Clone the given Git repo URL at the given commit into the given directory. If
55+
* the directory already exists, do nothing.
56+
*/
57+
const cloneIfNeeded = async (repo: string, commit: string, dir: string) => {
58+
if (await fileExists(dir)) {
59+
console.log(`${dir} already exists, skipping git clone`);
60+
const expectedSha = await shaOfReference(dir, commit);
61+
const actualSha = await shaOfReference(dir, 'HEAD');
62+
if (actualSha !== expectedSha) {
63+
throw new Error(
64+
`Git repo ${dir} is at commit ${actualSha}, but is configured for ${commit}. ` +
65+
`Update the config, or delete ${dir} and re-run this script to fix.`
66+
);
67+
}
68+
return;
69+
}
70+
console.log(`cloning git repo ${repo} to ${dir}`);
71+
await execFileAsync('git', ['clone', repo, dir]);
72+
console.log(`checking out commit ${commit}`);
73+
await execFileAsync('git', ['checkout', commit], {cwd: dir});
74+
};
75+
76+
/**
77+
* Run NPM install and other given setup commands. If a node_modules/ directory
78+
* already exists in the given directory, do nothing.
79+
*/
80+
const setupIfNeeded = async (
81+
dir: string,
82+
extraCommands?: Array<{cmd: string; args: string[]}>
83+
) => {
84+
if (await fileExists(pathlib.join(dir, 'node_modules'))) {
85+
console.log(`${dir}/node_modules already exists, skipping setup`);
86+
return;
87+
}
88+
console.log(`running npm ci in ${dir}`);
89+
await execFileAsync('npm', ['ci'], {cwd: dir});
90+
for (const {cmd, args} of extraCommands ?? []) {
91+
console.log(`running ${cmd} ${args.join(' ')} in ${dir}`);
92+
await execFileAsync(cmd, args, {cwd: dir});
93+
}
94+
};
95+
96+
const analyze = async (config: ApiDocsConfig) => {
97+
await cloneIfNeeded(config.repo, config.commit, config.gitDir);
98+
await setupIfNeeded(config.gitDir, config.extraSetupCommands);
99+
100+
const app = new typedoc.Application();
101+
app.options.addReader(new typedoc.TSConfigReader());
102+
app.bootstrap({
103+
tsconfig: config.tsConfigPath,
104+
entryPoints: config.entrypointModules,
105+
});
106+
const root = app.convert();
107+
if (!root) {
108+
throw new Error('TypeDoc.Application.convert() returned undefined');
109+
}
110+
111+
const json = await app.serializer.projectToObject(root);
112+
const transformer = new ApiDocsTransformer(json, config);
113+
const {pages, symbolMap} = await transformer.transform();
114+
115+
await fs.mkdir(pathlib.dirname(config.pagesOutPath), {recursive: true});
116+
await fs.writeFile(
117+
config.pagesOutPath,
118+
JSON.stringify(pages, null, 2),
119+
'utf8'
120+
);
121+
console.log(`Wrote ${config.pagesOutPath}`);
122+
123+
await fs.mkdir(pathlib.dirname(config.symbolsOutPath), {recursive: true});
124+
await fs.writeFile(
125+
config.symbolsOutPath,
126+
JSON.stringify(symbolMap, null, 2),
127+
'utf8'
128+
);
129+
console.log(`Wrote ${config.symbolsOutPath}`);
130+
};
131+
132+
const main = async () => {
133+
await Promise.all(configs.map((config) => analyze(config)));
134+
};
135+
136+
main();

packages/lit-dev-tools-cjs/src/generate-api-docs-data.ts packages/lit-dev-tools-cjs/src/api-docs/transformer.ts

+2-35
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ import {
1616
ExtendedSourceReference,
1717
Location,
1818
ExternalLocation,
19-
} from './api-docs/types.js';
20-
import {lit2Config} from './api-docs/configs/lit-2.js';
19+
} from './types.js';
2120

2221
const findIndexOrInfinity = <T>(
2322
array: ReadonlyArray<T>,
@@ -82,7 +81,7 @@ type SymbolMap = {
8281
* Eleventy template, and generate a symbol map that can be used to locate an
8382
* API within our custom page structure.
8483
*/
85-
class Transformer {
84+
export class ApiDocsTransformer {
8685
private config: ApiDocsConfig;
8786
private project: typedoc.JSONOutput.ProjectReflection;
8887
private symbolMap: SymbolMap = {};
@@ -722,35 +721,3 @@ class Transformer {
722721
return pagesArray;
723722
}
724723
}
725-
726-
async function main() {
727-
const app = new typedoc.Application();
728-
app.options.addReader(new typedoc.TSConfigReader());
729-
app.bootstrap({
730-
tsconfig: lit2Config.tsConfigPath,
731-
entryPoints: lit2Config.entrypointModules,
732-
});
733-
const root = app.convert();
734-
if (!root) {
735-
throw new Error('TypeDoc.Application.convert() returned undefined');
736-
}
737-
738-
const json = await app.serializer.projectToObject(root);
739-
const transformer = new Transformer(json, lit2Config);
740-
const {pages, symbolMap} = await transformer.transform();
741-
742-
await fs.writeFile(
743-
lit2Config.pagesOutPath,
744-
JSON.stringify(pages, null, 2),
745-
'utf8'
746-
);
747-
console.log(`Wrote ${lit2Config.pagesOutPath}`);
748-
await fs.writeFile(
749-
lit2Config.symbolsOutPath,
750-
JSON.stringify(symbolMap, null, 2),
751-
'utf8'
752-
);
753-
console.log(`Wrote ${lit2Config.symbolsOutPath}`);
754-
}
755-
756-
main();

packages/lit-dev-tools-cjs/src/api-docs/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export interface ApiDocsConfig {
6565
*/
6666
typedocRoot: string;
6767

68+
/**
69+
* Extra setup/build commands to run after NPM install and before running
70+
* TypeDoc.
71+
*/
72+
extraSetupCommands?: Array<{cmd: string; args: string[]}>;
73+
6874
/**
6975
* Entrypoint TypeScript modules for TypeDoc to analyze.
7076
*

0 commit comments

Comments
 (0)