Files
upstream-mattermost/docs/site/scripts/gen-plugin-jsdocs.mjs
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

114 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
// Generates docs/site/data/plugin-jsdocs.json, the data source consumed by the <PluginJsDocs />
// component that renders the web app plugin SDK reference
// (docs/develop/integrate/reference/webapp/index.md).
//
// Reads webapp/channels/src/plugins/registry.ts directly from this monorepo instead of fetching it
// from GitHub over HTTP.
//
// Usage: node scripts/gen-plugin-jsdocs.mjs (from docs/site/)
import {parse} from '@typescript-eslint/typescript-estree';
import {readFileSync, writeFileSync, mkdirSync} from 'node:fs';
import {resolve, dirname} from 'node:path';
import {fileURLToPath} from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url)); // docs/site/scripts
const SITE_ROOT = resolve(HERE, '..'); // docs/site
const REPO_ROOT = resolve(SITE_ROOT, '../..'); // mattermost/
const REGISTRY_PATH = resolve(REPO_ROOT, 'webapp/channels/src/plugins/registry.ts');
const OUT_PATH = resolve(SITE_ROOT, 'data/plugin-jsdocs.json');
function paramNamesFromPattern(pattern) {
if (pattern.type === 'Identifier') return [pattern.name];
if (pattern.type === 'ObjectPattern') {
return pattern.properties.flatMap((prop) => (prop.type === 'Property' ? paramNamesFromPattern(prop.value) : paramNamesFromPattern(prop)));
}
if (pattern.type === 'ArrayPattern') {
return pattern.elements.flatMap((el) => (el ? paramNamesFromPattern(el) : []));
}
if (pattern.type === 'AssignmentPattern') return paramNamesFromPattern(pattern.left);
if (pattern.type === 'RestElement') return paramNamesFromPattern(pattern.argument);
return [];
}
// Comments attached to `node`: walk backward from its start, collecting comments as long as only
// whitespace separates them from each other and from the node. Node-scoped, so unlike a global
// "which comments sit on consecutive lines" pass, it can't attribute a comment to the wrong member.
function leadingComments(sourceText, comments, node) {
const attached = [];
let cursor = node.range[0];
for (let i = comments.length - 1; i >= 0; i--) {
const comment = comments[i];
if (comment.range[1] > cursor) continue;
if (!/^\s*$/.test(sourceText.slice(comment.range[1], cursor))) break;
attached.unshift(comment);
cursor = comment.range[0];
}
return attached.flatMap((comment) =>
comment.value
.split('\n')
.map((line) => line.replace(/^\s*\*\s?/, '').trimEnd())
.filter((line) => line.length > 0),
);
}
// reArg(['name', ...], handler) documents its public parameter names explicitly in that array —
// that's the contract callers see, so prefer it over inferring names from the handler's own
// (possibly renamed or destructured) parameters.
function reArgParameterNames(callExpr) {
const [firstArg, ...rest] = callExpr.arguments;
if (callExpr.callee.type === 'Identifier' && callExpr.callee.name === 'reArg' && firstArg?.type === 'ArrayExpression') {
return firstArg.elements.filter((el) => el?.type === 'Literal' && typeof el.value === 'string').map((el) => el.value);
}
const handler = rest.find((arg) => arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression');
return handler ? handler.params.flatMap(paramNamesFromPattern) : [];
}
function findPluginRegistryClass(program) {
return program.body.find(
(statement) =>
statement.type === 'ExportDefaultDeclaration' &&
statement.declaration.type === 'ClassDeclaration' &&
statement.declaration.id?.name === 'PluginRegistry',
)?.declaration;
}
function main() {
const sourceText = readFileSync(REGISTRY_PATH, 'utf8');
const ast = parse(sourceText, {comment: true, range: true});
const classDecl = findPluginRegistryClass(ast);
if (!classDecl) {
throw new Error(`Could not find "export default class PluginRegistry" in ${REGISTRY_PATH}`);
}
const methods = [];
for (const member of classDecl.body.body) {
if (member.key?.type !== 'Identifier') continue;
let params;
if (member.type === 'MethodDefinition' && member.kind !== 'constructor') {
params = member.value.params.flatMap(paramNamesFromPattern);
} else if (member.type === 'PropertyDefinition' && member.value?.type === 'CallExpression') {
params = reArgParameterNames(member.value);
} else {
continue;
}
methods.push({
Name: member.key.name,
Parameters: params,
Comments: leadingComments(sourceText, ast.comments, member),
});
}
const output = {Interface: {Methods: methods}};
mkdirSync(dirname(OUT_PATH), {recursive: true});
writeFileSync(OUT_PATH, JSON.stringify(output, null, 2));
console.log(`[plugin-jsdocs] wrote ${methods.length} methods to ${OUT_PATH}`);
}
main();