nx/scripts/documentation/package-schemas/package-metadata.ts
Isaac Mann 12eb5df469
docs(core): powerpack docs (#27904)
-  Activate powerpack recipe
-  Powerpack owners documentation
- [x] Powerpack custom remote cache documentation
- [x] Powerpack conformance documentation

Infrastructure for powerpack docs

- Adds the ability to generate API docs from ocean packages

To generate API documentation for plugins in the ocean repository, run
the `nx documentation` command with the `NX_OCEAN_RELATIVE_PATH`
environment variable set to the relative path to your checked out copy
of the ocean repo.

```
NX_OCEAN_RELATIVE_PATH=../ocean nx documentation
```

This will create generated API documentation in the
`docs/external-generated` folder. This API will be merged into the
normal `docs/generated` documentation when the docs site is built.

Because there are two separate output folders, if someone runs `nx
documentation` without the `NX_OCEAN_RELATIVE_PATH` environment
variable, the ocean documentation will not be overwritten. The ocean
documentation will only be updated or deleted when someone explicitly
chooses to do so.

---------

Co-authored-by: Juri Strumpflohner <juri.strumpflohner@gmail.com>
2024-09-25 10:15:47 -04:00

180 lines
5.7 KiB
TypeScript

import {
convertToDocumentMetadata,
DocumentMetadata,
} from '@nx/nx-dev/models-document';
import { readFileSync } from 'fs';
import { readJsonSync } from 'fs-extra';
import { sync } from 'glob';
import { join, resolve } from 'path';
import * as DocumentationMap from '../../../docs/map.json';
import {
JsonSchema1,
PackageData,
SchemaMetadata,
} from '@nx/nx-dev/models-package';
function createSchemaMetadata(
name: string,
data: Record<string, any>,
paths: {
absoluteRoot: string;
folderName: string;
root: string;
},
type: 'executor' | 'generator'
): SchemaMetadata {
const path = join(paths.root, data.schema);
// "factory" is for Angular support, this is the same as "implementation"
if (!data['implementation'] && data['factory'])
data.implementation = data.factory;
const schemaMetadata: SchemaMetadata = {
name,
...data,
aliases: data.aliases ?? [],
description: data.description ?? '',
hidden: data.hidden ?? false,
implementation: join(paths.root, data.implementation) + '.ts',
path, // Switching property for less confusing naming conventions
schema: data.schema
? readJsonSync(join(paths.absoluteRoot, paths.root, data.schema))
: null,
type,
};
if (schemaMetadata.schema && !schemaMetadata.schema.presets) {
schemaMetadata.schema.presets = [];
}
return schemaMetadata;
}
function getSchemaList(
paths: {
absoluteRoot: string;
folderName: string;
root: string;
},
collectionFileName: string,
collectionEntries: string[]
): SchemaMetadata[] {
const targetPath = join(paths.absoluteRoot, paths.root, collectionFileName);
// We assume the type of the collection of schema is the name of the collection file (executors or generators)
const type = collectionFileName.replace('.json', '').replace('s', '') as
| 'executor'
| 'generator';
try {
const metadata: SchemaMetadata[] = [];
const collectionFile = readJsonSync(targetPath, 'utf8');
for (const entry of collectionEntries) {
if (!collectionFile[entry]) {
continue;
}
metadata.push(
...Object.entries<JsonSchema1>(collectionFile[entry])
.filter(([name]) => !metadata.find((x) => x.name === name))
.map(([name, schema]: [string, JsonSchema1]) =>
createSchemaMetadata(name, schema, paths, type)
)
);
}
return metadata;
} catch (e) {
console.log(
`SchemaMetadata "${paths.root
.split('/')
.pop()}" resolution skipped: no file found at "${targetPath}".`
);
return [];
}
}
/**
* Generate the package metadata by exploring the directory path given.
* This function will look for all the packages in the given directory under packagesDirectory.
* It will then look for the package.json file and read the description and name of the package.
* It will also look for the generators.json and executors.json files and read the schema of each generator and executor.
* It will also look for the documents.json file and read the documents of each package.
* If the package is private and NODE_ENV is not development, it will not be included in the metadata.
* @param absoluteRoot
* @param packagesDirectory
* @returns Configuration
*/
export function findPackageMetadataList(
absoluteRoot: string,
packagesDirectory: string = 'packages',
prefix = ''
): PackageData[] {
const packagesDir = resolve(join(absoluteRoot, packagesDirectory));
/**
* Get all the custom overview information on each package if available
*/
const additionalApiReferences: DocumentMetadata[] = DocumentationMap.content
.find((data) => data.id === 'additional-api-references')!
.itemList.map((item) => convertToDocumentMetadata(item));
// Do not use map.json, but add a documentation property on the package.json directly that can be easily resolved
return sync(`${packagesDir}/${prefix}*`, {
ignore: [`${packagesDir}/cli`, `${packagesDir}/*-e2e`],
})
.map((folderPath: string): PackageData => {
const folderName = folderPath.substring(packagesDir.length + 1);
const relativeFolderPath = folderPath.replace(absoluteRoot, '');
const packageJson = readJsonSync(
join(folderPath, 'package.json'),
'utf8'
);
const isPrivate =
packageJson.private && process.env.NODE_ENV !== 'development'; // skip this check in dev mode
const hasDocumentation = additionalApiReferences.find(
(pkg) => pkg.id === folderName
);
return isPrivate
? null
: {
githubRoot: 'https://github.com/nrwl/nx/blob/master',
name: folderName,
packageName: packageJson.name,
description: packageJson.description,
root: relativeFolderPath,
source: join(relativeFolderPath, '/src'),
documents: !!hasDocumentation
? hasDocumentation.itemList.map((item) => ({
...item,
path: item.path,
file: item.file,
content: readFileSync(
join('docs', item.file + '.md'),
'utf8'
),
}))
: [],
generators: getSchemaList(
{
absoluteRoot,
folderName,
root: relativeFolderPath,
},
'generators.json',
['generators']
),
executors: getSchemaList(
{
absoluteRoot,
folderName,
root: relativeFolderPath,
},
'executors.json',
['executors', 'builders']
),
};
})
.filter(Boolean);
}