@optique/man 0.10.7 → 1.0.0-dev.1116
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 +12 -3
- package/dist/cli.cjs +136 -36
- package/dist/cli.js +124 -24
- package/dist/{generator-zoVk04OH.cjs → generator-RyVL5b_V.cjs} +32 -32
- package/dist/{generator-CCa36YC2.js → generator-YM0drazb.js} +31 -2
- package/dist/index.cjs +5 -3
- package/dist/index.d.cts +12 -9
- package/dist/index.d.ts +12 -9
- package/dist/index.js +4 -4
- package/dist/{man-BMb0Vyt9.d.cts → man-B1Q2mhit.d.ts} +34 -3
- package/dist/man-BE1OY_lJ.js +386 -0
- package/dist/man-CkvscTlM.cjs +432 -0
- package/dist/{man-tclTvdWs.d.ts → man-DKwa47XV.d.cts} +33 -2
- package/dist/man.cjs +2 -2
- package/dist/man.d.cts +1 -1
- package/dist/man.d.ts +1 -1
- package/dist/man.js +2 -2
- package/dist/{roff-C_MiRXVS.js → roff-CCdIQO7B.js} +47 -6
- package/dist/{roff-72Am6STB.d.cts → roff-COeDIWWF.d.cts} +49 -2
- package/dist/{roff-B36zMxYc.d.ts → roff-CgYQDHBN.d.ts} +49 -2
- package/dist/{roff-EpcecLXU.cjs → roff-DJ-LkRXl.cjs} +58 -5
- package/dist/roff.cjs +3 -1
- package/dist/roff.d.cts +2 -2
- package/dist/roff.d.ts +2 -2
- package/dist/roff.js +2 -2
- package/package.json +3 -3
- package/dist/man-DGnow1Jr.cjs +0 -235
- package/dist/man-Leuf7kOn.js +0 -218
package/README.md
CHANGED
|
@@ -26,12 +26,18 @@ Quick start
|
|
|
26
26
|
|
|
27
27
|
~~~~ typescript
|
|
28
28
|
import { generateManPage } from "@optique/man";
|
|
29
|
-
import { object
|
|
29
|
+
import { object } from "@optique/core/constructs";
|
|
30
|
+
import { option } from "@optique/core/primitives";
|
|
30
31
|
import { string, integer } from "@optique/core/valueparser";
|
|
32
|
+
import { message } from "@optique/core/message";
|
|
31
33
|
|
|
32
34
|
const parser = object({
|
|
33
|
-
port: option("-p", "--port", integer(), {
|
|
34
|
-
|
|
35
|
+
port: option("-p", "--port", integer(), {
|
|
36
|
+
description: message`Port to listen on`,
|
|
37
|
+
}),
|
|
38
|
+
host: option("-h", "--host", string(), {
|
|
39
|
+
description: message`Host to bind to`,
|
|
40
|
+
}),
|
|
35
41
|
});
|
|
36
42
|
|
|
37
43
|
const manPage = generateManPage(parser, {
|
|
@@ -66,8 +72,11 @@ formatMessageAsRoff(msg); // "Use \\fB\\-\\-help\\fR for more info."
|
|
|
66
72
|
|
|
67
73
|
~~~~ typescript
|
|
68
74
|
import { formatDocPageAsMan } from "@optique/man/man";
|
|
75
|
+
import { message } from "@optique/core/message";
|
|
69
76
|
import type { DocPage } from "@optique/core/doc";
|
|
70
77
|
|
|
78
|
+
declare const docPage: DocPage;
|
|
79
|
+
|
|
71
80
|
const manPage = formatDocPageAsMan(docPage, {
|
|
72
81
|
name: "myapp",
|
|
73
82
|
section: 1,
|
package/dist/cli.cjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const
|
|
3
|
-
require('./roff-
|
|
4
|
-
require('./
|
|
5
|
-
const __optique_core_constructs =
|
|
6
|
-
const __optique_core_primitives =
|
|
7
|
-
const __optique_core_valueparser =
|
|
8
|
-
const __optique_core_modifiers =
|
|
9
|
-
const __optique_core_message =
|
|
10
|
-
const __optique_core_program =
|
|
11
|
-
const __optique_run =
|
|
12
|
-
const node_fs =
|
|
13
|
-
const node_fs_promises =
|
|
14
|
-
const node_path =
|
|
15
|
-
const node_process =
|
|
16
|
-
const node_url =
|
|
2
|
+
const require_man = require('./man-CkvscTlM.cjs');
|
|
3
|
+
require('./roff-DJ-LkRXl.cjs');
|
|
4
|
+
const require_generator = require('./generator-RyVL5b_V.cjs');
|
|
5
|
+
const __optique_core_constructs = require_man.__toESM(require("@optique/core/constructs"));
|
|
6
|
+
const __optique_core_primitives = require_man.__toESM(require("@optique/core/primitives"));
|
|
7
|
+
const __optique_core_valueparser = require_man.__toESM(require("@optique/core/valueparser"));
|
|
8
|
+
const __optique_core_modifiers = require_man.__toESM(require("@optique/core/modifiers"));
|
|
9
|
+
const __optique_core_message = require_man.__toESM(require("@optique/core/message"));
|
|
10
|
+
const __optique_core_program = require_man.__toESM(require("@optique/core/program"));
|
|
11
|
+
const __optique_run = require_man.__toESM(require("@optique/run"));
|
|
12
|
+
const node_fs = require_man.__toESM(require("node:fs"));
|
|
13
|
+
const node_fs_promises = require_man.__toESM(require("node:fs/promises"));
|
|
14
|
+
const node_path = require_man.__toESM(require("node:path"));
|
|
15
|
+
const node_process = require_man.__toESM(require("node:process"));
|
|
16
|
+
const node_url = require_man.__toESM(require("node:url"));
|
|
17
17
|
|
|
18
18
|
//#region deno.json
|
|
19
19
|
var name = "@optique/man";
|
|
20
|
-
var version = "0.
|
|
20
|
+
var version = "1.0.0-dev.1116+6084dd3a";
|
|
21
21
|
var license = "MIT";
|
|
22
22
|
var exports$1 = {
|
|
23
23
|
".": "./src/index.ts",
|
|
@@ -56,6 +56,7 @@ var deno_default = {
|
|
|
56
56
|
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/cli.ts
|
|
59
|
+
const jsxTsxPattern = /\.[mc]?[jt]sx$/;
|
|
59
60
|
const EXIT_FILE_NOT_FOUND = 1;
|
|
60
61
|
const EXIT_EXPORT_NOT_FOUND = 2;
|
|
61
62
|
const EXIT_NOT_PROGRAM_OR_PARSER = 3;
|
|
@@ -89,15 +90,31 @@ a ${(0, __optique_core_message.metavar)("PROGRAM")} (from ${(0, __optique_core_m
|
|
|
89
90
|
a ${(0, __optique_core_message.metavar)("PARSER")}. If not specified, the default export is used.` }), "default"),
|
|
90
91
|
output: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("-o", "--output", (0, __optique_core_valueparser.string)({ metavar: "PATH" }), { description: __optique_core_message.message`Output file path. If not specified, the man page
|
|
91
92
|
is written to stdout.` })),
|
|
92
|
-
name: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--name", (0, __optique_core_valueparser.string)({
|
|
93
|
+
name: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--name", (0, __optique_core_valueparser.string)({
|
|
94
|
+
metavar: "NAME",
|
|
95
|
+
pattern: /.+/,
|
|
96
|
+
errors: { patternMismatch: __optique_core_message.message`Program name must not be empty.` }
|
|
97
|
+
}), { description: __optique_core_message.message`Program name to use in the man page header.
|
|
93
98
|
If not specified, inferred from the ${(0, __optique_core_message.metavar)("PROGRAM")} metadata
|
|
94
99
|
or the input file name.` })),
|
|
95
|
-
date: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--date", (0, __optique_core_valueparser.string)({
|
|
100
|
+
date: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--date", (0, __optique_core_valueparser.string)({
|
|
101
|
+
metavar: "DATE",
|
|
102
|
+
pattern: /.+/,
|
|
103
|
+
errors: { patternMismatch: __optique_core_message.message`Date must not be empty.` }
|
|
104
|
+
}), { description: __optique_core_message.message`Date to display in the man page footer.
|
|
96
105
|
Defaults to the current date.` })),
|
|
97
|
-
versionString: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--version-string", (0, __optique_core_valueparser.string)({
|
|
106
|
+
versionString: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--version-string", (0, __optique_core_valueparser.string)({
|
|
107
|
+
metavar: "VERSION",
|
|
108
|
+
pattern: /.+/,
|
|
109
|
+
errors: { patternMismatch: __optique_core_message.message`Version string must not be empty.` }
|
|
110
|
+
}), { description: __optique_core_message.message`Version string for the man page footer
|
|
98
111
|
(e.g., ${"MyApp 1.0.0"}). Overrides the version from
|
|
99
112
|
${(0, __optique_core_message.metavar)("PROGRAM")} metadata if provided.` })),
|
|
100
|
-
manual: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--manual", (0, __optique_core_valueparser.string)({
|
|
113
|
+
manual: (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)("--manual", (0, __optique_core_valueparser.string)({
|
|
114
|
+
metavar: "TITLE",
|
|
115
|
+
pattern: /.+/,
|
|
116
|
+
errors: { patternMismatch: __optique_core_message.message`Manual name must not be empty.` }
|
|
117
|
+
}), { description: __optique_core_message.message`Manual name for the man page header
|
|
101
118
|
(e.g., ${"User Commands"}).` }))
|
|
102
119
|
}),
|
|
103
120
|
metadata: {
|
|
@@ -187,7 +204,7 @@ an Optique parser (e.g., from ${(0, __optique_core_message.commandLine)("object(
|
|
|
187
204
|
${(0, __optique_core_message.commandLine)("command()")}, etc.).`, { exitCode: EXIT_NOT_PROGRAM_OR_PARSER });
|
|
188
205
|
}
|
|
189
206
|
/**
|
|
190
|
-
* Error handler for missing tsx.
|
|
207
|
+
* Error handler for missing tsx for TypeScript files.
|
|
191
208
|
*/
|
|
192
209
|
function tsxRequiredError(filePath) {
|
|
193
210
|
const version$1 = getNodeMajorMinor();
|
|
@@ -200,6 +217,22 @@ Install tsx as a dev dependency:
|
|
|
200
217
|
|
|
201
218
|
Or upgrade to Node.js 25.2.0 or later, which supports TypeScript natively.
|
|
202
219
|
|
|
220
|
+
Alternatively, use a pre-compiled JavaScript file instead.`, { exitCode: EXIT_TSX_REQUIRED });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Error handler for missing tsx for JSX/TSX files.
|
|
224
|
+
*/
|
|
225
|
+
function jsxLoaderRequiredError(filePath) {
|
|
226
|
+
const version$1 = getNodeMajorMinor();
|
|
227
|
+
const versionStr = version$1 ? `${version$1[0]}.${version$1[1]}` : "unknown";
|
|
228
|
+
const isTsx = /\.[mc]?tsx$/.test(filePath);
|
|
229
|
+
const fileKind = isTsx ? "TSX" : "JSX";
|
|
230
|
+
(0, __optique_run.printError)(__optique_core_message.message`${fileKind} file ${filePath} cannot be loaded on Node.js ${versionStr}.
|
|
231
|
+
|
|
232
|
+
Install tsx as a dev dependency:
|
|
233
|
+
|
|
234
|
+
${(0, __optique_core_message.commandLine)("npm install -D tsx")}
|
|
235
|
+
|
|
203
236
|
Alternatively, use a pre-compiled JavaScript file instead.`, { exitCode: EXIT_TSX_REQUIRED });
|
|
204
237
|
}
|
|
205
238
|
/**
|
|
@@ -218,34 +251,100 @@ Make sure you have write permission and the parent directory exists.`, { exitCod
|
|
|
218
251
|
}
|
|
219
252
|
/**
|
|
220
253
|
* Imports a module from the given file path.
|
|
221
|
-
* Handles TypeScript files on Node.js by using tsx if needed.
|
|
254
|
+
* Handles TypeScript and JSX files on Node.js by using tsx if needed.
|
|
255
|
+
* @param filePath The path to the module file.
|
|
256
|
+
* @returns The imported module's exports.
|
|
257
|
+
* @throws If the module fails to import for reasons other than a missing
|
|
258
|
+
* tsx loader (which causes the process to exit instead).
|
|
222
259
|
*/
|
|
223
260
|
async function importModule(filePath) {
|
|
224
261
|
const absolutePath = (0, node_path.resolve)(filePath);
|
|
225
262
|
if (!(0, node_fs.existsSync)(absolutePath)) fileNotFoundError(filePath);
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
263
|
+
const isPlainTs = /\.[mc]?ts$/.test(filePath);
|
|
264
|
+
const isJsxOrTsx = jsxTsxPattern.test(filePath);
|
|
265
|
+
const isDeno = "Deno" in globalThis;
|
|
266
|
+
const isBun = "Bun" in globalThis;
|
|
267
|
+
if (!isDeno && !isBun && (isJsxOrTsx || isPlainTs && !nodeSupportsNativeTypeScript())) await registerTsx(filePath, isJsxOrTsx);
|
|
268
|
+
else if (!isDeno && !isBun) await tryRegisterTsx();
|
|
269
|
+
const fileUrl = (0, node_url.pathToFileURL)(absolutePath).href;
|
|
270
|
+
try {
|
|
271
|
+
return await import(fileUrl);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (!isDeno && !isBun && isNodeError(error) && error.code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
274
|
+
const failedPath = extractPathFromExtensionError(error.message);
|
|
275
|
+
if (failedPath != null && jsxTsxPattern.test(failedPath)) jsxLoaderRequiredError(failedPath);
|
|
276
|
+
}
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Checks whether an error is a Node.js system error with a string `code`.
|
|
282
|
+
* @param error The value to check.
|
|
283
|
+
* @returns `true` if the error has a string `code` property.
|
|
284
|
+
*/
|
|
285
|
+
function isNodeError(error) {
|
|
286
|
+
return error instanceof Error && "code" in error && typeof error.code === "string";
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Extracts the file path from a Node.js `ERR_UNKNOWN_FILE_EXTENSION` error
|
|
290
|
+
* message, which has the format `Unknown file extension ".ext" for /path`.
|
|
291
|
+
* @param message The error message string.
|
|
292
|
+
* @returns The file path, or null if the message format is unexpected.
|
|
293
|
+
*/
|
|
294
|
+
function extractPathFromExtensionError(message$1) {
|
|
295
|
+
const match = /^Unknown file extension "\.[^"]*" for (.+)$/.exec(message$1);
|
|
296
|
+
return match?.[1] ?? null;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Registers the tsx loader for TypeScript/JSX/TSX support on Node.js.
|
|
300
|
+
* @param filePath The file path being loaded (used for error messages).
|
|
301
|
+
* @param isJsxOrTsx Whether the file uses a JSX or TSX extension.
|
|
302
|
+
*/
|
|
303
|
+
async function registerTsx(filePath, isJsxOrTsx) {
|
|
304
|
+
try {
|
|
230
305
|
const tsx = await import("tsx/esm/api");
|
|
231
306
|
tsx.register();
|
|
232
|
-
} catch {
|
|
233
|
-
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (!isNodeError(error) || error.code !== "ERR_MODULE_NOT_FOUND") throw error;
|
|
309
|
+
if (isJsxOrTsx) jsxLoaderRequiredError(filePath);
|
|
310
|
+
else tsxRequiredError(filePath);
|
|
234
311
|
}
|
|
235
|
-
|
|
236
|
-
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Tries to register the tsx loader, silently ignoring all failures.
|
|
315
|
+
* Used on Node.js for entries that may have transitive JSX/TSX dependencies.
|
|
316
|
+
*
|
|
317
|
+
* All errors are suppressed because this is a best-effort preload: the
|
|
318
|
+
* entry file itself does not require tsx, and failing here would be a
|
|
319
|
+
* regression for environments where tsx is absent, incompatible, or
|
|
320
|
+
* broken. If a transitive JSX/TSX dependency is later encountered
|
|
321
|
+
* without a loader, the caller's catch block produces a helpful error.
|
|
322
|
+
*/
|
|
323
|
+
async function tryRegisterTsx() {
|
|
324
|
+
try {
|
|
325
|
+
const tsx = await import("tsx/esm/api");
|
|
326
|
+
tsx.register();
|
|
327
|
+
} catch {}
|
|
237
328
|
}
|
|
238
329
|
/**
|
|
239
330
|
* Checks if a value is a Program object.
|
|
240
331
|
*/
|
|
241
332
|
function isProgram(value) {
|
|
242
|
-
|
|
333
|
+
try {
|
|
334
|
+
return value != null && typeof value === "object" && "parser" in value && "metadata" in value && typeof value.metadata === "object" && value.metadata != null && typeof value.metadata.name === "string" && isParser(value.parser);
|
|
335
|
+
} catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
243
338
|
}
|
|
244
339
|
/**
|
|
245
340
|
* Checks if a value is a Parser object.
|
|
246
341
|
*/
|
|
247
342
|
function isParser(value) {
|
|
248
|
-
|
|
343
|
+
try {
|
|
344
|
+
return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "$mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
|
|
345
|
+
} catch {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
249
348
|
}
|
|
250
349
|
/**
|
|
251
350
|
* Infers the program name from a file path.
|
|
@@ -253,7 +352,7 @@ function isParser(value) {
|
|
|
253
352
|
function inferNameFromPath(filePath) {
|
|
254
353
|
const base = (0, node_path.basename)(filePath);
|
|
255
354
|
const ext = (0, node_path.extname)(base);
|
|
256
|
-
return base.slice(0, -ext.length);
|
|
355
|
+
return ext.length > 0 ? base.slice(0, -ext.length) : base;
|
|
257
356
|
}
|
|
258
357
|
/**
|
|
259
358
|
* Gets available exports from a module.
|
|
@@ -278,7 +377,7 @@ async function main() {
|
|
|
278
377
|
help: "option",
|
|
279
378
|
version: {
|
|
280
379
|
value: deno_default.version,
|
|
281
|
-
|
|
380
|
+
option: true
|
|
282
381
|
}
|
|
283
382
|
});
|
|
284
383
|
const mod = await importModule(args.file);
|
|
@@ -286,19 +385,20 @@ async function main() {
|
|
|
286
385
|
if (target === void 0) exportNotFoundError(args.file, args.exportName, getAvailableExports(mod));
|
|
287
386
|
if (!isProgram(target) && !isParser(target)) notProgramOrParserError(args.file, args.exportName, describeType(target));
|
|
288
387
|
const name$1 = args.name ?? (isProgram(target) ? target.metadata.name : null) ?? inferNameFromPath(args.file);
|
|
388
|
+
const date = args.date ?? /* @__PURE__ */ new Date();
|
|
289
389
|
let manPage;
|
|
290
390
|
try {
|
|
291
391
|
if (isProgram(target)) manPage = await require_generator.generateManPageAsync(target, {
|
|
292
392
|
section: args.section,
|
|
293
393
|
name: args.name,
|
|
294
|
-
date
|
|
394
|
+
date,
|
|
295
395
|
version: args.versionString,
|
|
296
396
|
manual: args.manual
|
|
297
397
|
});
|
|
298
398
|
else manPage = await require_generator.generateManPageAsync(target, {
|
|
299
399
|
name: name$1,
|
|
300
400
|
section: args.section,
|
|
301
|
-
date
|
|
401
|
+
date,
|
|
302
402
|
version: args.versionString,
|
|
303
403
|
manual: args.manual
|
|
304
404
|
});
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./roff-
|
|
3
|
-
import "./man-
|
|
4
|
-
import { generateManPageAsync } from "./generator-
|
|
2
|
+
import "./roff-CCdIQO7B.js";
|
|
3
|
+
import "./man-BE1OY_lJ.js";
|
|
4
|
+
import { generateManPageAsync } from "./generator-YM0drazb.js";
|
|
5
5
|
import { object } from "@optique/core/constructs";
|
|
6
6
|
import { argument, option } from "@optique/core/primitives";
|
|
7
7
|
import { choice, string } from "@optique/core/valueparser";
|
|
@@ -17,7 +17,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
17
17
|
|
|
18
18
|
//#region deno.json
|
|
19
19
|
var name = "@optique/man";
|
|
20
|
-
var version = "0.
|
|
20
|
+
var version = "1.0.0-dev.1116+6084dd3a";
|
|
21
21
|
var license = "MIT";
|
|
22
22
|
var exports = {
|
|
23
23
|
".": "./src/index.ts",
|
|
@@ -56,6 +56,7 @@ var deno_default = {
|
|
|
56
56
|
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/cli.ts
|
|
59
|
+
const jsxTsxPattern = /\.[mc]?[jt]sx$/;
|
|
59
60
|
const EXIT_FILE_NOT_FOUND = 1;
|
|
60
61
|
const EXIT_EXPORT_NOT_FOUND = 2;
|
|
61
62
|
const EXIT_NOT_PROGRAM_OR_PARSER = 3;
|
|
@@ -89,15 +90,31 @@ a ${metavar("PROGRAM")} (from ${commandLine("defineProgram()")}) or
|
|
|
89
90
|
a ${metavar("PARSER")}. If not specified, the default export is used.` }), "default"),
|
|
90
91
|
output: optional(option("-o", "--output", string({ metavar: "PATH" }), { description: message`Output file path. If not specified, the man page
|
|
91
92
|
is written to stdout.` })),
|
|
92
|
-
name: optional(option("--name", string({
|
|
93
|
+
name: optional(option("--name", string({
|
|
94
|
+
metavar: "NAME",
|
|
95
|
+
pattern: /.+/,
|
|
96
|
+
errors: { patternMismatch: message`Program name must not be empty.` }
|
|
97
|
+
}), { description: message`Program name to use in the man page header.
|
|
93
98
|
If not specified, inferred from the ${metavar("PROGRAM")} metadata
|
|
94
99
|
or the input file name.` })),
|
|
95
|
-
date: optional(option("--date", string({
|
|
100
|
+
date: optional(option("--date", string({
|
|
101
|
+
metavar: "DATE",
|
|
102
|
+
pattern: /.+/,
|
|
103
|
+
errors: { patternMismatch: message`Date must not be empty.` }
|
|
104
|
+
}), { description: message`Date to display in the man page footer.
|
|
96
105
|
Defaults to the current date.` })),
|
|
97
|
-
versionString: optional(option("--version-string", string({
|
|
106
|
+
versionString: optional(option("--version-string", string({
|
|
107
|
+
metavar: "VERSION",
|
|
108
|
+
pattern: /.+/,
|
|
109
|
+
errors: { patternMismatch: message`Version string must not be empty.` }
|
|
110
|
+
}), { description: message`Version string for the man page footer
|
|
98
111
|
(e.g., ${"MyApp 1.0.0"}). Overrides the version from
|
|
99
112
|
${metavar("PROGRAM")} metadata if provided.` })),
|
|
100
|
-
manual: optional(option("--manual", string({
|
|
113
|
+
manual: optional(option("--manual", string({
|
|
114
|
+
metavar: "TITLE",
|
|
115
|
+
pattern: /.+/,
|
|
116
|
+
errors: { patternMismatch: message`Manual name must not be empty.` }
|
|
117
|
+
}), { description: message`Manual name for the man page header
|
|
101
118
|
(e.g., ${"User Commands"}).` }))
|
|
102
119
|
}),
|
|
103
120
|
metadata: {
|
|
@@ -187,7 +204,7 @@ an Optique parser (e.g., from ${commandLine("object()")},
|
|
|
187
204
|
${commandLine("command()")}, etc.).`, { exitCode: EXIT_NOT_PROGRAM_OR_PARSER });
|
|
188
205
|
}
|
|
189
206
|
/**
|
|
190
|
-
* Error handler for missing tsx.
|
|
207
|
+
* Error handler for missing tsx for TypeScript files.
|
|
191
208
|
*/
|
|
192
209
|
function tsxRequiredError(filePath) {
|
|
193
210
|
const version$1 = getNodeMajorMinor();
|
|
@@ -200,6 +217,22 @@ Install tsx as a dev dependency:
|
|
|
200
217
|
|
|
201
218
|
Or upgrade to Node.js 25.2.0 or later, which supports TypeScript natively.
|
|
202
219
|
|
|
220
|
+
Alternatively, use a pre-compiled JavaScript file instead.`, { exitCode: EXIT_TSX_REQUIRED });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Error handler for missing tsx for JSX/TSX files.
|
|
224
|
+
*/
|
|
225
|
+
function jsxLoaderRequiredError(filePath) {
|
|
226
|
+
const version$1 = getNodeMajorMinor();
|
|
227
|
+
const versionStr = version$1 ? `${version$1[0]}.${version$1[1]}` : "unknown";
|
|
228
|
+
const isTsx = /\.[mc]?tsx$/.test(filePath);
|
|
229
|
+
const fileKind = isTsx ? "TSX" : "JSX";
|
|
230
|
+
printError(message`${fileKind} file ${filePath} cannot be loaded on Node.js ${versionStr}.
|
|
231
|
+
|
|
232
|
+
Install tsx as a dev dependency:
|
|
233
|
+
|
|
234
|
+
${commandLine("npm install -D tsx")}
|
|
235
|
+
|
|
203
236
|
Alternatively, use a pre-compiled JavaScript file instead.`, { exitCode: EXIT_TSX_REQUIRED });
|
|
204
237
|
}
|
|
205
238
|
/**
|
|
@@ -218,34 +251,100 @@ Make sure you have write permission and the parent directory exists.`, { exitCod
|
|
|
218
251
|
}
|
|
219
252
|
/**
|
|
220
253
|
* Imports a module from the given file path.
|
|
221
|
-
* Handles TypeScript files on Node.js by using tsx if needed.
|
|
254
|
+
* Handles TypeScript and JSX files on Node.js by using tsx if needed.
|
|
255
|
+
* @param filePath The path to the module file.
|
|
256
|
+
* @returns The imported module's exports.
|
|
257
|
+
* @throws If the module fails to import for reasons other than a missing
|
|
258
|
+
* tsx loader (which causes the process to exit instead).
|
|
222
259
|
*/
|
|
223
260
|
async function importModule(filePath) {
|
|
224
261
|
const absolutePath = resolve(filePath);
|
|
225
262
|
if (!existsSync(absolutePath)) fileNotFoundError(filePath);
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
263
|
+
const isPlainTs = /\.[mc]?ts$/.test(filePath);
|
|
264
|
+
const isJsxOrTsx = jsxTsxPattern.test(filePath);
|
|
265
|
+
const isDeno = "Deno" in globalThis;
|
|
266
|
+
const isBun = "Bun" in globalThis;
|
|
267
|
+
if (!isDeno && !isBun && (isJsxOrTsx || isPlainTs && !nodeSupportsNativeTypeScript())) await registerTsx(filePath, isJsxOrTsx);
|
|
268
|
+
else if (!isDeno && !isBun) await tryRegisterTsx();
|
|
269
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
270
|
+
try {
|
|
271
|
+
return await import(fileUrl);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (!isDeno && !isBun && isNodeError(error) && error.code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
274
|
+
const failedPath = extractPathFromExtensionError(error.message);
|
|
275
|
+
if (failedPath != null && jsxTsxPattern.test(failedPath)) jsxLoaderRequiredError(failedPath);
|
|
276
|
+
}
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Checks whether an error is a Node.js system error with a string `code`.
|
|
282
|
+
* @param error The value to check.
|
|
283
|
+
* @returns `true` if the error has a string `code` property.
|
|
284
|
+
*/
|
|
285
|
+
function isNodeError(error) {
|
|
286
|
+
return error instanceof Error && "code" in error && typeof error.code === "string";
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Extracts the file path from a Node.js `ERR_UNKNOWN_FILE_EXTENSION` error
|
|
290
|
+
* message, which has the format `Unknown file extension ".ext" for /path`.
|
|
291
|
+
* @param message The error message string.
|
|
292
|
+
* @returns The file path, or null if the message format is unexpected.
|
|
293
|
+
*/
|
|
294
|
+
function extractPathFromExtensionError(message$1) {
|
|
295
|
+
const match = /^Unknown file extension "\.[^"]*" for (.+)$/.exec(message$1);
|
|
296
|
+
return match?.[1] ?? null;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Registers the tsx loader for TypeScript/JSX/TSX support on Node.js.
|
|
300
|
+
* @param filePath The file path being loaded (used for error messages).
|
|
301
|
+
* @param isJsxOrTsx Whether the file uses a JSX or TSX extension.
|
|
302
|
+
*/
|
|
303
|
+
async function registerTsx(filePath, isJsxOrTsx) {
|
|
304
|
+
try {
|
|
230
305
|
const tsx = await import("tsx/esm/api");
|
|
231
306
|
tsx.register();
|
|
232
|
-
} catch {
|
|
233
|
-
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (!isNodeError(error) || error.code !== "ERR_MODULE_NOT_FOUND") throw error;
|
|
309
|
+
if (isJsxOrTsx) jsxLoaderRequiredError(filePath);
|
|
310
|
+
else tsxRequiredError(filePath);
|
|
234
311
|
}
|
|
235
|
-
|
|
236
|
-
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Tries to register the tsx loader, silently ignoring all failures.
|
|
315
|
+
* Used on Node.js for entries that may have transitive JSX/TSX dependencies.
|
|
316
|
+
*
|
|
317
|
+
* All errors are suppressed because this is a best-effort preload: the
|
|
318
|
+
* entry file itself does not require tsx, and failing here would be a
|
|
319
|
+
* regression for environments where tsx is absent, incompatible, or
|
|
320
|
+
* broken. If a transitive JSX/TSX dependency is later encountered
|
|
321
|
+
* without a loader, the caller's catch block produces a helpful error.
|
|
322
|
+
*/
|
|
323
|
+
async function tryRegisterTsx() {
|
|
324
|
+
try {
|
|
325
|
+
const tsx = await import("tsx/esm/api");
|
|
326
|
+
tsx.register();
|
|
327
|
+
} catch {}
|
|
237
328
|
}
|
|
238
329
|
/**
|
|
239
330
|
* Checks if a value is a Program object.
|
|
240
331
|
*/
|
|
241
332
|
function isProgram(value) {
|
|
242
|
-
|
|
333
|
+
try {
|
|
334
|
+
return value != null && typeof value === "object" && "parser" in value && "metadata" in value && typeof value.metadata === "object" && value.metadata != null && typeof value.metadata.name === "string" && isParser(value.parser);
|
|
335
|
+
} catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
243
338
|
}
|
|
244
339
|
/**
|
|
245
340
|
* Checks if a value is a Parser object.
|
|
246
341
|
*/
|
|
247
342
|
function isParser(value) {
|
|
248
|
-
|
|
343
|
+
try {
|
|
344
|
+
return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "$mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
|
|
345
|
+
} catch {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
249
348
|
}
|
|
250
349
|
/**
|
|
251
350
|
* Infers the program name from a file path.
|
|
@@ -253,7 +352,7 @@ function isParser(value) {
|
|
|
253
352
|
function inferNameFromPath(filePath) {
|
|
254
353
|
const base = basename(filePath);
|
|
255
354
|
const ext = extname(base);
|
|
256
|
-
return base.slice(0, -ext.length);
|
|
355
|
+
return ext.length > 0 ? base.slice(0, -ext.length) : base;
|
|
257
356
|
}
|
|
258
357
|
/**
|
|
259
358
|
* Gets available exports from a module.
|
|
@@ -278,7 +377,7 @@ async function main() {
|
|
|
278
377
|
help: "option",
|
|
279
378
|
version: {
|
|
280
379
|
value: deno_default.version,
|
|
281
|
-
|
|
380
|
+
option: true
|
|
282
381
|
}
|
|
283
382
|
});
|
|
284
383
|
const mod = await importModule(args.file);
|
|
@@ -286,19 +385,20 @@ async function main() {
|
|
|
286
385
|
if (target === void 0) exportNotFoundError(args.file, args.exportName, getAvailableExports(mod));
|
|
287
386
|
if (!isProgram(target) && !isParser(target)) notProgramOrParserError(args.file, args.exportName, describeType(target));
|
|
288
387
|
const name$1 = args.name ?? (isProgram(target) ? target.metadata.name : null) ?? inferNameFromPath(args.file);
|
|
388
|
+
const date = args.date ?? /* @__PURE__ */ new Date();
|
|
289
389
|
let manPage;
|
|
290
390
|
try {
|
|
291
391
|
if (isProgram(target)) manPage = await generateManPageAsync(target, {
|
|
292
392
|
section: args.section,
|
|
293
393
|
name: args.name,
|
|
294
|
-
date
|
|
394
|
+
date,
|
|
295
395
|
version: args.versionString,
|
|
296
396
|
manual: args.manual
|
|
297
397
|
});
|
|
298
398
|
else manPage = await generateManPageAsync(target, {
|
|
299
399
|
name: name$1,
|
|
300
400
|
section: args.section,
|
|
301
|
-
date
|
|
401
|
+
date,
|
|
302
402
|
version: args.versionString,
|
|
303
403
|
manual: args.manual
|
|
304
404
|
});
|
|
@@ -1,41 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
const require_man = require('./man-DGnow1Jr.cjs');
|
|
25
|
-
const __optique_core_parser = __toESM(require("@optique/core/parser"));
|
|
1
|
+
const require_man = require('./man-CkvscTlM.cjs');
|
|
2
|
+
const __optique_core_parser = require_man.__toESM(require("@optique/core/parser"));
|
|
26
3
|
|
|
27
4
|
//#region src/generator.ts
|
|
28
5
|
/**
|
|
6
|
+
* Checks if the given value looks like a {@link Parser} at runtime.
|
|
7
|
+
*/
|
|
8
|
+
function isParser(value) {
|
|
9
|
+
try {
|
|
10
|
+
if (value == null || typeof value !== "object") return false;
|
|
11
|
+
const p = value;
|
|
12
|
+
return "parse" in p && typeof p.parse === "function" && "complete" in p && typeof p.complete === "function" && "$mode" in p && (p.$mode === "sync" || p.$mode === "async") && "usage" in p && Array.isArray(p.usage) && "initialState" in p && "suggest" in p && typeof p.suggest === "function" && "getDocFragments" in p && typeof p.getDocFragments === "function";
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
29
18
|
* Checks if the given value is a {@link Program} object.
|
|
30
19
|
*/
|
|
31
20
|
function isProgram(value) {
|
|
32
|
-
|
|
21
|
+
try {
|
|
22
|
+
return typeof value === "object" && value !== null && "parser" in value && "metadata" in value && typeof value.metadata === "object" && value.metadata !== null && "name" in value.metadata && typeof value.metadata.name === "string";
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validates that the extracted parser is a genuine Optique parser.
|
|
29
|
+
* @throws {TypeError} If the value is not a valid Parser.
|
|
30
|
+
*/
|
|
31
|
+
function validateParser(value) {
|
|
32
|
+
if (!isParser(value)) throw new TypeError("The given value is not a valid Parser or Program.");
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Extracts parser and merged options from a parser or program.
|
|
36
36
|
*/
|
|
37
37
|
function extractParserAndOptions(parserOrProgram, options) {
|
|
38
38
|
if (isProgram(parserOrProgram)) {
|
|
39
|
+
validateParser(parserOrProgram.parser);
|
|
39
40
|
const { metadata } = parserOrProgram;
|
|
40
41
|
const programOptions = options;
|
|
41
42
|
return {
|
|
@@ -49,6 +50,9 @@ function extractParserAndOptions(parserOrProgram, options) {
|
|
|
49
50
|
author: programOptions.author ?? metadata.author,
|
|
50
51
|
bugs: programOptions.bugs ?? metadata.bugs,
|
|
51
52
|
examples: programOptions.examples ?? metadata.examples,
|
|
53
|
+
brief: programOptions.brief ?? metadata.brief,
|
|
54
|
+
description: programOptions.description ?? metadata.description,
|
|
55
|
+
footer: programOptions.footer ?? metadata.footer,
|
|
52
56
|
seeAlso: programOptions.seeAlso,
|
|
53
57
|
environment: programOptions.environment,
|
|
54
58
|
files: programOptions.files,
|
|
@@ -56,6 +60,7 @@ function extractParserAndOptions(parserOrProgram, options) {
|
|
|
56
60
|
}
|
|
57
61
|
};
|
|
58
62
|
}
|
|
63
|
+
validateParser(parserOrProgram);
|
|
59
64
|
return {
|
|
60
65
|
parser: parserOrProgram,
|
|
61
66
|
mergedOptions: options
|
|
@@ -63,6 +68,7 @@ function extractParserAndOptions(parserOrProgram, options) {
|
|
|
63
68
|
}
|
|
64
69
|
function generateManPageSync(parserOrProgram, options) {
|
|
65
70
|
const { parser, mergedOptions } = extractParserAndOptions(parserOrProgram, options);
|
|
71
|
+
if (parser.$mode === "async") throw new TypeError("Cannot use an async parser with generateManPageSync(). Use generateManPageAsync() or generateManPage() instead.");
|
|
66
72
|
const docPage = (0, __optique_core_parser.getDocPageSync)(parser) ?? { sections: [] };
|
|
67
73
|
return require_man.formatDocPageAsMan(docPage, mergedOptions);
|
|
68
74
|
}
|
|
@@ -78,12 +84,6 @@ function generateManPage(parserOrProgram, options) {
|
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
//#endregion
|
|
81
|
-
Object.defineProperty(exports, '__toESM', {
|
|
82
|
-
enumerable: true,
|
|
83
|
-
get: function () {
|
|
84
|
-
return __toESM;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
87
|
Object.defineProperty(exports, 'generateManPage', {
|
|
88
88
|
enumerable: true,
|
|
89
89
|
get: function () {
|