@nowline/cli 0.2.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/LICENSE +190 -0
- package/README.md +372 -0
- package/dist/cli/args.d.ts +54 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +165 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/formats.d.ts +61 -0
- package/dist/cli/formats.d.ts.map +1 -0
- package/dist/cli/formats.js +153 -0
- package/dist/cli/formats.js.map +1 -0
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +90 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/output-path.d.ts +57 -0
- package/dist/cli/output-path.d.ts.map +1 -0
- package/dist/cli/output-path.js +70 -0
- package/dist/cli/output-path.js.map +1 -0
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +80 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/render.d.ts +15 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +435 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/serve.d.ts +16 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +287 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/convert/parse-json.d.ts +7 -0
- package/dist/convert/parse-json.d.ts.map +1 -0
- package/dist/convert/parse-json.js +34 -0
- package/dist/convert/parse-json.js.map +1 -0
- package/dist/convert/printer.d.ts +6 -0
- package/dist/convert/printer.d.ts.map +1 -0
- package/dist/convert/printer.js +334 -0
- package/dist/convert/printer.js.map +1 -0
- package/dist/convert/schema.d.ts +33 -0
- package/dist/convert/schema.d.ts.map +1 -0
- package/dist/convert/schema.js +77 -0
- package/dist/convert/schema.js.map +1 -0
- package/dist/core/parse.d.ts +24 -0
- package/dist/core/parse.d.ts.map +1 -0
- package/dist/core/parse.js +58 -0
- package/dist/core/parse.js.map +1 -0
- package/dist/diagnostics/adapt.d.ts +46 -0
- package/dist/diagnostics/adapt.d.ts.map +1 -0
- package/dist/diagnostics/adapt.js +109 -0
- package/dist/diagnostics/adapt.js.map +1 -0
- package/dist/diagnostics/format.d.ts +18 -0
- package/dist/diagnostics/format.d.ts.map +1 -0
- package/dist/diagnostics/format.js +41 -0
- package/dist/diagnostics/format.js.map +1 -0
- package/dist/diagnostics/index.d.ts +5 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +5 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/diagnostics/json.d.ts +8 -0
- package/dist/diagnostics/json.d.ts.map +1 -0
- package/dist/diagnostics/json.js +24 -0
- package/dist/diagnostics/json.js.map +1 -0
- package/dist/diagnostics/model.d.ts +44 -0
- package/dist/diagnostics/model.d.ts.map +1 -0
- package/dist/diagnostics/model.js +2 -0
- package/dist/diagnostics/model.js.map +1 -0
- package/dist/diagnostics/text.d.ts +6 -0
- package/dist/diagnostics/text.d.ts.map +1 -0
- package/dist/diagnostics/text.js +43 -0
- package/dist/diagnostics/text.js.map +1 -0
- package/dist/generated/templates.d.ts +4 -0
- package/dist/generated/templates.d.ts.map +1 -0
- package/dist/generated/templates.js +9 -0
- package/dist/generated/templates.js.map +1 -0
- package/dist/generated/version.d.ts +11 -0
- package/dist/generated/version.d.ts.map +1 -0
- package/dist/generated/version.js +8 -0
- package/dist/generated/version.js.map +1 -0
- package/dist/i18n/locale.d.ts +56 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +107 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/io/config.d.ts +2 -0
- package/dist/io/config.d.ts.map +1 -0
- package/dist/io/config.js +5 -0
- package/dist/io/config.js.map +1 -0
- package/dist/io/exit-codes.d.ts +12 -0
- package/dist/io/exit-codes.d.ts.map +1 -0
- package/dist/io/exit-codes.js +15 -0
- package/dist/io/exit-codes.js.map +1 -0
- package/dist/io/read.d.ts +13 -0
- package/dist/io/read.d.ts.map +1 -0
- package/dist/io/read.js +53 -0
- package/dist/io/read.js.map +1 -0
- package/dist/io/write.d.ts +32 -0
- package/dist/io/write.d.ts.map +1 -0
- package/dist/io/write.js +61 -0
- package/dist/io/write.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +20 -0
- package/dist/version.js.map +1 -0
- package/man/fr/nowline.1 +424 -0
- package/man/fr/nowline.5 +1864 -0
- package/man/nowline.1 +517 -0
- package/man/nowline.5 +1784 -0
- package/package.json +66 -0
- package/scripts/bundle-templates.mjs +105 -0
- package/scripts/compile.mjs +131 -0
- package/src/cli/args.ts +252 -0
- package/src/cli/formats.ts +207 -0
- package/src/cli/help.ts +92 -0
- package/src/cli/output-path.ts +98 -0
- package/src/commands/init.ts +99 -0
- package/src/commands/render.ts +566 -0
- package/src/commands/serve.ts +322 -0
- package/src/convert/parse-json.ts +57 -0
- package/src/convert/printer.ts +376 -0
- package/src/convert/schema.ts +105 -0
- package/src/core/parse.ts +93 -0
- package/src/diagnostics/adapt.ts +148 -0
- package/src/diagnostics/format.ts +70 -0
- package/src/diagnostics/index.ts +4 -0
- package/src/diagnostics/json.ts +30 -0
- package/src/diagnostics/model.ts +48 -0
- package/src/diagnostics/text.ts +62 -0
- package/src/generated/templates.ts +12 -0
- package/src/generated/version.ts +18 -0
- package/src/i18n/locale.ts +133 -0
- package/src/index.ts +60 -0
- package/src/io/config.ts +11 -0
- package/src/io/exit-codes.ts +18 -0
- package/src/io/read.ts +70 -0
- package/src/io/write.ts +94 -0
- package/src/version.ts +21 -0
package/src/io/read.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { CliError, ExitCode } from './exit-codes.js';
|
|
4
|
+
|
|
5
|
+
export interface ReadInputResult {
|
|
6
|
+
contents: string;
|
|
7
|
+
path: string;
|
|
8
|
+
displayPath: string;
|
|
9
|
+
isStdin: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ReadInputOptions {
|
|
13
|
+
readFile?: (absPath: string) => Promise<string>;
|
|
14
|
+
readStdin?: () => Promise<string>;
|
|
15
|
+
cwd?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function readInput(
|
|
19
|
+
inputArg: string,
|
|
20
|
+
options: ReadInputOptions = {},
|
|
21
|
+
): Promise<ReadInputResult> {
|
|
22
|
+
const readFile = options.readFile ?? ((p) => fs.readFile(p, 'utf-8'));
|
|
23
|
+
const readStdin = options.readStdin ?? defaultReadStdin;
|
|
24
|
+
const cwd = options.cwd ?? process.cwd();
|
|
25
|
+
|
|
26
|
+
if (inputArg === '-') {
|
|
27
|
+
return {
|
|
28
|
+
contents: await readStdin(),
|
|
29
|
+
path: '<stdin>',
|
|
30
|
+
displayPath: '<stdin>',
|
|
31
|
+
isStdin: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const absPath = path.resolve(cwd, inputArg);
|
|
36
|
+
try {
|
|
37
|
+
const contents = await readFile(absPath);
|
|
38
|
+
return {
|
|
39
|
+
contents,
|
|
40
|
+
path: absPath,
|
|
41
|
+
displayPath: inputArg,
|
|
42
|
+
isStdin: false,
|
|
43
|
+
};
|
|
44
|
+
} catch (err) {
|
|
45
|
+
throw new CliError(ExitCode.InputError, formatReadError(inputArg, err));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatReadError(inputArg: string, err: unknown): string {
|
|
50
|
+
if (isNodeError(err)) {
|
|
51
|
+
if (err.code === 'ENOENT') return `File not found: ${inputArg}`;
|
|
52
|
+
if (err.code === 'EACCES') return `Permission denied: ${inputArg}`;
|
|
53
|
+
if (err.code === 'EISDIR') return `Not a file: ${inputArg}`;
|
|
54
|
+
}
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
return `Could not read ${inputArg}: ${message}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isNodeError(err: unknown): err is NodeJS.ErrnoException {
|
|
60
|
+
return err instanceof Error && typeof (err as { code?: unknown }).code === 'string';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function defaultReadStdin(): Promise<string> {
|
|
64
|
+
let data = '';
|
|
65
|
+
process.stdin.setEncoding('utf-8');
|
|
66
|
+
for await (const chunk of process.stdin) {
|
|
67
|
+
data += chunk;
|
|
68
|
+
}
|
|
69
|
+
return data;
|
|
70
|
+
}
|
package/src/io/write.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { CliError, ExitCode } from './exit-codes.js';
|
|
4
|
+
|
|
5
|
+
export interface WriteOutputOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Current working directory used to resolve relative output paths. Defaults
|
|
8
|
+
* to `process.cwd()`.
|
|
9
|
+
*/
|
|
10
|
+
cwd?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Test seam: writes the output bytes to `absPath`. Defaults to
|
|
13
|
+
* `fs.writeFile`.
|
|
14
|
+
*/
|
|
15
|
+
writeFile?: (absPath: string, data: string | Uint8Array) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Test seam: stdout writer. Defaults to `process.stdout.write`.
|
|
18
|
+
*/
|
|
19
|
+
stdoutWrite?: (data: string | Uint8Array) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Test seam: stdout-is-a-TTY override. Defaults to `process.stdout.isTTY`.
|
|
22
|
+
*/
|
|
23
|
+
stdoutIsTTY?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type OutputFormat = 'text' | 'binary';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Writes `data` to either a file (`outputArg` is a path) or stdout (`outputArg`
|
|
30
|
+
* is `-` or `undefined` — though `undefined` is no longer the default; mode
|
|
31
|
+
* dispatch always resolves a concrete path now).
|
|
32
|
+
*
|
|
33
|
+
* Existing files are silently overwritten. m2b.5 removed the `--force` gate;
|
|
34
|
+
* matches POSIX redirection (`> file`) and every peer drawing CLI (mmdc, d2,
|
|
35
|
+
* prettier, tsc, pandoc).
|
|
36
|
+
*/
|
|
37
|
+
export async function writeOutput(
|
|
38
|
+
outputArg: string | undefined,
|
|
39
|
+
data: string | Uint8Array,
|
|
40
|
+
format: OutputFormat,
|
|
41
|
+
options: WriteOutputOptions = {},
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
if (outputArg === undefined || outputArg === '-') {
|
|
44
|
+
guardBinaryStdout(format, options);
|
|
45
|
+
const write = options.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
|
|
46
|
+
const payload = ensureTrailingNewline(data, format);
|
|
47
|
+
write(payload);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cwd = options.cwd ?? process.cwd();
|
|
52
|
+
const absPath = path.resolve(cwd, outputArg);
|
|
53
|
+
|
|
54
|
+
const writeFile = options.writeFile ?? ((p, d) => fs.writeFile(p, d));
|
|
55
|
+
try {
|
|
56
|
+
await writeFile(absPath, data);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new CliError(ExitCode.OutputError, formatWriteError(outputArg, err));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function guardBinaryStdout(format: OutputFormat, options: WriteOutputOptions): void {
|
|
63
|
+
if (format !== 'binary') return;
|
|
64
|
+
const isTTY = options.stdoutIsTTY ?? process.stdout.isTTY === true;
|
|
65
|
+
if (isTTY) {
|
|
66
|
+
throw new CliError(
|
|
67
|
+
ExitCode.InputError,
|
|
68
|
+
'nowline: binary output to terminal refused; use -o <path> or pipe to a file.',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ensureTrailingNewline(
|
|
74
|
+
data: string | Uint8Array,
|
|
75
|
+
format: OutputFormat,
|
|
76
|
+
): string | Uint8Array {
|
|
77
|
+
if (format !== 'text') return data;
|
|
78
|
+
if (typeof data !== 'string') return data;
|
|
79
|
+
return data.endsWith('\n') ? data : `${data}\n`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function formatWriteError(outputArg: string, err: unknown): string {
|
|
83
|
+
if (isNodeError(err)) {
|
|
84
|
+
if (err.code === 'EACCES') return `Permission denied: ${outputArg}`;
|
|
85
|
+
if (err.code === 'ENOENT') return `Output directory does not exist: ${outputArg}`;
|
|
86
|
+
if (err.code === 'EISDIR') return `Output path is a directory: ${outputArg}`;
|
|
87
|
+
}
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
return `Could not write ${outputArg}: ${message}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isNodeError(err: unknown): err is NodeJS.ErrnoException {
|
|
93
|
+
return err instanceof Error && typeof (err as { code?: unknown }).code === 'string';
|
|
94
|
+
}
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { CLI_BUILD, CLI_VERSION, type CliBuild } from './generated/version.js';
|
|
2
|
+
|
|
3
|
+
import { CLI_BUILD, CLI_VERSION } from './generated/version.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compose the user-visible version string per SemVer build-metadata
|
|
7
|
+
* rules. Released builds print just the SemVer (`0.1.0`); dev builds
|
|
8
|
+
* append the short SHA (and `.dirty` if the working tree had local
|
|
9
|
+
* edits when the binary was built):
|
|
10
|
+
*
|
|
11
|
+
* release -> 0.1.0
|
|
12
|
+
* dev (clean) -> 0.1.0+abc1234
|
|
13
|
+
* dev (dirty) -> 0.1.0+abc1234.dirty
|
|
14
|
+
*/
|
|
15
|
+
export function fullVersionString(): string {
|
|
16
|
+
if (CLI_BUILD.isRelease || CLI_BUILD.sha === '') {
|
|
17
|
+
return CLI_VERSION;
|
|
18
|
+
}
|
|
19
|
+
const dirty = CLI_BUILD.isDirty ? '.dirty' : '';
|
|
20
|
+
return `${CLI_VERSION}+${CLI_BUILD.sha}${dirty}`;
|
|
21
|
+
}
|