@ndp-software/lit-md 0.3.0 → 0.4.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 +2 -6
- package/package.json +2 -6
- package/src/docs/README.lit-md.ts +8 -5
- package/src/parser.ts +19 -3
- package/src/shell.ts +5 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndp-software/lit-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Literate test files that generate README.md",
|
|
5
5
|
"license": "UNLICENSED -- All rights reserved. See LICENSE file.",
|
|
6
6
|
"author": "Andy Peterson <andy@ndpsoftware.com>",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": ""
|
|
10
|
+
"url": "https://github.com/ndp-software/lit-md"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"literate programming",
|
|
@@ -30,10 +30,6 @@
|
|
|
30
30
|
"docs/**/*",
|
|
31
31
|
"src/**/*"
|
|
32
32
|
],
|
|
33
|
-
"exports": {
|
|
34
|
-
"main": "./dist/index.js",
|
|
35
|
-
"types": "./dist/index.d.ts"
|
|
36
|
-
},
|
|
37
33
|
"scripts": {
|
|
38
34
|
"prepublishOnly": "npm run verify && npm run build",
|
|
39
35
|
"readme": "node ./src/cli.ts --test --typecheck --out ./README.md src/docs/README.lit-md.ts",
|
|
@@ -66,10 +66,11 @@ what appears in the output and how it looks.
|
|
|
66
66
|
|
|
67
67
|
# A simple example
|
|
68
68
|
*/
|
|
69
|
-
shellExample(`lit-md
|
|
69
|
+
shellExample(`lit-md my-readme.ts`, {
|
|
70
70
|
inputFiles: [{
|
|
71
|
-
path: '
|
|
71
|
+
path: 'my-readme.ts',
|
|
72
72
|
displayPath: false,
|
|
73
|
+
display: true,
|
|
73
74
|
content: `
|
|
74
75
|
describe('My Project README.', () => {
|
|
75
76
|
/*
|
|
@@ -91,9 +92,11 @@ example('multiply example', () => {
|
|
|
91
92
|
})
|
|
92
93
|
`
|
|
93
94
|
}],
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
displayCommand: true,
|
|
96
|
+
stdout: {
|
|
97
|
+
display: true,
|
|
98
|
+
contains: 'This is a really great project!'
|
|
99
|
+
}
|
|
97
100
|
})
|
|
98
101
|
|
|
99
102
|
/*
|
package/src/parser.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
3
3
|
import { writeFileSync, mkdtempSync, rmSync, readFileSync } from 'node:fs'
|
|
4
4
|
import { join, isAbsolute } from 'node:path'
|
|
5
5
|
import { tmpdir } from 'node:os'
|
|
6
|
+
import { buildAliasPrefix } from './shell.ts'
|
|
6
7
|
|
|
7
8
|
export type ProseNode = { kind: 'prose'; text: string; terminal?: true; noBlankAfter?: true; noBlankBefore?: true }
|
|
8
9
|
export type CodeNode = { kind: 'code'; lang: string; text: string; title?: string }
|
|
@@ -230,6 +231,13 @@ export function parse(src: string, lang = 'typescript'): DocNode[] {
|
|
|
230
231
|
if (opts && isExecutionNeeded(opts)) {
|
|
231
232
|
const outputPaths = extractOutputFilePaths(opts)
|
|
232
233
|
execution = executeShellCommand(cmd, inputFiles, outputPaths, expectedExitCode)
|
|
234
|
+
|
|
235
|
+
// Fail if exit code doesn't match expectations
|
|
236
|
+
if (execution && execution.exitCode !== expectedExitCode) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`shellExample failed: ${cmd}\nexit ${execution.exitCode} (expected exit code ${expectedExitCode})`
|
|
239
|
+
)
|
|
240
|
+
}
|
|
233
241
|
}
|
|
234
242
|
|
|
235
243
|
const displayCommand = opts ? readBoolOption(getProp(opts, 'displayCommand')) : true
|
|
@@ -881,8 +889,10 @@ function executeShellCommand(cmd: string, inputFiles: Array<InputFileInfo>, outp
|
|
|
881
889
|
writeFileSync(resolvePath(f.path), f.content, 'utf8')
|
|
882
890
|
}
|
|
883
891
|
|
|
884
|
-
// Execute command
|
|
885
|
-
const
|
|
892
|
+
// Execute command with alias prefix
|
|
893
|
+
const prefix = buildAliasPrefix()
|
|
894
|
+
const fullCmd = prefix ? `${prefix}${cmd}` : cmd
|
|
895
|
+
const result = spawnSync(fullCmd, { shell: true, encoding: 'utf8', cwd: tmpDir })
|
|
886
896
|
|
|
887
897
|
// Capture output files when exit code matches expectation
|
|
888
898
|
const outputFiles = new Map<string, string>()
|
|
@@ -930,10 +940,16 @@ function appendShellExampleAnnotations(
|
|
|
930
940
|
const containsProp = getProp(prop.initializer as ts.ObjectLiteralExpression, 'contains')
|
|
931
941
|
const displayProp = getProp(prop.initializer as ts.ObjectLiteralExpression, 'display')
|
|
932
942
|
|
|
933
|
-
// If display is true, use cached execution
|
|
943
|
+
// If display is true, use cached execution to show stdout
|
|
934
944
|
if (displayProp && displayProp.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
|
935
945
|
if (execution && execution.exitCode === 0) {
|
|
936
946
|
lines.push(execution.stdout)
|
|
947
|
+
} else if (execution && execution.exitCode !== 0) {
|
|
948
|
+
// This shouldn't happen now since we throw on exit code mismatch
|
|
949
|
+
lines.push(`# [ERROR] stdout unavailable (exit code: ${execution.exitCode})`)
|
|
950
|
+
} else {
|
|
951
|
+
// This shouldn't happen now since we throw on errors
|
|
952
|
+
lines.push(`# [ERROR] stdout unavailable (execution failed)`)
|
|
937
953
|
}
|
|
938
954
|
} else if (containsProp && ts.isStringLiteralLike(containsProp.initializer)) {
|
|
939
955
|
// Show contains assertion only if display is not true
|
package/src/shell.ts
CHANGED
|
@@ -41,7 +41,7 @@ function resolveCmdPath(cmdString: string): string {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** Build shell alias prefix lines to prepend to commands. */
|
|
44
|
-
function buildAliasPrefix(): string {
|
|
44
|
+
export function buildAliasPrefix(): string {
|
|
45
45
|
if (_aliases.size === 0) return ''
|
|
46
46
|
const lines = [..._aliases.entries()].map(([name, cmd]) => `alias ${name}='${cmd}'`)
|
|
47
47
|
return lines.join('\n') + '\n'
|
|
@@ -109,6 +109,10 @@ export function _runShellExample(cmd: string, opts: ShellExampleOpts): void {
|
|
|
109
109
|
}
|
|
110
110
|
const stdout = result.stdout
|
|
111
111
|
if (opts.stdout !== undefined) {
|
|
112
|
+
// If display is true, stdout must not be empty
|
|
113
|
+
if (opts.stdout.display && (!stdout || stdout.trim() === '')) {
|
|
114
|
+
throw new Error(`stdout.display: true but command produced no output (exit code: ${actualExitCode})`)
|
|
115
|
+
}
|
|
112
116
|
if (opts.stdout.contains !== undefined) {
|
|
113
117
|
const actualDesc = stdout === '' ? '(empty)' : stdout
|
|
114
118
|
assert.ok(
|