Skip to content

refactor(docs): centralize JSDoc configuration to repository root - #9056

Draft
quirogas wants to merge 3 commits into
googleapis:mainfrom
quirogas:refactor/4584-centralize-jsdoc-root
Draft

refactor(docs): centralize JSDoc configuration to repository root#9056
quirogas wants to merge 3 commits into
googleapis:mainfrom
quirogas:refactor/4584-centralize-jsdoc-root

Conversation

@quirogas

@quirogas quirogas commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Replace 241 per-package .jsdoc.js and .jsdoc.cjs configuration copies across packages/* with a unified root-level .jsdoc.js file, and update package "docs" scripts to execute jsdoc -c ../../.jsdoc.js.

Every client package previously maintained a structurally identical JSDoc configuration file where the only variation was the hardcoded systemName (package name) and proto include paths. Maintaining over 200 duplicate configuration files created high maintenance friction. The new centralized root .jsdoc.js
dynamically evaluates process.cwd() to resolve systemName and existing include paths (build/src, build/esm, and protos). The file utilizes dual named and default module exports to guarantee seamless compatibility with both ES Module resolution ("type": "module" in root package.json) and JSDoc's
loader.

For googleapis/librarian#4584

quirogas added 3 commits July 31, 2026 12:40
Replace 241 per-package `.jsdoc.js` and `.jsdoc.cjs` configuration copies across `packages/*` with a unified root-level `.jsdoc.js` file, and update package `"docs"` scripts to execute `jsdoc -c ../../.jsdoc.js`.

Every client package previously maintained a structurally identical JSDoc configuration file where the only variation was the hardcoded `systemName` (package name) and proto include paths. Maintaining over 200 duplicate configuration files created high maintenance friction. The new centralized root `.jsdoc.js` dynamically evaluates `process.cwd()` to resolve `systemName` and existing include paths (`build/src`, `build/esm`, and `protos`). The file utilizes dual named and default module exports to guarantee seamless compatibility with both ES Module resolution (`"type": "module"` in root package.json) and JSDoc's loader.

For googleapis/librarian#4584
@quirogas quirogas self-assigned this Aug 1, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request centralizes JSDoc configuration by removing individual configuration files across packages and referencing a single, centralized .jsdoc.js file at the root. However, because the root package.json uses "type": "module", JSDoc will fail to load this configuration file due to an ESM loading error. To resolve this, the centralized configuration should be renamed to .jsdoc.cjs using CommonJS syntax, and all package template references should be updated accordingly.

Comment thread .jsdoc.js
Comment on lines +15 to +88
import fs from 'fs';
import path from 'path';

let systemName = '';
const packageJsonPath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(packageJsonPath)) {
try {
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (pkg.name) {
systemName = pkg.name;
}
} catch (err) {
// Ignore invalid JSON or read errors
}
}

const include = [];
if (fs.existsSync(path.join(process.cwd(), 'build/src'))) {
include.push('build/src');
} else if (fs.existsSync(path.join(process.cwd(), 'build/esm/src'))) {
include.push('build/esm/src');
} else if (fs.existsSync(path.join(process.cwd(), 'build/cjs/src'))) {
include.push('build/cjs/src');
} else if (fs.existsSync(path.join(process.cwd(), 'src'))) {
include.push('src');
} else {
include.push('build/src');
}

if (fs.existsSync(path.join(process.cwd(), 'protos'))) {
include.push('protos');
} else if (fs.existsSync(path.join(process.cwd(), 'build/protos'))) {
include.push('build/protos');
}

export const opts = {
readme: './README.md',
package: './package.json',
template: './node_modules/jsdoc-fresh',
recurse: true,
verbose: true,
destination: './docs/',
};

export const plugins = ['plugins/markdown', 'jsdoc-region-tag'];

export const source = {
excludePattern: '(^|\\/|\\\\)[._]',
include,
includePattern: '\\.(js|cjs|mjs)$',
};

export const templates = {
copyright: 'Copyright 2026 Google LLC',
includeDate: false,
sourceFiles: false,
systemName,
theme: 'lumen',
default: {
outputSourceFiles: false,
},
};

export const markdown = {
idInHeadings: true,
};

export default {
opts,
plugins,
source,
templates,
markdown,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since the root package.json has "type": "module", any .js file at the root is treated as an ES module by Node.js. However, JSDoc loads configuration files using CommonJS require(). When JSDoc attempts to load an ES module via require(), Node.js will throw an ERR_REQUIRE_ESM error. Conversely, if the root does not use "type": "module", loading this file via require() will throw a SyntaxError due to the ESM import/export syntax.

To ensure compatibility with JSDoc's CommonJS loader across all environments, the centralized configuration file should be written using CommonJS syntax (require and module.exports) and named with a .cjs extension (e.g., .jsdoc.cjs). This guarantees that Node.js always treats it as CommonJS.

Additionally, we can make the template path resolution more robust by using path.join(__dirname, 'node_modules/jsdoc-fresh') to resolve it to the root node_modules directory, regardless of the package directory from which JSDoc is executed.

const fs = require('fs');
const path = require('path');

let systemName = '';
const packageJsonPath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(packageJsonPath)) {
  try {
    const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
    if (pkg.name) {
      systemName = pkg.name;
    }
  } catch (err) {
    // Ignore invalid JSON or read errors
  }
}

const include = [];
if (fs.existsSync(path.join(process.cwd(), 'build/src'))) {
  include.push('build/src');
} else if (fs.existsSync(path.join(process.cwd(), 'build/esm/src'))) {
  include.push('build/esm/src');
} else if (fs.existsSync(path.join(process.cwd(), 'build/cjs/src'))) {
  include.push('build/cjs/src');
} else if (fs.existsSync(path.join(process.cwd(), 'src'))) {
  include.push('src');
} else {
  include.push('build/src');
}

if (fs.existsSync(path.join(process.cwd(), 'protos'))) {
  include.push('protos');
} else if (fs.existsSync(path.join(process.cwd(), 'build/protos'))) {
  include.push('build/protos');
}

const opts = {
  readme: './README.md',
  package: './package.json',
  template: path.join(__dirname, 'node_modules/jsdoc-fresh'),
  recurse: true,
  verbose: true,
  destination: './docs/',
};

const plugins = ['plugins/markdown', 'jsdoc-region-tag'];

const source = { 
  excludePattern: '(^|\\/|\\\\)[._]',
  include,
  includePattern: '\\.(js|cjs|mjs)$',
};

const templates = {
  copyright: 'Copyright 2026 Google LLC',
  includeDate: false,
  sourceFiles: false,
  systemName,
  theme: 'lumen',
  default: {
    outputSourceFiles: false,
  },
};

const markdown = {
  idInHeadings: true,
};

module.exports = {
  opts,
  plugins,
  source,
  templates,
  markdown,
};

"compile": "tsc -p . && cp -r protos build/{% if not api.legacyProtoLoad %} && minifyProtoJson{% endif %}",
"compile-protos": "compileProtos {% if api.legacyProtoLoad %}--skip-json {% endif %}src",
"docs": "jsdoc -c .jsdoc.js",
"docs": "jsdoc -c ../../.jsdoc.js",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the JSDoc configuration file reference to use the .cjs extension to match the renamed centralized configuration file.

Suggested change
"docs": "jsdoc -c ../../.jsdoc.js",
"docs": "jsdoc -c ../../.jsdoc.cjs",

"clean": "gts clean",
"compile-protos": "compileProtos esm/src --esm {% if api.legacyProtoLoad %}--skip-json {% endif %}",
"docs": "jsdoc -c .jsdoc.cjs",
"docs": "jsdoc -c ../../.jsdoc.js",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the JSDoc configuration file reference to use the .cjs extension to match the renamed centralized configuration file.

Suggested change
"docs": "jsdoc -c ../../.jsdoc.js",
"docs": "jsdoc -c ../../.jsdoc.cjs",

"compile": "tsc -p . && cp -r protos build/",
"compile-protos": "compileProtos src",
"docs": "jsdoc -c .jsdoc.js",
"docs": "jsdoc -c ../../.jsdoc.js",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the JSDoc configuration file reference to use the .cjs extension to match the renamed centralized configuration file.

Suggested change
"docs": "jsdoc -c ../../.jsdoc.js",
"docs": "jsdoc -c ../../.jsdoc.cjs",

@quirogas quirogas changed the title Refactor/4584 centralize jsdoc root refactor(docs): centralize JSDoc configuration to repository root Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant