@opensyntaxhq/autodocs 1.0.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.
- package/README.md +15 -0
- package/dist/index-exports.d.ts +57 -0
- package/dist/index-exports.js +39 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1060 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
interface AutodocsConfig {
|
|
2
|
+
include: string[];
|
|
3
|
+
exclude?: string[];
|
|
4
|
+
entryPoints?: string[];
|
|
5
|
+
tsconfig?: string;
|
|
6
|
+
compilerOptions?: Record<string, unknown>;
|
|
7
|
+
output: OutputConfig;
|
|
8
|
+
theme?: ThemeConfig;
|
|
9
|
+
sidebar?: SidebarItem[];
|
|
10
|
+
features?: FeaturesConfig;
|
|
11
|
+
plugins?: Array<string | PluginConfig>;
|
|
12
|
+
ignoreErrors?: boolean;
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
cache?: boolean;
|
|
15
|
+
cacheDir?: string;
|
|
16
|
+
}
|
|
17
|
+
interface OutputConfig {
|
|
18
|
+
dir: string;
|
|
19
|
+
format: 'static' | 'json' | 'markdown';
|
|
20
|
+
clean?: boolean;
|
|
21
|
+
publicPath?: string;
|
|
22
|
+
siteUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
interface ThemeConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
primaryColor?: string;
|
|
27
|
+
secondaryColor?: string;
|
|
28
|
+
logo?: string;
|
|
29
|
+
favicon?: string;
|
|
30
|
+
fonts?: {
|
|
31
|
+
sans?: string;
|
|
32
|
+
mono?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
interface SidebarItem {
|
|
36
|
+
title: string;
|
|
37
|
+
path?: string;
|
|
38
|
+
items?: SidebarItem[];
|
|
39
|
+
autogenerate?: string;
|
|
40
|
+
collapsed?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface FeaturesConfig {
|
|
43
|
+
search?: boolean;
|
|
44
|
+
darkMode?: boolean;
|
|
45
|
+
playground?: boolean;
|
|
46
|
+
examples?: boolean;
|
|
47
|
+
download?: boolean;
|
|
48
|
+
sourceLinks?: boolean;
|
|
49
|
+
}
|
|
50
|
+
interface PluginConfig {
|
|
51
|
+
name: string;
|
|
52
|
+
options?: Record<string, unknown>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare function defineConfig(config: AutodocsConfig): AutodocsConfig;
|
|
56
|
+
|
|
57
|
+
export { type AutodocsConfig, type OutputConfig, type ThemeConfig, defineConfig };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index-exports.ts
|
|
21
|
+
var index_exports_exports = {};
|
|
22
|
+
__export(index_exports_exports, {
|
|
23
|
+
defineConfig: () => defineConfig
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports_exports);
|
|
26
|
+
|
|
27
|
+
// src/config/loader.ts
|
|
28
|
+
var import_cosmiconfig = require("cosmiconfig");
|
|
29
|
+
var import_jiti = require("jiti");
|
|
30
|
+
var jiti = (0, import_jiti.createJiti)(__filename);
|
|
31
|
+
|
|
32
|
+
// src/config/index.ts
|
|
33
|
+
function defineConfig(config) {
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
37
|
+
0 && (module.exports = {
|
|
38
|
+
defineConfig
|
|
39
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,1060 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_commander = require("commander");
|
|
28
|
+
|
|
29
|
+
// src/commands/init.ts
|
|
30
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
31
|
+
var import_path = __toESM(require("path"));
|
|
32
|
+
var import_chalk = __toESM(require("chalk"));
|
|
33
|
+
var import_inquirer = __toESM(require("inquirer"));
|
|
34
|
+
function registerInit(program2) {
|
|
35
|
+
program2.command("init").description("Initialize autodocs configuration").option("-f, --force", "Overwrite existing config").option("--typescript", "Use TypeScript config (default)", true).option("--javascript", "Use JavaScript config").option("--json", "Use JSON config").action(async (options) => {
|
|
36
|
+
try {
|
|
37
|
+
const opts = options;
|
|
38
|
+
const configType = opts.json ? "json" : opts.javascript ? "js" : "ts";
|
|
39
|
+
const configFile = `autodocs.config.${configType}`;
|
|
40
|
+
const configPath = import_path.default.join(process.cwd(), configFile);
|
|
41
|
+
const exists = await fileExists(configPath);
|
|
42
|
+
if (exists && !opts.force) {
|
|
43
|
+
console.log(import_chalk.default.yellow(`Config file already exists: ${configFile}`));
|
|
44
|
+
console.log(import_chalk.default.yellow("Use --force to overwrite"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const answers = await import_inquirer.default.prompt([
|
|
48
|
+
{
|
|
49
|
+
type: "input",
|
|
50
|
+
name: "include",
|
|
51
|
+
message: "Source files to document:",
|
|
52
|
+
default: "src/**/*.ts"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "input",
|
|
56
|
+
name: "outputDir",
|
|
57
|
+
message: "Output directory:",
|
|
58
|
+
default: "./docs-dist"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: "list",
|
|
62
|
+
name: "format",
|
|
63
|
+
message: "Output format:",
|
|
64
|
+
choices: ["static", "json", "markdown"],
|
|
65
|
+
default: "static"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "input",
|
|
69
|
+
name: "primaryColor",
|
|
70
|
+
message: "Primary color (hex):",
|
|
71
|
+
default: "#6366f1",
|
|
72
|
+
validate: (input) => {
|
|
73
|
+
return /^#[0-9A-F]{6}$/i.test(input) || "Invalid hex color";
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "confirm",
|
|
78
|
+
name: "darkMode",
|
|
79
|
+
message: "Enable dark mode?",
|
|
80
|
+
default: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "confirm",
|
|
84
|
+
name: "search",
|
|
85
|
+
message: "Enable search?",
|
|
86
|
+
default: true
|
|
87
|
+
}
|
|
88
|
+
]);
|
|
89
|
+
const content = generateConfigContent(answers, configType);
|
|
90
|
+
await import_promises.default.writeFile(configPath, content, "utf-8");
|
|
91
|
+
console.log(import_chalk.default.green(`\u2713 Created ${configFile}`));
|
|
92
|
+
await addToGitignore(answers.outputDir);
|
|
93
|
+
console.log(import_chalk.default.cyan("\nNext steps:"));
|
|
94
|
+
console.log(import_chalk.default.cyan(" 1. Review the configuration file"));
|
|
95
|
+
console.log(import_chalk.default.cyan(" 2. Run: autodocs build"));
|
|
96
|
+
console.log(import_chalk.default.cyan(" 3. Run: autodocs serve"));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(import_chalk.default.red("Error initializing config:"), error);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function generateConfigContent(answers, type) {
|
|
104
|
+
const include = answers.include;
|
|
105
|
+
const outputDir = answers.outputDir;
|
|
106
|
+
const format = answers.format;
|
|
107
|
+
const primaryColor = answers.primaryColor;
|
|
108
|
+
const search = answers.search;
|
|
109
|
+
const darkMode = answers.darkMode;
|
|
110
|
+
const config = {
|
|
111
|
+
include: [include],
|
|
112
|
+
exclude: ["**/*.test.ts", "**/*.spec.ts"],
|
|
113
|
+
output: {
|
|
114
|
+
dir: outputDir,
|
|
115
|
+
format,
|
|
116
|
+
clean: true
|
|
117
|
+
},
|
|
118
|
+
theme: {
|
|
119
|
+
name: "default",
|
|
120
|
+
primaryColor
|
|
121
|
+
},
|
|
122
|
+
features: {
|
|
123
|
+
search,
|
|
124
|
+
darkMode,
|
|
125
|
+
examples: true
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
if (type === "json") {
|
|
129
|
+
return JSON.stringify(config, null, 2);
|
|
130
|
+
}
|
|
131
|
+
const importLine = type === "ts" ? "import { defineConfig } from '@opensyntaxhq/autodocs';" : "const { defineConfig } = require('@opensyntaxhq/autodocs');";
|
|
132
|
+
const exportLine = type === "ts" ? "export default" : "module.exports =";
|
|
133
|
+
return `${importLine}
|
|
134
|
+
|
|
135
|
+
${exportLine} defineConfig(${JSON.stringify(config, null, 2)});
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
async function fileExists(path7) {
|
|
139
|
+
try {
|
|
140
|
+
await import_promises.default.access(path7);
|
|
141
|
+
return true;
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function addToGitignore(outputDir) {
|
|
147
|
+
const gitignorePath = import_path.default.join(process.cwd(), ".gitignore");
|
|
148
|
+
try {
|
|
149
|
+
let content = "";
|
|
150
|
+
try {
|
|
151
|
+
content = await import_promises.default.readFile(gitignorePath, "utf-8");
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
if (!content.includes(outputDir)) {
|
|
155
|
+
content += `
|
|
156
|
+
# Autodocs
|
|
157
|
+
${outputDir}
|
|
158
|
+
.autodocs-cache
|
|
159
|
+
`;
|
|
160
|
+
await import_promises.default.writeFile(gitignorePath, content, "utf-8");
|
|
161
|
+
console.log(import_chalk.default.green(`\u2713 Added ${outputDir} to .gitignore`));
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
console.warn(import_chalk.default.yellow("Could not update .gitignore"));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/commands/build.ts
|
|
169
|
+
var import_path3 = __toESM(require("path"));
|
|
170
|
+
var import_url = require("url");
|
|
171
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
172
|
+
var import_ora = __toESM(require("ora"));
|
|
173
|
+
var import_glob = require("glob");
|
|
174
|
+
var import_child_process = require("child_process");
|
|
175
|
+
var import_util = require("util");
|
|
176
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
177
|
+
|
|
178
|
+
// src/config/defaults.ts
|
|
179
|
+
var DEFAULT_CONFIG = {
|
|
180
|
+
include: ["src/**/*.ts", "src/**/*.tsx"],
|
|
181
|
+
exclude: [
|
|
182
|
+
"**/*.test.ts",
|
|
183
|
+
"**/*.spec.ts",
|
|
184
|
+
"**/*.test.tsx",
|
|
185
|
+
"**/*.spec.tsx",
|
|
186
|
+
"**/node_modules/**",
|
|
187
|
+
"**/*.d.ts"
|
|
188
|
+
],
|
|
189
|
+
output: {
|
|
190
|
+
dir: "./docs-dist",
|
|
191
|
+
format: "static",
|
|
192
|
+
clean: true
|
|
193
|
+
},
|
|
194
|
+
theme: {
|
|
195
|
+
name: "default",
|
|
196
|
+
primaryColor: "#6366f1",
|
|
197
|
+
secondaryColor: "#8b5cf6"
|
|
198
|
+
},
|
|
199
|
+
features: {
|
|
200
|
+
search: true,
|
|
201
|
+
darkMode: true,
|
|
202
|
+
playground: false,
|
|
203
|
+
examples: true,
|
|
204
|
+
download: true,
|
|
205
|
+
sourceLinks: true
|
|
206
|
+
},
|
|
207
|
+
cache: true,
|
|
208
|
+
cacheDir: ".autodocs-cache",
|
|
209
|
+
verbose: false,
|
|
210
|
+
ignoreErrors: false
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/config/loader.ts
|
|
214
|
+
var import_cosmiconfig = require("cosmiconfig");
|
|
215
|
+
var import_fs = __toESM(require("fs"));
|
|
216
|
+
var import_path2 = __toESM(require("path"));
|
|
217
|
+
var import_jiti = require("jiti");
|
|
218
|
+
var jiti = (0, import_jiti.createJiti)(__filename);
|
|
219
|
+
async function loadConfig(searchFrom) {
|
|
220
|
+
const explorer = (0, import_cosmiconfig.cosmiconfig)("autodocs", {
|
|
221
|
+
searchPlaces: [
|
|
222
|
+
"autodocs.config.ts",
|
|
223
|
+
"autodocs.config.js",
|
|
224
|
+
"autodocs.config.mjs",
|
|
225
|
+
"autodocs.config.cjs",
|
|
226
|
+
"autodocs.config.json",
|
|
227
|
+
"package.json"
|
|
228
|
+
],
|
|
229
|
+
loaders: {
|
|
230
|
+
".ts": (filepath) => jiti.import(filepath, { default: true })
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
try {
|
|
234
|
+
const resolvedPath = searchFrom ? import_path2.default.resolve(searchFrom) : void 0;
|
|
235
|
+
const hasExplicitConfig = resolvedPath && import_path2.default.extname(resolvedPath) && import_fs.default.existsSync(resolvedPath);
|
|
236
|
+
const result = hasExplicitConfig ? await explorer.load(resolvedPath) : await explorer.search(searchFrom);
|
|
237
|
+
if (!result || result.isEmpty) {
|
|
238
|
+
if (hasExplicitConfig && resolvedPath) {
|
|
239
|
+
const ext = import_path2.default.extname(resolvedPath);
|
|
240
|
+
if (ext === ".json") {
|
|
241
|
+
const raw = await import_fs.default.promises.readFile(resolvedPath, "utf-8");
|
|
242
|
+
return mergeConfig(DEFAULT_CONFIG, JSON.parse(raw));
|
|
243
|
+
}
|
|
244
|
+
if ([".ts", ".js", ".mjs", ".cjs"].includes(ext)) {
|
|
245
|
+
const loaded = await jiti.import(resolvedPath, { default: true });
|
|
246
|
+
const config2 = loaded.default || loaded;
|
|
247
|
+
return mergeConfig(DEFAULT_CONFIG, config2);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const rawConfig = result.config;
|
|
253
|
+
const hasDefaultExport = typeof rawConfig === "object" && rawConfig !== null && "default" in rawConfig;
|
|
254
|
+
const config = hasDefaultExport ? rawConfig.default ?? rawConfig : rawConfig;
|
|
255
|
+
return mergeConfig(DEFAULT_CONFIG, config);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
throw new Error(`Failed to load config: ${error.message}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function mergeConfig(base, override) {
|
|
261
|
+
return {
|
|
262
|
+
...base,
|
|
263
|
+
...override,
|
|
264
|
+
output: {
|
|
265
|
+
...base.output,
|
|
266
|
+
...override.output
|
|
267
|
+
},
|
|
268
|
+
theme: {
|
|
269
|
+
...base.theme,
|
|
270
|
+
...override.theme || {}
|
|
271
|
+
},
|
|
272
|
+
features: {
|
|
273
|
+
...base.features,
|
|
274
|
+
...override.features
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function resolveConfigPaths(config, configDir) {
|
|
279
|
+
const resolveAssetPath = (value) => {
|
|
280
|
+
if (!value) {
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
if (/^(https?:|data:)/.test(value) || value.startsWith("/")) {
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
return import_path2.default.resolve(configDir, value);
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
...config,
|
|
290
|
+
include: config.include.map((p) => import_path2.default.resolve(configDir, p)),
|
|
291
|
+
exclude: config.exclude?.map((p) => import_path2.default.resolve(configDir, p)),
|
|
292
|
+
output: {
|
|
293
|
+
...config.output,
|
|
294
|
+
dir: import_path2.default.resolve(configDir, config.output.dir)
|
|
295
|
+
},
|
|
296
|
+
tsconfig: config.tsconfig ? import_path2.default.resolve(configDir, config.tsconfig) : void 0,
|
|
297
|
+
cacheDir: config.cacheDir ? import_path2.default.resolve(configDir, config.cacheDir) : config.cacheDir,
|
|
298
|
+
theme: config.theme ? {
|
|
299
|
+
...config.theme,
|
|
300
|
+
logo: resolveAssetPath(config.theme.logo),
|
|
301
|
+
favicon: resolveAssetPath(config.theme.favicon)
|
|
302
|
+
} : config.theme
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/utils/configHash.ts
|
|
307
|
+
var import_crypto = __toESM(require("crypto"));
|
|
308
|
+
function computeConfigHash(config) {
|
|
309
|
+
const relevant = {
|
|
310
|
+
tsconfig: config.tsconfig,
|
|
311
|
+
compilerOptions: config.compilerOptions,
|
|
312
|
+
include: config.include,
|
|
313
|
+
exclude: config.exclude ?? [],
|
|
314
|
+
plugins: normalizePlugins(config.plugins ?? [])
|
|
315
|
+
};
|
|
316
|
+
const payload = stableStringify(relevant);
|
|
317
|
+
return import_crypto.default.createHash("sha256").update(payload).digest("hex");
|
|
318
|
+
}
|
|
319
|
+
function normalizePlugins(plugins) {
|
|
320
|
+
return plugins.map((plugin) => {
|
|
321
|
+
if (typeof plugin === "string") {
|
|
322
|
+
return plugin;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
name: plugin.name,
|
|
326
|
+
options: plugin.options ?? {}
|
|
327
|
+
};
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function stableStringify(value) {
|
|
331
|
+
if (Array.isArray(value)) {
|
|
332
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
333
|
+
}
|
|
334
|
+
if (value && typeof value === "object") {
|
|
335
|
+
const entries = Object.entries(value).sort(
|
|
336
|
+
([a], [b]) => a.localeCompare(b)
|
|
337
|
+
);
|
|
338
|
+
return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
|
|
339
|
+
}
|
|
340
|
+
return JSON.stringify(value);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/commands/build.ts
|
|
344
|
+
var import_autodocs_core = require("@opensyntaxhq/autodocs-core");
|
|
345
|
+
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
346
|
+
var ASSET_MIME_TYPES = {
|
|
347
|
+
".svg": "image/svg+xml",
|
|
348
|
+
".png": "image/png",
|
|
349
|
+
".jpg": "image/jpeg",
|
|
350
|
+
".jpeg": "image/jpeg",
|
|
351
|
+
".webp": "image/webp",
|
|
352
|
+
".gif": "image/gif",
|
|
353
|
+
".ico": "image/x-icon"
|
|
354
|
+
};
|
|
355
|
+
async function assetToDataUrl(value) {
|
|
356
|
+
if (!value) {
|
|
357
|
+
return void 0;
|
|
358
|
+
}
|
|
359
|
+
if (/^(https?:|data:)/.test(value)) {
|
|
360
|
+
return value;
|
|
361
|
+
}
|
|
362
|
+
if (!import_path3.default.isAbsolute(value)) {
|
|
363
|
+
return value;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const buffer = await import_promises2.default.readFile(value);
|
|
367
|
+
const ext = import_path3.default.extname(value).toLowerCase();
|
|
368
|
+
const mime = ASSET_MIME_TYPES[ext] || "application/octet-stream";
|
|
369
|
+
return `data:${mime};base64,${buffer.toString("base64")}`;
|
|
370
|
+
} catch {
|
|
371
|
+
return void 0;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function isPathLike(value) {
|
|
375
|
+
return value.startsWith(".") || value.startsWith("/") || value.startsWith("file:");
|
|
376
|
+
}
|
|
377
|
+
async function loadPluginModule(name, configDir) {
|
|
378
|
+
if (name.startsWith("file:")) {
|
|
379
|
+
return import(name);
|
|
380
|
+
}
|
|
381
|
+
if (isPathLike(name)) {
|
|
382
|
+
const resolvedPath = import_path3.default.resolve(configDir, name);
|
|
383
|
+
return import((0, import_url.pathToFileURL)(resolvedPath).href);
|
|
384
|
+
}
|
|
385
|
+
return import(name);
|
|
386
|
+
}
|
|
387
|
+
function resolvePluginExport(exported, options) {
|
|
388
|
+
if (typeof exported === "function") {
|
|
389
|
+
return exported(options);
|
|
390
|
+
}
|
|
391
|
+
return exported;
|
|
392
|
+
}
|
|
393
|
+
async function loadPlugins(pluginManager, plugins, configDir) {
|
|
394
|
+
if (!plugins || plugins.length === 0) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
for (const plugin of plugins) {
|
|
398
|
+
if (typeof plugin === "string") {
|
|
399
|
+
if (isPathLike(plugin)) {
|
|
400
|
+
const module3 = await loadPluginModule(plugin, configDir);
|
|
401
|
+
const exported2 = module3.default ?? module3;
|
|
402
|
+
const instance2 = resolvePluginExport(exported2);
|
|
403
|
+
await pluginManager.loadPlugin(instance2);
|
|
404
|
+
} else {
|
|
405
|
+
await pluginManager.loadPlugin(plugin);
|
|
406
|
+
}
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const module2 = await loadPluginModule(plugin.name, configDir);
|
|
410
|
+
const exported = module2.default ?? module2;
|
|
411
|
+
const instance = resolvePluginExport(exported, plugin.options ?? {});
|
|
412
|
+
await pluginManager.loadPlugin(instance);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async function copySidebarFiles(sidebar, outputDir, configDir) {
|
|
416
|
+
if (!sidebar || !configDir) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
for (const item of sidebar) {
|
|
420
|
+
if (item.items && item.items.length > 0) {
|
|
421
|
+
await copySidebarFiles(item.items, outputDir, configDir);
|
|
422
|
+
}
|
|
423
|
+
if (!item.path || !item.path.endsWith(".md")) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (/^(https?:|data:)/.test(item.path)) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
const relativePath = item.path.startsWith("/") ? item.path.slice(1) : item.path;
|
|
430
|
+
const sourcePath = import_path3.default.resolve(configDir, relativePath);
|
|
431
|
+
try {
|
|
432
|
+
await import_promises2.default.access(sourcePath);
|
|
433
|
+
} catch {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const destinationPath = import_path3.default.join(outputDir, relativePath);
|
|
437
|
+
await import_promises2.default.mkdir(import_path3.default.dirname(destinationPath), { recursive: true });
|
|
438
|
+
await import_promises2.default.copyFile(sourcePath, destinationPath);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async function copyDirectory(src, dest) {
|
|
442
|
+
await import_promises2.default.mkdir(dest, { recursive: true });
|
|
443
|
+
const entries = await import_promises2.default.readdir(src, { withFileTypes: true });
|
|
444
|
+
for (const entry of entries) {
|
|
445
|
+
const srcPath = import_path3.default.join(src, entry.name);
|
|
446
|
+
const destPath = import_path3.default.join(dest, entry.name);
|
|
447
|
+
if (entry.isDirectory()) {
|
|
448
|
+
await copyDirectory(srcPath, destPath);
|
|
449
|
+
} else {
|
|
450
|
+
await import_promises2.default.copyFile(srcPath, destPath);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async function buildUiConfigPayload(uiConfig) {
|
|
455
|
+
const theme = uiConfig.theme ? {
|
|
456
|
+
...uiConfig.theme,
|
|
457
|
+
logo: await assetToDataUrl(uiConfig.theme.logo),
|
|
458
|
+
favicon: await assetToDataUrl(uiConfig.theme.favicon)
|
|
459
|
+
} : void 0;
|
|
460
|
+
return {
|
|
461
|
+
version: import_autodocs_core.VERSION,
|
|
462
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
463
|
+
theme,
|
|
464
|
+
features: uiConfig.features,
|
|
465
|
+
sidebar: uiConfig.sidebar
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
async function writeStaticDocs(docs, outputDir, options) {
|
|
469
|
+
await import_promises2.default.mkdir(outputDir, { recursive: true });
|
|
470
|
+
await copySidebarFiles(options.uiConfig.sidebar, outputDir, options.configDir);
|
|
471
|
+
const { generateJson } = await import("@opensyntaxhq/autodocs-core");
|
|
472
|
+
await generateJson(docs, outputDir, { pretty: true, rootDir: options.rootDir });
|
|
473
|
+
const configData = await buildUiConfigPayload(options.uiConfig);
|
|
474
|
+
await import_promises2.default.writeFile(
|
|
475
|
+
import_path3.default.join(outputDir, "config.json"),
|
|
476
|
+
JSON.stringify(configData, null, 2),
|
|
477
|
+
"utf-8"
|
|
478
|
+
);
|
|
479
|
+
await (0, import_autodocs_core.generateStaticSite)({
|
|
480
|
+
outputDir,
|
|
481
|
+
siteUrl: options.siteUrl,
|
|
482
|
+
siteName: options.siteName ?? "Autodocs"
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
async function buildReactUI(docs, outputDir, spinner, options) {
|
|
486
|
+
let uiDir;
|
|
487
|
+
let uiDistDir;
|
|
488
|
+
if (options.uiDir) {
|
|
489
|
+
uiDir = options.uiDir;
|
|
490
|
+
uiDistDir = import_path3.default.join(uiDir, "dist");
|
|
491
|
+
} else {
|
|
492
|
+
try {
|
|
493
|
+
const uiPackageJson = require.resolve("@opensyntaxhq/autodocs-ui/package.json");
|
|
494
|
+
uiDir = import_path3.default.dirname(uiPackageJson);
|
|
495
|
+
uiDistDir = import_path3.default.join(uiDir, "dist");
|
|
496
|
+
} catch {
|
|
497
|
+
uiDir = import_path3.default.resolve(__dirname, "../../ui");
|
|
498
|
+
uiDistDir = import_path3.default.join(uiDir, "dist");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
await import_promises2.default.access(uiDir);
|
|
503
|
+
} catch {
|
|
504
|
+
spinner.text = "React UI not found, using basic HTML generator...";
|
|
505
|
+
const { generateHtml } = await import("@opensyntaxhq/autodocs-core");
|
|
506
|
+
await generateHtml(docs, outputDir);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
spinner.text = "Building React UI...";
|
|
510
|
+
try {
|
|
511
|
+
await execAsync("npm run build", { cwd: uiDir });
|
|
512
|
+
} catch {
|
|
513
|
+
spinner.fail(import_chalk2.default.red("Failed to build React UI, falling back to basic HTML"));
|
|
514
|
+
const { generateHtml } = await import("@opensyntaxhq/autodocs-core");
|
|
515
|
+
await generateHtml(docs, outputDir);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
spinner.succeed(import_chalk2.default.green("React UI built"));
|
|
519
|
+
spinner.start("Preparing output directory...");
|
|
520
|
+
await import_promises2.default.rm(outputDir, { recursive: true, force: true });
|
|
521
|
+
await import_promises2.default.mkdir(outputDir, { recursive: true });
|
|
522
|
+
spinner.text = "Copying UI assets...";
|
|
523
|
+
await copyDirectory(uiDistDir, outputDir);
|
|
524
|
+
spinner.succeed(import_chalk2.default.green("UI assets copied"));
|
|
525
|
+
spinner.start("Generating docs data...");
|
|
526
|
+
await writeStaticDocs(docs, outputDir, {
|
|
527
|
+
rootDir: options.rootDir,
|
|
528
|
+
configDir: options.configDir,
|
|
529
|
+
uiConfig: options.uiConfig,
|
|
530
|
+
siteUrl: options.siteUrl,
|
|
531
|
+
siteName: options.siteName
|
|
532
|
+
});
|
|
533
|
+
spinner.succeed(import_chalk2.default.green("Documentation data generated"));
|
|
534
|
+
}
|
|
535
|
+
function registerBuild(program2) {
|
|
536
|
+
program2.command("build").description("Build documentation").option("-c, --config <path>", "Config file path").option("-o, --output <dir>", "Output directory (overrides config)").option("--format <format>", "Output format (overrides config)").option("--clean", "Clean output directory first").option("-v, --verbose", "Verbose logging").option("--no-cache", "Disable caching").action(async (options) => {
|
|
537
|
+
const spinner = (0, import_ora.default)("Loading configuration...").start();
|
|
538
|
+
let pluginManager = null;
|
|
539
|
+
try {
|
|
540
|
+
const opts = options;
|
|
541
|
+
let config = await loadConfig(opts.config);
|
|
542
|
+
if (!config) {
|
|
543
|
+
spinner.fail("No configuration found");
|
|
544
|
+
console.log(import_chalk2.default.yellow("\nRun: autodocs init"));
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
const configDir = opts.config ? import_path3.default.dirname(opts.config) : process.cwd();
|
|
548
|
+
config = resolveConfigPaths(config, configDir);
|
|
549
|
+
if (opts.output) {
|
|
550
|
+
config.output.dir = import_path3.default.resolve(opts.output);
|
|
551
|
+
}
|
|
552
|
+
if (opts.format) {
|
|
553
|
+
config.output.format = opts.format;
|
|
554
|
+
}
|
|
555
|
+
if (opts.clean !== void 0) {
|
|
556
|
+
config.output.clean = opts.clean;
|
|
557
|
+
}
|
|
558
|
+
if (opts.verbose) {
|
|
559
|
+
config.verbose = true;
|
|
560
|
+
}
|
|
561
|
+
if (opts.cache === false) {
|
|
562
|
+
config.cache = false;
|
|
563
|
+
}
|
|
564
|
+
const siteUrl = config.output.siteUrl ?? process.env.SITE_URL;
|
|
565
|
+
const siteName = config.theme?.name && config.theme.name !== "default" ? config.theme.name : "Autodocs";
|
|
566
|
+
spinner.text = "Loading plugins...";
|
|
567
|
+
pluginManager = new import_autodocs_core.PluginManager(config);
|
|
568
|
+
const manager = pluginManager;
|
|
569
|
+
await loadPlugins(manager, config.plugins, configDir);
|
|
570
|
+
spinner.text = "Finding source files...";
|
|
571
|
+
let files = await (0, import_glob.glob)(config.include, {
|
|
572
|
+
ignore: config.exclude || [],
|
|
573
|
+
absolute: true
|
|
574
|
+
});
|
|
575
|
+
files = await manager.runHook("beforeParse", files);
|
|
576
|
+
if (files.length === 0) {
|
|
577
|
+
spinner.fail("No files found matching include patterns");
|
|
578
|
+
console.log(import_chalk2.default.yellow("\nInclude patterns:"));
|
|
579
|
+
config.include.forEach((p) => {
|
|
580
|
+
console.log(import_chalk2.default.yellow(` ${p}`));
|
|
581
|
+
});
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
spinner.succeed(import_chalk2.default.green(`Found ${files.length.toString()} files`));
|
|
585
|
+
const configHash = computeConfigHash(config);
|
|
586
|
+
let docs = [];
|
|
587
|
+
let rootDir = process.cwd();
|
|
588
|
+
let diagnostics = [];
|
|
589
|
+
spinner.start("Parsing TypeScript...");
|
|
590
|
+
if (config.cache !== false) {
|
|
591
|
+
const cache = new import_autodocs_core.FileCache({
|
|
592
|
+
cacheDir: config.cacheDir || import_path3.default.join(configDir, ".autodocs-cache"),
|
|
593
|
+
enabled: true
|
|
594
|
+
});
|
|
595
|
+
const result = await (0, import_autodocs_core.incrementalBuild)({
|
|
596
|
+
files,
|
|
597
|
+
cache,
|
|
598
|
+
tsconfig: config.tsconfig,
|
|
599
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
600
|
+
compilerOptions: config.compilerOptions,
|
|
601
|
+
configHash,
|
|
602
|
+
onProgram: async (program3, parsedSourceFiles) => {
|
|
603
|
+
await manager.runHook("afterParse", program3);
|
|
604
|
+
await manager.runHook("beforeExtract", parsedSourceFiles);
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
docs = result.docs;
|
|
608
|
+
rootDir = result.rootDir;
|
|
609
|
+
diagnostics = result.diagnostics;
|
|
610
|
+
spinner.succeed(
|
|
611
|
+
import_chalk2.default.green(
|
|
612
|
+
`Processed ${result.changedFiles.length.toString()} changed files (${result.fromCache.toString()} from cache)`
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
} else {
|
|
616
|
+
const parseResult = (0, import_autodocs_core.createProgram)(files, {
|
|
617
|
+
configFile: config.tsconfig,
|
|
618
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
619
|
+
compilerOptions: config.compilerOptions,
|
|
620
|
+
skipLibCheck: true
|
|
621
|
+
});
|
|
622
|
+
await manager.runHook("afterParse", parseResult.program);
|
|
623
|
+
await manager.runHook("beforeExtract", parseResult.sourceFiles);
|
|
624
|
+
docs = (0, import_autodocs_core.extractDocs)(parseResult.program, { rootDir: parseResult.rootDir });
|
|
625
|
+
rootDir = parseResult.rootDir;
|
|
626
|
+
diagnostics = parseResult.diagnostics;
|
|
627
|
+
spinner.succeed(import_chalk2.default.green("TypeScript parsed"));
|
|
628
|
+
}
|
|
629
|
+
if (config.verbose && diagnostics.length > 0) {
|
|
630
|
+
spinner.info("TypeScript diagnostics:");
|
|
631
|
+
diagnostics.forEach((d) => {
|
|
632
|
+
const message = typeof d.messageText === "string" ? d.messageText : d.messageText.messageText;
|
|
633
|
+
console.log(import_chalk2.default.gray(` ${message}`));
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
spinner.start("Extracting documentation...");
|
|
637
|
+
docs = await manager.runHook("afterExtract", docs);
|
|
638
|
+
if (docs.length === 0) {
|
|
639
|
+
spinner.warn("No exported symbols found to document");
|
|
640
|
+
console.log(
|
|
641
|
+
import_chalk2.default.yellow("\nMake sure your code exports interfaces, types, or functions")
|
|
642
|
+
);
|
|
643
|
+
process.exit(0);
|
|
644
|
+
}
|
|
645
|
+
spinner.succeed(import_chalk2.default.green(`Extracted ${docs.length.toString()} entries`));
|
|
646
|
+
docs = await manager.runHook("beforeGenerate", docs);
|
|
647
|
+
spinner.start("Generating documentation...");
|
|
648
|
+
switch (config.output.format) {
|
|
649
|
+
case "json": {
|
|
650
|
+
const { generateJson } = await import("@opensyntaxhq/autodocs-core");
|
|
651
|
+
await generateJson(docs, config.output.dir, {
|
|
652
|
+
pretty: true,
|
|
653
|
+
rootDir
|
|
654
|
+
});
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
case "markdown": {
|
|
658
|
+
const { generateMarkdown } = await import("@opensyntaxhq/autodocs-core");
|
|
659
|
+
await generateMarkdown(docs, config.output.dir);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
case "static":
|
|
663
|
+
default: {
|
|
664
|
+
await buildReactUI(docs, config.output.dir, spinner, {
|
|
665
|
+
rootDir,
|
|
666
|
+
configDir,
|
|
667
|
+
uiConfig: {
|
|
668
|
+
theme: config.theme,
|
|
669
|
+
features: config.features,
|
|
670
|
+
sidebar: config.sidebar
|
|
671
|
+
},
|
|
672
|
+
siteUrl,
|
|
673
|
+
siteName
|
|
674
|
+
});
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
await manager.runHook("afterGenerate", config.output.dir);
|
|
679
|
+
await manager.cleanup();
|
|
680
|
+
spinner.succeed(import_chalk2.default.green("Documentation generated!"));
|
|
681
|
+
console.log(import_chalk2.default.cyan("\nStatistics:"));
|
|
682
|
+
console.log(` Files processed: ${files.length.toString()}`);
|
|
683
|
+
console.log(` Entries generated: ${docs.length.toString()}`);
|
|
684
|
+
console.log(` Output: ${config.output.dir}`);
|
|
685
|
+
const kindCounts = {};
|
|
686
|
+
docs.forEach((d) => {
|
|
687
|
+
kindCounts[d.kind] = (kindCounts[d.kind] || 0) + 1;
|
|
688
|
+
});
|
|
689
|
+
console.log(import_chalk2.default.cyan("\nBy kind:"));
|
|
690
|
+
Object.entries(kindCounts).forEach(([kind, count]) => {
|
|
691
|
+
console.log(` ${kind}: ${count.toString()}`);
|
|
692
|
+
});
|
|
693
|
+
} catch (error) {
|
|
694
|
+
spinner.fail(import_chalk2.default.red("Build failed"));
|
|
695
|
+
console.error(error);
|
|
696
|
+
if (pluginManager) {
|
|
697
|
+
await pluginManager.cleanup();
|
|
698
|
+
}
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/commands/check.ts
|
|
705
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
706
|
+
var import_ora2 = __toESM(require("ora"));
|
|
707
|
+
var import_path4 = __toESM(require("path"));
|
|
708
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
709
|
+
var import_glob2 = require("glob");
|
|
710
|
+
function registerCheck(program2) {
|
|
711
|
+
program2.command("check").description("Validate configuration and check for issues").option("-c, --config <path>", "Config file path").action(async (options) => {
|
|
712
|
+
const spinner = (0, import_ora2.default)("Checking configuration...").start();
|
|
713
|
+
try {
|
|
714
|
+
const opts = options;
|
|
715
|
+
let config = await loadConfig(opts.config);
|
|
716
|
+
if (!config) {
|
|
717
|
+
spinner.fail("No configuration found");
|
|
718
|
+
console.log(import_chalk3.default.yellow("\nRun: autodocs init"));
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
const configDir = opts.config ? import_path4.default.dirname(opts.config) : process.cwd();
|
|
722
|
+
config = resolveConfigPaths(config, configDir);
|
|
723
|
+
spinner.succeed(import_chalk3.default.green("Configuration loaded"));
|
|
724
|
+
const issues = [];
|
|
725
|
+
const warnings = [];
|
|
726
|
+
spinner.start("Checking source files...");
|
|
727
|
+
const files = await (0, import_glob2.glob)(config.include, {
|
|
728
|
+
ignore: config.exclude || [],
|
|
729
|
+
absolute: true
|
|
730
|
+
});
|
|
731
|
+
if (files.length === 0) {
|
|
732
|
+
issues.push("No files found matching include patterns");
|
|
733
|
+
} else {
|
|
734
|
+
spinner.succeed(import_chalk3.default.green(`Found ${files.length.toString()} source files`));
|
|
735
|
+
}
|
|
736
|
+
if (config.tsconfig) {
|
|
737
|
+
spinner.start("Checking tsconfig...");
|
|
738
|
+
try {
|
|
739
|
+
await import_promises3.default.access(config.tsconfig);
|
|
740
|
+
spinner.succeed(import_chalk3.default.green("tsconfig.json found"));
|
|
741
|
+
} catch {
|
|
742
|
+
warnings.push(`tsconfig.json not found: ${config.tsconfig}`);
|
|
743
|
+
spinner.warn(import_chalk3.default.yellow("tsconfig.json not found"));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
spinner.start("Checking output directory...");
|
|
747
|
+
try {
|
|
748
|
+
const stat = await import_promises3.default.stat(config.output.dir);
|
|
749
|
+
if (!stat.isDirectory()) {
|
|
750
|
+
issues.push(`Output path exists but is not a directory: ${config.output.dir}`);
|
|
751
|
+
}
|
|
752
|
+
spinner.succeed(import_chalk3.default.green("Output directory exists"));
|
|
753
|
+
} catch {
|
|
754
|
+
spinner.info("Output directory will be created");
|
|
755
|
+
}
|
|
756
|
+
console.log(import_chalk3.default.cyan("\n=== Summary ==="));
|
|
757
|
+
if (issues.length === 0 && warnings.length === 0) {
|
|
758
|
+
console.log(import_chalk3.default.green("\u2713 No issues found"));
|
|
759
|
+
} else {
|
|
760
|
+
if (issues.length > 0) {
|
|
761
|
+
console.log(import_chalk3.default.red("\nErrors:"));
|
|
762
|
+
issues.forEach((issue) => {
|
|
763
|
+
console.log(import_chalk3.default.red(` \u2717 ${issue}`));
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
if (warnings.length > 0) {
|
|
767
|
+
console.log(import_chalk3.default.yellow("\nWarnings:"));
|
|
768
|
+
warnings.forEach((warn) => {
|
|
769
|
+
console.log(import_chalk3.default.yellow(` \u26A0 ${warn}`));
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (issues.length > 0) {
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
776
|
+
} catch (error) {
|
|
777
|
+
spinner.fail("Check failed");
|
|
778
|
+
console.error(error);
|
|
779
|
+
process.exit(1);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/commands/serve.ts
|
|
785
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
786
|
+
var import_path5 = __toESM(require("path"));
|
|
787
|
+
var import_fs2 = __toESM(require("fs"));
|
|
788
|
+
function registerServe(program2) {
|
|
789
|
+
program2.command("serve").description("Serve documentation locally").option("-p, --port <port>", "Port number", "3000").option("--host <host>", "Host address", "localhost").option("-o, --open", "Open browser automatically").option("--docs <dir>", "Docs directory to serve", "./docs-dist").action(async (options) => {
|
|
790
|
+
try {
|
|
791
|
+
const opts = options;
|
|
792
|
+
const express = await import("express");
|
|
793
|
+
const open = opts.open ? await import("open") : null;
|
|
794
|
+
const app = express.default();
|
|
795
|
+
const docsDir = import_path5.default.resolve(opts.docs);
|
|
796
|
+
if (!import_fs2.default.existsSync(docsDir)) {
|
|
797
|
+
console.error(import_chalk4.default.red("Error: Docs directory not found:", docsDir));
|
|
798
|
+
console.log(import_chalk4.default.yellow("Run: autodocs build"));
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
801
|
+
app.use(express.default.static(docsDir));
|
|
802
|
+
app.get(/(.*)/, (_req, res) => {
|
|
803
|
+
res.sendFile(import_path5.default.join(docsDir, "index.html"), (err) => {
|
|
804
|
+
if (err) {
|
|
805
|
+
res.status(404).send("Documentation not found. Run: autodocs build");
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
app.listen(parseInt(opts.port), opts.host, () => {
|
|
810
|
+
const url = `http://${opts.host}:${opts.port}`;
|
|
811
|
+
console.log(import_chalk4.default.green("\u2713 Server running at:"), import_chalk4.default.cyan(url));
|
|
812
|
+
console.log(import_chalk4.default.gray("\nPress Ctrl+C to stop"));
|
|
813
|
+
if (open) {
|
|
814
|
+
open.default(url);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
} catch (error) {
|
|
818
|
+
console.error(import_chalk4.default.red("Error starting server:"), error);
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/commands/watch.ts
|
|
825
|
+
var import_path6 = __toESM(require("path"));
|
|
826
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
827
|
+
var import_ora3 = __toESM(require("ora"));
|
|
828
|
+
var import_glob3 = require("glob");
|
|
829
|
+
|
|
830
|
+
// src/utils/watcher.ts
|
|
831
|
+
var import_chokidar = require("chokidar");
|
|
832
|
+
var import_events = require("events");
|
|
833
|
+
var FileWatcher = class extends import_events.EventEmitter {
|
|
834
|
+
watcher = null;
|
|
835
|
+
debounceTimer = null;
|
|
836
|
+
options;
|
|
837
|
+
constructor(options) {
|
|
838
|
+
super();
|
|
839
|
+
this.options = {
|
|
840
|
+
paths: options.paths,
|
|
841
|
+
ignored: options.ignored || ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
842
|
+
debounce: options.debounce || 300
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
start() {
|
|
846
|
+
const watcher = (0, import_chokidar.watch)(this.options.paths, {
|
|
847
|
+
ignored: this.options.ignored,
|
|
848
|
+
persistent: true,
|
|
849
|
+
ignoreInitial: true,
|
|
850
|
+
awaitWriteFinish: {
|
|
851
|
+
stabilityThreshold: 100,
|
|
852
|
+
pollInterval: 100
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
this.watcher = watcher;
|
|
856
|
+
watcher.on("change", (path7) => {
|
|
857
|
+
this.handleChange(path7);
|
|
858
|
+
});
|
|
859
|
+
watcher.on("add", (path7) => {
|
|
860
|
+
this.handleChange(path7);
|
|
861
|
+
});
|
|
862
|
+
watcher.on("unlink", (path7) => {
|
|
863
|
+
this.handleChange(path7);
|
|
864
|
+
});
|
|
865
|
+
this.emit("ready");
|
|
866
|
+
}
|
|
867
|
+
async stop() {
|
|
868
|
+
if (this.watcher) {
|
|
869
|
+
await this.watcher.close();
|
|
870
|
+
this.watcher = null;
|
|
871
|
+
}
|
|
872
|
+
if (this.debounceTimer) {
|
|
873
|
+
clearTimeout(this.debounceTimer);
|
|
874
|
+
this.debounceTimer = null;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
handleChange(path7) {
|
|
878
|
+
if (this.debounceTimer) {
|
|
879
|
+
clearTimeout(this.debounceTimer);
|
|
880
|
+
}
|
|
881
|
+
this.debounceTimer = setTimeout(() => {
|
|
882
|
+
this.emit("change", path7);
|
|
883
|
+
}, this.options.debounce);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
// src/commands/watch.ts
|
|
888
|
+
var import_autodocs_core2 = require("@opensyntaxhq/autodocs-core");
|
|
889
|
+
async function runBuild({ config, configDir, mode }) {
|
|
890
|
+
const spinner = (0, import_ora3.default)("Loading plugins...").start();
|
|
891
|
+
let pluginManager = null;
|
|
892
|
+
try {
|
|
893
|
+
const siteUrl = config.output.siteUrl ?? process.env.SITE_URL;
|
|
894
|
+
const siteName = config.theme?.name && config.theme.name !== "default" ? config.theme.name : "Autodocs";
|
|
895
|
+
pluginManager = new import_autodocs_core2.PluginManager(config);
|
|
896
|
+
const manager = pluginManager;
|
|
897
|
+
await loadPlugins(manager, config.plugins, configDir);
|
|
898
|
+
spinner.text = "Finding source files...";
|
|
899
|
+
let files = await (0, import_glob3.glob)(config.include, {
|
|
900
|
+
ignore: config.exclude || [],
|
|
901
|
+
absolute: true
|
|
902
|
+
});
|
|
903
|
+
files = await manager.runHook("beforeParse", files);
|
|
904
|
+
if (files.length === 0) {
|
|
905
|
+
spinner.fail("No files found matching include patterns");
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const configHash = computeConfigHash(config);
|
|
909
|
+
let docs = [];
|
|
910
|
+
let rootDir = process.cwd();
|
|
911
|
+
spinner.start("Parsing TypeScript...");
|
|
912
|
+
if (config.cache !== false) {
|
|
913
|
+
const cache = new import_autodocs_core2.FileCache({
|
|
914
|
+
cacheDir: config.cacheDir || import_path6.default.join(configDir, ".autodocs-cache"),
|
|
915
|
+
enabled: true
|
|
916
|
+
});
|
|
917
|
+
const result = await (0, import_autodocs_core2.incrementalBuild)({
|
|
918
|
+
files,
|
|
919
|
+
cache,
|
|
920
|
+
tsconfig: config.tsconfig,
|
|
921
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
922
|
+
compilerOptions: config.compilerOptions,
|
|
923
|
+
configHash,
|
|
924
|
+
onProgram: async (program2, sourceFiles) => {
|
|
925
|
+
await manager.runHook("afterParse", program2);
|
|
926
|
+
await manager.runHook("beforeExtract", sourceFiles);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
docs = result.docs;
|
|
930
|
+
rootDir = result.rootDir;
|
|
931
|
+
spinner.succeed(
|
|
932
|
+
import_chalk5.default.green(
|
|
933
|
+
`Processed ${result.changedFiles.length.toString()} changed files (${result.fromCache.toString()} from cache)`
|
|
934
|
+
)
|
|
935
|
+
);
|
|
936
|
+
} else {
|
|
937
|
+
const parseResult = (0, import_autodocs_core2.createProgram)(files, {
|
|
938
|
+
configFile: config.tsconfig,
|
|
939
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
940
|
+
compilerOptions: config.compilerOptions,
|
|
941
|
+
skipLibCheck: true
|
|
942
|
+
});
|
|
943
|
+
await manager.runHook("afterParse", parseResult.program);
|
|
944
|
+
await manager.runHook("beforeExtract", parseResult.sourceFiles);
|
|
945
|
+
docs = (0, import_autodocs_core2.extractDocs)(parseResult.program, { rootDir: parseResult.rootDir });
|
|
946
|
+
rootDir = parseResult.rootDir;
|
|
947
|
+
spinner.succeed(import_chalk5.default.green("TypeScript parsed"));
|
|
948
|
+
}
|
|
949
|
+
spinner.start("Extracting documentation...");
|
|
950
|
+
docs = await manager.runHook("afterExtract", docs);
|
|
951
|
+
docs = await manager.runHook("beforeGenerate", docs);
|
|
952
|
+
spinner.start("Generating documentation...");
|
|
953
|
+
switch (config.output.format) {
|
|
954
|
+
case "json": {
|
|
955
|
+
const { generateJson } = await import("@opensyntaxhq/autodocs-core");
|
|
956
|
+
await generateJson(docs, config.output.dir, { pretty: true, rootDir });
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
case "markdown": {
|
|
960
|
+
const { generateMarkdown } = await import("@opensyntaxhq/autodocs-core");
|
|
961
|
+
await generateMarkdown(docs, config.output.dir);
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
case "static":
|
|
965
|
+
default: {
|
|
966
|
+
if (mode === "full") {
|
|
967
|
+
await buildReactUI(docs, config.output.dir, spinner, {
|
|
968
|
+
rootDir,
|
|
969
|
+
configDir,
|
|
970
|
+
uiConfig: {
|
|
971
|
+
theme: config.theme,
|
|
972
|
+
features: config.features,
|
|
973
|
+
sidebar: config.sidebar
|
|
974
|
+
},
|
|
975
|
+
siteUrl,
|
|
976
|
+
siteName
|
|
977
|
+
});
|
|
978
|
+
} else {
|
|
979
|
+
await writeStaticDocs(docs, config.output.dir, {
|
|
980
|
+
rootDir,
|
|
981
|
+
configDir,
|
|
982
|
+
uiConfig: {
|
|
983
|
+
theme: config.theme,
|
|
984
|
+
features: config.features,
|
|
985
|
+
sidebar: config.sidebar
|
|
986
|
+
},
|
|
987
|
+
siteUrl,
|
|
988
|
+
siteName
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
await manager.runHook("afterGenerate", config.output.dir);
|
|
995
|
+
await manager.cleanup();
|
|
996
|
+
spinner.succeed(import_chalk5.default.green("Documentation generated"));
|
|
997
|
+
} catch (error) {
|
|
998
|
+
spinner.fail("Build failed");
|
|
999
|
+
console.error(error);
|
|
1000
|
+
if (pluginManager) {
|
|
1001
|
+
await pluginManager.cleanup();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function registerWatch(program2) {
|
|
1006
|
+
program2.command("watch").description("Watch files and rebuild on changes").option("-c, --config <path>", "Config file path").action(async (options) => {
|
|
1007
|
+
const spinner = (0, import_ora3.default)("Loading configuration...").start();
|
|
1008
|
+
try {
|
|
1009
|
+
let config = await loadConfig(options.config);
|
|
1010
|
+
if (!config) {
|
|
1011
|
+
spinner.fail("No configuration found");
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const configDir = options.config ? import_path6.default.dirname(options.config) : process.cwd();
|
|
1015
|
+
config = resolveConfigPaths(config, configDir);
|
|
1016
|
+
spinner.succeed("Configuration loaded");
|
|
1017
|
+
console.log(import_chalk5.default.cyan("\nBuilding initial docs..."));
|
|
1018
|
+
await runBuild({ config, configDir, mode: "full" });
|
|
1019
|
+
const watcher = new FileWatcher({
|
|
1020
|
+
paths: config.include,
|
|
1021
|
+
ignored: config.exclude
|
|
1022
|
+
});
|
|
1023
|
+
watcher.on("ready", () => {
|
|
1024
|
+
console.log(import_chalk5.default.green("\n\u2713 Watching for changes..."));
|
|
1025
|
+
console.log(import_chalk5.default.gray("Press Ctrl+C to stop\n"));
|
|
1026
|
+
});
|
|
1027
|
+
watcher.on("change", (changedPath) => {
|
|
1028
|
+
console.log(import_chalk5.default.cyan(`
|
|
1029
|
+
File changed: ${changedPath}`));
|
|
1030
|
+
console.log(import_chalk5.default.cyan("Rebuilding docs...\n"));
|
|
1031
|
+
void runBuild({ config, configDir, mode: "docs-only" }).then(() => {
|
|
1032
|
+
console.log(import_chalk5.default.green("\u2713 Docs updated\n"));
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
watcher.start();
|
|
1036
|
+
process.on("SIGINT", () => {
|
|
1037
|
+
console.log(import_chalk5.default.yellow("\nStopping watcher..."));
|
|
1038
|
+
void watcher.stop().then(() => process.exit(0));
|
|
1039
|
+
});
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
spinner.fail("Watch failed");
|
|
1042
|
+
console.error(error);
|
|
1043
|
+
process.exit(1);
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/index.ts
|
|
1049
|
+
var import_autodocs_core3 = require("@opensyntaxhq/autodocs-core");
|
|
1050
|
+
var program = new import_commander.Command();
|
|
1051
|
+
program.name("autodocs").description("Engineer-first documentation generator").version(import_autodocs_core3.VERSION);
|
|
1052
|
+
registerInit(program);
|
|
1053
|
+
registerBuild(program);
|
|
1054
|
+
registerCheck(program);
|
|
1055
|
+
registerServe(program);
|
|
1056
|
+
registerWatch(program);
|
|
1057
|
+
program.parse(process.argv);
|
|
1058
|
+
if (!process.argv.slice(2).length) {
|
|
1059
|
+
program.outputHelp();
|
|
1060
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opensyntaxhq/autodocs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for Autodocs documentation generator",
|
|
5
|
+
"bin": {
|
|
6
|
+
"autodocs": "dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts src/index-exports.ts --format cjs --dts --no-splitting",
|
|
13
|
+
"dev": "tsup src/index.ts --format cjs --watch",
|
|
14
|
+
"lint": "eslint .",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"type-check": "tsc --noEmit",
|
|
17
|
+
"clean": "rm -rf dist"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index-exports.d.ts",
|
|
22
|
+
"default": "./dist/index-exports.js"
|
|
23
|
+
},
|
|
24
|
+
"./config": {
|
|
25
|
+
"types": "./dist/index-exports.d.ts",
|
|
26
|
+
"default": "./dist/index-exports.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@opensyntaxhq/autodocs-core": "^1.0.0",
|
|
31
|
+
"chalk": "^5.6.2",
|
|
32
|
+
"chokidar": "^5.0.0",
|
|
33
|
+
"commander": "^14.0.3",
|
|
34
|
+
"cosmiconfig": "^9.0.0",
|
|
35
|
+
"express": "^5.2.1",
|
|
36
|
+
"glob": "^13.0.1",
|
|
37
|
+
"inquirer": "^13.2.2",
|
|
38
|
+
"jiti": "^2.6.1",
|
|
39
|
+
"open": "^11.0.0",
|
|
40
|
+
"ora": "^9.3.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/express": "^5.0.6",
|
|
44
|
+
"@types/inquirer": "^8.2.10",
|
|
45
|
+
"@types/jest": "^30.0.0",
|
|
46
|
+
"@types/node": "^25.2.1",
|
|
47
|
+
"jest": "^30.2.0",
|
|
48
|
+
"ts-jest": "^29.4.6",
|
|
49
|
+
"tsup": "^8.0.0",
|
|
50
|
+
"typescript": "^5.9.0"
|
|
51
|
+
},
|
|
52
|
+
"main": "./dist/index-exports.js",
|
|
53
|
+
"types": "./dist/index-exports.d.ts",
|
|
54
|
+
"directories": {
|
|
55
|
+
"test": "tests"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "https://github.com/OpenSyntaxHQ/autodocs.git",
|
|
60
|
+
"directory": "packages/cli"
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/OpenSyntaxHQ/autodocs/issues"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://github.com/OpenSyntaxHQ/autodocs",
|
|
66
|
+
"keywords": [
|
|
67
|
+
"documentation",
|
|
68
|
+
"typescript",
|
|
69
|
+
"cli",
|
|
70
|
+
"api-docs",
|
|
71
|
+
"jsdoc"
|
|
72
|
+
],
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public"
|
|
75
|
+
},
|
|
76
|
+
"author": "",
|
|
77
|
+
"license": "Apache-2.0",
|
|
78
|
+
"type": "commonjs"
|
|
79
|
+
}
|