vuepress-jsdoc 4.3.1 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,82 +1,125 @@
1
- "use strict";
2
- var __values = (this && this.__values) || function(o) {
3
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
- if (m) return m.call(o);
5
- if (o && typeof o.length === "number") return {
6
- next: function () {
7
- if (o && i >= o.length) o = void 0;
8
- return { value: o && o[i++], done: !o };
9
- }
10
- };
11
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
- };
13
- var __importDefault = (this && this.__importDefault) || function (mod) {
14
- return (mod && mod.__esModule) ? mod : { "default": mod };
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.generateVueSidebar = void 0;
18
1
  /*
19
2
  * @vuepress
20
3
  * ---
21
4
  * headline: lib/vue-sidebar.ts
22
5
  * ---
23
6
  */
24
- var fs_1 = __importDefault(require("fs"));
25
- var path_1 = require("path");
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { SIDEBAR_TITLE_PLACEHOLDER } from '../constants.js';
10
+ import { getPageName, normalizePath } from './list-folder.js';
26
11
  /**
27
- * Runs through the given tree structure and creates a vuepress config
28
- * @param {object} data Informations to build config
29
- * @param {array} data.fileTree tree strcture
30
- * @param {string} data.codeFolder ./code/ folder
31
- * @param {string} data.srcFolder ./src/ folder
32
- * @param {string} data.docsFolder ./documentation/ folder
33
- * @param {string} data.title title string
34
- * @returns {object} returns the vuepress menu strcture
12
+ * Get the route of the generated folder, e.g. `/code/`
13
+ * @param {string} codeFolder
14
+ * @returns {string}
35
15
  */
36
- var generateVueSidebar = function (_a) {
37
- var _b;
38
- var fileTree = _a.fileTree, codeFolder = _a.codeFolder, srcFolder = _a.srcFolder, docsFolder = _a.docsFolder, title = _a.title;
39
- var rootFiles = [['', '::vuepress-jsdoc-title::']];
40
- rootFiles = rootFiles.concat(fileTree.filter(function (file) { return !file.children; }).map(function (file) { return file.name; }));
41
- var rootFolder = fileTree.filter(function (file) { return file.children && file.children.length > 0; });
42
- var buildChildren = function (children, name, depth) {
43
- var e_1, _a;
44
- var newChildren = [];
45
- try {
46
- for (var children_1 = __values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) {
47
- var child = children_1_1.value;
48
- if (child.children && child.children.length > 0) {
49
- newChildren = newChildren.concat(buildChildren(child.children, child.name, depth + 1));
50
- }
51
- else if (child.fullPath) {
52
- if (fs_1.default.existsSync((0, path_1.join)(docsFolder, child.fullPath.replace(srcFolder, '')) + '.md')) {
53
- newChildren.push(child.fullPath.replace("".concat(srcFolder, "/"), ''));
54
- }
16
+ export const getSidebarBase = (codeFolder) => `/${normalizePath(codeFolder)}/`;
17
+ /**
18
+ * Runs through the given tree structure and creates a vuepress 2 sidebar config.
19
+ * Only pages that were generated are linked. Folders become nested sidebar groups.
20
+ * @param {SidebarOptions} data Information to build config
21
+ * @returns {object} returns the vuepress sidebar structure
22
+ */
23
+ export const generateVueSidebar = ({ fileTree, codeFolder, srcFolder, docsFolder, title, readmeTitle = SIDEBAR_TITLE_PLACEHOLDER, collapsible = false }) => {
24
+ const base = getSidebarBase(codeFolder);
25
+ const root = normalizePath(srcFolder);
26
+ const toLink = (file) => {
27
+ if (!file.fullPath)
28
+ return null;
29
+ const relativePath = path.posix.relative(root, normalizePath(file.fullPath));
30
+ if (!fs.existsSync(`${path.join(docsFolder, relativePath)}.md`))
31
+ return null;
32
+ return `${base}${relativePath}`;
33
+ };
34
+ const buildChildren = (children) => {
35
+ const items = [];
36
+ for (const child of children) {
37
+ if (child.children) {
38
+ const nested = buildChildren(child.children);
39
+ if (nested.length > 0) {
40
+ items.push({ text: child.name, collapsible: true, children: nested });
55
41
  }
56
42
  }
57
- }
58
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
59
- finally {
60
- try {
61
- if (children_1_1 && !children_1_1.done && (_a = children_1.return)) _a.call(children_1);
43
+ else {
44
+ const link = toLink(child);
45
+ if (link)
46
+ items.push(link);
62
47
  }
63
- finally { if (e_1) throw e_1.error; }
64
48
  }
65
- return newChildren;
49
+ return items;
66
50
  };
67
- var tree = rootFolder.map(function (folder) { return ({
68
- title: folder.name,
69
- collapsable: false,
70
- children: buildChildren(folder.children, folder.name, 0)
71
- }); });
72
- return _b = {},
73
- _b["/".concat(codeFolder, "/")] = [
74
- {
75
- title: title,
76
- collapsable: false,
77
- children: rootFiles
78
- }
79
- ].concat(tree),
80
- _b;
51
+ const rootFiles = [
52
+ { text: readmeTitle, link: base },
53
+ ...buildChildren(fileTree.filter((file) => !file.children))
54
+ ];
55
+ const folders = fileTree
56
+ .filter((file) => file.children)
57
+ .map((folder) => ({ text: folder.name, collapsible, children: buildChildren(folder.children) }))
58
+ .filter((group) => group.children.length > 0);
59
+ return {
60
+ [base]: [{ text: title, collapsible, children: rootFiles }, ...folders]
61
+ };
62
+ };
63
+ /**
64
+ * Get the `title` of the frontmatter of a generated page (set with the `@vuepress` block)
65
+ * @param {string} mdPath path to the generated markdown file
66
+ * @returns {string|undefined}
67
+ */
68
+ const readPageTitle = (mdPath) => {
69
+ const title = /^---\r?\n[\s\S]*?^title:\s*(.+?)\s*$[\s\S]*?^---/m.exec(fs.readFileSync(mdPath, 'utf-8'))?.[1];
70
+ return title?.replace(/^(['"])(.*)\1$/, '$2');
71
+ };
72
+ /**
73
+ * Runs through the given tree structure and creates a vitepress sidebar config.
74
+ * Vitepress needs the text of every link, so it is read from the generated page.
75
+ * @param {SidebarOptions} data Information to build config
76
+ * @returns {object} returns the vitepress sidebar structure
77
+ */
78
+ export const generateVitepressSidebar = (data) => {
79
+ const base = getSidebarBase(data.codeFolder);
80
+ const root = normalizePath(data.srcFolder);
81
+ // the frontmatter title or the file name. Index files are shown as `index` instead of their prefixed page name.
82
+ const getText = (link) => {
83
+ const relativePath = link.slice(base.length);
84
+ const name = path.posix.basename(relativePath);
85
+ const isIndex = name === getPageName(path.posix.join(root, path.posix.dirname(relativePath)), 'index');
86
+ return readPageTitle(`${path.join(data.docsFolder, relativePath)}.md`) ?? (isIndex ? 'index' : name);
87
+ };
88
+ const toVitepressItem = (item, nested = true) => {
89
+ if (typeof item === 'string') {
90
+ return { text: getText(item), link: item };
91
+ }
92
+ if ('link' in item) {
93
+ return { text: item.text, link: item.link };
94
+ }
95
+ return {
96
+ text: item.text,
97
+ // nested folders are collapsed, vitepress opens groups with the active page.
98
+ // collapsible top level groups stay open.
99
+ ...(item.collapsible ? { collapsed: nested } : {}),
100
+ items: item.children.map((child) => toVitepressItem(child))
101
+ };
102
+ };
103
+ return { [base]: generateVueSidebar(data)[base].map((group) => toVitepressItem(group, false)) };
104
+ };
105
+ /**
106
+ * Create the content of the `config.js`, which is placed into the generated folder.
107
+ * It exports the file tree and a `sidebarTree(title)` function, which returns the vuepress or vitepress sidebar.
108
+ * @param {SidebarOptions} options
109
+ * @returns {string} javascript code (ES module)
110
+ */
111
+ export const createSidebarConfig = (options) => {
112
+ // the placeholder is replaced with the `title` parameter of `sidebarTree`
113
+ const data = { ...options, readmeTitle: SIDEBAR_TITLE_PLACEHOLDER };
114
+ const tree = options.format === 'vitepress' ? generateVitepressSidebar(data) : generateVueSidebar(data);
115
+ const sidebar = JSON.stringify(tree, null, 2).replace(JSON.stringify(SIDEBAR_TITLE_PLACEHOLDER), 'title');
116
+ return [
117
+ '// This file is generated by vuepress-jsdoc. Do not edit it manually.',
118
+ `export const fileTree = ${JSON.stringify(options.fileTree, null, 2)};`,
119
+ '',
120
+ `export const sidebarTree = (title = 'Mainpage') => (${sidebar});`,
121
+ '',
122
+ 'export default sidebarTree;',
123
+ ''
124
+ ].join('\n');
81
125
  };
82
- exports.generateVueSidebar = generateVueSidebar;
@@ -0,0 +1,48 @@
1
+ import { CLIArguments } from '../interfaces.js';
2
+ export interface VuepressJsdocPluginOptions extends Omit<CLIArguments, 'dist' | 'watch' | 'format' | 'config'> {
3
+ /**
4
+ * Add the generated sidebar to all generated pages (frontmatter `sidebar`).
5
+ * Disable it, if you configure the sidebar in your theme.
6
+ * @default true
7
+ */
8
+ sidebar?: boolean;
9
+ /**
10
+ * Text of the first sidebar entry, which links to the readme
11
+ * @default 'Readme'
12
+ */
13
+ sidebarTitle?: string;
14
+ }
15
+ /** Minimal parts of the vuepress app, which are used by the plugin */
16
+ interface App {
17
+ dir: {
18
+ source: (...args: string[]) => string;
19
+ };
20
+ env: {
21
+ isDev: boolean;
22
+ };
23
+ }
24
+ interface Page {
25
+ path: string;
26
+ frontmatter: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * VuePress 2 plugin, which generates the documentation when vuepress starts and
30
+ * updates it while `vuepress dev` is running. No separate CLI call is needed.
31
+ * @example
32
+ * import { vuepressJsdocPlugin } from 'vuepress-jsdoc';
33
+ *
34
+ * export default defineUserConfig({
35
+ * plugins: [vuepressJsdocPlugin({ source: './src', folder: 'code' })]
36
+ * });
37
+ * @param {VuepressJsdocPluginOptions} pluginOptions
38
+ * @returns {Function} vuepress plugin
39
+ */
40
+ export declare const vuepressJsdocPlugin: ({ sidebar, sidebarTitle, ...pluginOptions }?: VuepressJsdocPluginOptions) => (app: App) => {
41
+ name: string;
42
+ extendsMarkdownOptions: () => Promise<void>;
43
+ extendsPage: (page: Page) => Promise<void>;
44
+ onWatched: (_app: App, watchers: {
45
+ close: () => Promise<void> | void;
46
+ }[]) => void;
47
+ };
48
+ export {};
@@ -0,0 +1,63 @@
1
+ /*
2
+ * @vuepress
3
+ * ---
4
+ * headline: lib/vuepress-plugin.ts
5
+ * ---
6
+ */
7
+ import path from 'node:path';
8
+ import { generate, getSidebarOptions, parseArguments, watchFiles } from '../index.js';
9
+ import { listFolder } from './list-folder.js';
10
+ import { generateVueSidebar, getSidebarBase } from './vue-sidebar.js';
11
+ /**
12
+ * VuePress 2 plugin, which generates the documentation when vuepress starts and
13
+ * updates it while `vuepress dev` is running. No separate CLI call is needed.
14
+ * @example
15
+ * import { vuepressJsdocPlugin } from 'vuepress-jsdoc';
16
+ *
17
+ * export default defineUserConfig({
18
+ * plugins: [vuepressJsdocPlugin({ source: './src', folder: 'code' })]
19
+ * });
20
+ * @param {VuepressJsdocPluginOptions} pluginOptions
21
+ * @returns {Function} vuepress plugin
22
+ */
23
+ export const vuepressJsdocPlugin = ({ sidebar = true, sidebarTitle = 'Readme', ...pluginOptions } = {}) => (app) => {
24
+ // generated files are written into the source folder of vuepress
25
+ const argv = {
26
+ ...pluginOptions,
27
+ dist: path.relative(process.cwd(), app.dir.source()) || '.',
28
+ format: 'vuepress',
29
+ watch: false
30
+ };
31
+ const options = parseArguments(argv);
32
+ const base = getSidebarBase(options.codeFolder);
33
+ let generated = null;
34
+ // the promise is cached, because all pages are extended at the same time
35
+ let sidebarCache = null;
36
+ const buildSidebar = async () => {
37
+ const { tree } = await listFolder(options.srcFolder, options.exclude, options.include);
38
+ return generateVueSidebar({ ...getSidebarOptions(options, tree), readmeTitle: sidebarTitle });
39
+ };
40
+ return {
41
+ name: 'vuepress-plugin-jsdoc',
42
+ // this hook runs before the pages are resolved, so the generated files become pages
43
+ extendsMarkdownOptions: async () => {
44
+ generated ??= generate(argv);
45
+ await generated;
46
+ },
47
+ extendsPage: async (page) => {
48
+ if (!sidebar || !page.path.startsWith(base) || page.frontmatter.sidebar !== undefined)
49
+ return;
50
+ sidebarCache ??= buildSidebar();
51
+ page.frontmatter.sidebar = await sidebarCache;
52
+ },
53
+ onWatched: (_app, watchers) => {
54
+ const watcher = watchFiles(argv);
55
+ // changed files can create or remove a page, which changes the sidebar
56
+ watcher.on('all', (event) => {
57
+ if (event !== 'addDir')
58
+ sidebarCache = null;
59
+ });
60
+ watchers.push(watcher);
61
+ }
62
+ };
63
+ };
package/package.json CHANGED
@@ -1,21 +1,16 @@
1
1
  {
2
2
  "name": "vuepress-jsdoc",
3
- "version": "4.3.1",
3
+ "version": "6.1.0",
4
4
  "description": "Generate jsdoc markdown files for vuepress",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
- "scripts": {
7
- "test": "jest",
8
- "test-coverage": "cross-env NODE_ENV=test jest --coverage",
9
- "test-watch": "cross-env NODE_ENV=test jest --watchAll",
10
- "lint": "eslint --ext .tsx,.ts,.json,.js src/ --fix",
11
- "dev": "npm run build -- -w",
12
- "build": "tsc",
13
- "prepublishOnly": "npm i && npm run build",
14
- "prepare": "husky install",
15
- "version": "conventional-changelog -p karma -i CHANGELOG.md -s -r 0 && git add .",
16
- "docs": "./bin/vuepress-jsdoc.js --source=./dist --partials=./example/partials/*.hbs --readme ./README.md --include=\"**/*.(ts|js)\" --exclude=\"**/*.d.ts,**/interfaces.*,**/constants.*\"",
17
- "docs:dev": "npx concurrently --kill-others \"npm run dev\" \"npm run docs -- -w\" \"npx vuepress dev documentation\"",
18
- "docs:build": "npm run build && npm run docs && npx vuepress build documentation"
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
19
14
  },
20
15
  "keywords": [
21
16
  "jsdoc",
@@ -25,7 +20,7 @@
25
20
  "vuepress"
26
21
  ],
27
22
  "engines": {
28
- "node": ">=14"
23
+ "node": ">=22.22.0"
29
24
  },
30
25
  "license": "MIT",
31
26
  "author": "Philip Stapelfeldt <me@ph1p.dev>",
@@ -40,116 +35,74 @@
40
35
  "printWidth": 120,
41
36
  "trailingComma": "none"
42
37
  },
43
- "jest": {
44
- "coverageThreshold": {
45
- "global": {
46
- "branches": 95,
47
- "functions": 60,
48
- "lines": 75,
49
- "statements": -10
50
- }
51
- },
52
- "roots": [
53
- "<rootDir>/src"
54
- ],
55
- "transform": {
56
- "^.+\\.tsx?$": "ts-jest"
57
- },
58
- "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
59
- "moduleFileExtensions": [
60
- "ts",
61
- "tsx",
62
- "js",
63
- "jsx",
64
- "json",
65
- "node"
66
- ],
67
- "bail": true,
68
- "verbose": true,
69
- "collectCoverageFrom": [
70
- "**/*.{js,jsx,ts,tsx}",
71
- "!**/node_modules/**",
72
- "!**/coverage/**",
73
- "!**/example/**",
74
- "!**/vendor/**"
75
- ],
76
- "coverageDirectory": "./coverage"
77
- },
38
+ "files": [
39
+ "bin",
40
+ "dist",
41
+ "template"
42
+ ],
78
43
  "bin": {
79
44
  "vuepress-jsdoc": "bin/vuepress-jsdoc.js"
80
45
  },
81
46
  "dependencies": {
82
- "chalk": "^4.1.2",
83
- "chokidar": "^3.5.3",
84
- "commander": "^9.4.1",
85
- "del": "^6.0.0",
47
+ "chokidar": "^6.0.0",
48
+ "commander": "^15.1.0",
49
+ "del": "^9.0.1",
86
50
  "front-matter": "^4.0.2",
87
- "jsdoc-to-markdown": "^7.1.1",
88
- "micromatch": "^4.0.5",
89
- "mkdirp": "^1.0.4",
90
- "vue-docgen-cli": "^4.51.0"
51
+ "jsdoc-to-markdown": "^9.1.3",
52
+ "kleur": "^4.1.5",
53
+ "micromatch": "^4.0.8",
54
+ "vue-docgen-cli": "^4.79.0"
55
+ },
56
+ "peerDependencies": {
57
+ "typescript": "*"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "typescript": {
61
+ "optional": true
62
+ }
91
63
  },
92
64
  "devDependencies": {
93
- "@types/jest": "^29.1.1",
94
- "@types/jsdoc-to-markdown": "^7.0.3",
95
- "@types/micromatch": "^4.0.2",
96
- "@types/mkdirp": "^1.0.2",
97
- "@typescript-eslint/eslint-plugin": "^5.39.0",
98
- "@typescript-eslint/parser": "^5.39.0",
99
- "babel-jest": "^29.1.2",
100
- "cross-env": "^7.0.3",
101
- "eslint": "^8.24.0",
102
- "eslint-config-prettier": "^8.5.0",
103
- "eslint-plugin-import": "^2.26.0",
104
- "eslint-plugin-jest": "^27.1.1",
105
- "eslint-plugin-prefer-arrow": "^1.2.3",
106
- "husky": "^8.0.1",
107
- "jest": "^29.1.2",
108
- "lint-staged": "^13.0.3",
109
- "memfs": "^3.4.7",
110
- "ts-jest": "^29.0.3",
111
- "typescript": "^4.8.4",
112
- "vue": "^2.7.8"
65
+ "@eslint/js": "^10.0.1",
66
+ "@types/jsdoc-to-markdown": "^7.0.6",
67
+ "@types/micromatch": "^4.0.10",
68
+ "@types/node": "^26.6.6",
69
+ "@typescript/native": "npm:typescript@^7.0.2",
70
+ "@vitest/coverage-v8": "^5.1.0",
71
+ "conventional-changelog": "^8.1.3",
72
+ "conventional-changelog-conventionalcommits": "^10.4.1",
73
+ "eslint": "^10.14.0",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "globals": "^17.14.0",
76
+ "husky": "^9.1.7",
77
+ "lint-staged": "^17.6.0",
78
+ "memfs": "^4.80.0",
79
+ "prettier": "^3.9.10",
80
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
81
+ "typescript-eslint": "^8.72.1",
82
+ "vitepress": "^1.6.4",
83
+ "vitest": "^5.1.0"
113
84
  },
114
85
  "lint-staged": {
115
- "src/**/*.{css,scss,json}": [
116
- "prettier --write",
117
- "git add"
86
+ "src/**/*.{js,ts}": [
87
+ "eslint --fix",
88
+ "prettier --write"
118
89
  ],
119
- "src/**/*.{js,jsx,ts,tsx}": [
120
- "eslint",
121
- "prettier --write",
122
- "git add"
90
+ "src/**/*.json": [
91
+ "prettier --write"
123
92
  ]
124
93
  },
125
- "eslintConfig": {
126
- "env": {
127
- "commonjs": true,
128
- "es6": true,
129
- "node": true,
130
- "jest": true
131
- },
132
- "extends": "eslint:recommended",
133
- "globals": {
134
- "Atomics": "readonly",
135
- "SharedArrayBuffer": "readonly"
136
- },
137
- "parserOptions": {
138
- "ecmaVersion": 2018
139
- },
140
- "rules": {
141
- "quotes": [
142
- "error",
143
- "single",
144
- {
145
- "allowTemplateLiterals": true
146
- }
147
- ],
148
- "no-console": 0,
149
- "arrow-parens": [
150
- "error",
151
- "as-needed"
152
- ]
153
- }
94
+ "scripts": {
95
+ "test": "vitest run",
96
+ "test-coverage": "vitest run --coverage",
97
+ "test-watch": "vitest",
98
+ "typecheck": "tsc -p tsconfig.test.json",
99
+ "lint": "eslint src",
100
+ "lint:fix": "eslint src --fix",
101
+ "dev": "tsc -w",
102
+ "build": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc",
103
+ "version": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s && git add CHANGELOG.md",
104
+ "docs": "./bin/vuepress-jsdoc.js --source=./src --partials=./example/partials/*.hbs --readme=./README.md --format=vitepress --exclude=\"**/__tests__/**\"",
105
+ "docs:dev": "pnpm run build && pnpm run docs && vitepress dev documentation",
106
+ "docs:build": "pnpm run build && pnpm run docs && vitepress build documentation"
154
107
  }
155
- }
108
+ }