patram 0.0.2 → 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.
Files changed (67) hide show
  1. package/bin/patram.js +25 -147
  2. package/lib/build-graph-identity.js +270 -0
  3. package/lib/build-graph.js +156 -77
  4. package/lib/check-graph.js +23 -7
  5. package/lib/claim-helpers.js +55 -0
  6. package/lib/cli-help-metadata.js +552 -0
  7. package/lib/command-output.js +83 -0
  8. package/lib/derived-summary.js +278 -0
  9. package/lib/format-derived-summary-row.js +9 -0
  10. package/lib/format-node-header.js +19 -0
  11. package/lib/format-output-item-block.js +22 -0
  12. package/lib/format-output-metadata.js +62 -0
  13. package/lib/layout-stored-queries.js +361 -0
  14. package/lib/list-queries.js +18 -0
  15. package/lib/list-source-files.js +50 -15
  16. package/lib/load-patram-config.js +505 -18
  17. package/lib/load-patram-config.types.ts +40 -0
  18. package/lib/load-project-graph.js +124 -0
  19. package/lib/output-view.types.ts +88 -0
  20. package/lib/parse-claims.js +38 -158
  21. package/lib/parse-claims.types.ts +7 -0
  22. package/lib/parse-cli-arguments-helpers.js +446 -0
  23. package/lib/parse-cli-arguments.js +266 -0
  24. package/lib/parse-cli-arguments.types.ts +69 -0
  25. package/lib/parse-cli-color-options.js +44 -0
  26. package/lib/parse-cli-query-pagination.js +49 -0
  27. package/lib/parse-jsdoc-blocks.js +184 -0
  28. package/lib/parse-jsdoc-claims.js +280 -0
  29. package/lib/parse-jsdoc-prose.js +111 -0
  30. package/lib/parse-markdown-claims.js +242 -0
  31. package/lib/parse-markdown-directives.js +136 -0
  32. package/lib/parse-where-clause.js +707 -0
  33. package/lib/parse-where-clause.types.ts +70 -0
  34. package/lib/patram-cli.js +464 -0
  35. package/lib/patram-config.js +3 -1
  36. package/lib/patram-config.types.ts +2 -1
  37. package/lib/patram.js +6 -0
  38. package/lib/query-graph.js +368 -0
  39. package/lib/query-inspection.js +523 -0
  40. package/lib/render-check-output.js +315 -0
  41. package/lib/render-cli-help.js +419 -0
  42. package/lib/render-json-output.js +161 -0
  43. package/lib/render-output-view.js +222 -0
  44. package/lib/render-plain-output.js +182 -0
  45. package/lib/render-rich-output.js +240 -0
  46. package/lib/render-rich-source.js +1333 -0
  47. package/lib/resolve-check-target.js +190 -0
  48. package/lib/resolve-output-mode.js +60 -0
  49. package/lib/resolve-patram-graph-config.js +88 -0
  50. package/lib/resolve-where-clause.js +66 -0
  51. package/lib/show-document.js +311 -0
  52. package/lib/source-file-defaults.js +28 -0
  53. package/lib/tagged-fenced-block-error.js +17 -0
  54. package/lib/tagged-fenced-block-markdown.js +111 -0
  55. package/lib/tagged-fenced-block-metadata.js +97 -0
  56. package/lib/tagged-fenced-block-parser.js +292 -0
  57. package/lib/tagged-fenced-blocks.js +100 -0
  58. package/lib/tagged-fenced-blocks.types.ts +38 -0
  59. package/lib/write-paged-output.js +87 -0
  60. package/package.json +28 -12
  61. package/bin/patram.test.js +0 -184
  62. package/lib/build-graph.test.js +0 -141
  63. package/lib/check-graph.test.js +0 -103
  64. package/lib/list-source-files.test.js +0 -101
  65. package/lib/load-patram-config.test.js +0 -211
  66. package/lib/parse-claims.test.js +0 -113
  67. package/lib/patram-config.test.js +0 -147
@@ -0,0 +1,87 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { once } from 'node:events';
3
+ import process from 'node:process';
4
+
5
+ /**
6
+ * @param {string} output_text
7
+ * @returns {Promise<void>}
8
+ */
9
+ export async function writePagedOutput(output_text) {
10
+ const pager_command = process.env.PAGER?.trim();
11
+
12
+ if (pager_command) {
13
+ await writeOutputThroughPager(output_text, pager_command, [], true);
14
+
15
+ return;
16
+ }
17
+
18
+ await writeOutputThroughPager(output_text, 'less', ['-FIRXS'], false);
19
+ }
20
+
21
+ /**
22
+ * @param {string} output_text
23
+ * @param {string} pager_command
24
+ * @param {string[]} pager_arguments
25
+ * @param {boolean} use_shell
26
+ * @returns {Promise<void>}
27
+ */
28
+ async function writeOutputThroughPager(
29
+ output_text,
30
+ pager_command,
31
+ pager_arguments,
32
+ use_shell,
33
+ ) {
34
+ const pager_process = spawn(pager_command, pager_arguments, {
35
+ shell: use_shell,
36
+ stdio: ['pipe', 'inherit', 'inherit'],
37
+ });
38
+ const close_promise = once(pager_process, 'close');
39
+ const error_promise = once(pager_process, 'error').then(([error]) => {
40
+ throw error;
41
+ });
42
+
43
+ await writeToPagerInput(pager_process.stdin, output_text);
44
+
45
+ const [exit_code, signal_code] = await Promise.race([
46
+ close_promise,
47
+ error_promise,
48
+ ]);
49
+
50
+ if (exit_code === 0) {
51
+ return;
52
+ }
53
+
54
+ if (signal_code) {
55
+ throw new Error(`Pager exited with signal "${signal_code}".`);
56
+ }
57
+
58
+ throw new Error(`Pager exited with code ${exit_code}.`);
59
+ }
60
+
61
+ /**
62
+ * @param {NodeJS.WritableStream} pager_input
63
+ * @param {string} output_text
64
+ * @returns {Promise<void>}
65
+ */
66
+ function writeToPagerInput(pager_input, output_text) {
67
+ return new Promise((resolve, reject) => {
68
+ pager_input.once('error', (error) => {
69
+ if (isBrokenPipe(error)) {
70
+ resolve();
71
+
72
+ return;
73
+ }
74
+
75
+ reject(error);
76
+ });
77
+ pager_input.end(output_text, 'utf8', resolve);
78
+ });
79
+ }
80
+
81
+ /**
82
+ * @param {unknown} error
83
+ * @returns {boolean}
84
+ */
85
+ function isBrokenPipe(error) {
86
+ return error instanceof Error && 'code' in error && error.code === 'EPIPE';
87
+ }
package/package.json CHANGED
@@ -1,13 +1,23 @@
1
1
  {
2
2
  "name": "patram",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
+ "main": "./lib/patram.js",
6
+ "exports": {
7
+ ".": "./lib/patram.js",
8
+ "./bin/patram.js": "./bin/patram.js"
9
+ },
5
10
  "files": [
6
- "bin/",
7
- "lib/"
11
+ "bin/patram.js",
12
+ "lib/**/*.js",
13
+ "lib/**/*.ts",
14
+ "!bin/**/*.test.js",
15
+ "!bin/**/*.test-helpers.js",
16
+ "!lib/**/*.test.js",
17
+ "!lib/**/*.test-helpers.js"
8
18
  ],
9
19
  "bin": {
10
- "patram": "./bin/patram.js"
20
+ "patram": "bin/patram.js"
11
21
  },
12
22
  "homepage": "https://github.com/mantoni/patram",
13
23
  "repository": {
@@ -19,18 +29,18 @@
19
29
  },
20
30
  "license": "MIT",
21
31
  "scripts": {
22
- "all": "npm run check:lint && npm run check:format && npm run check:types && npm run test:unit && npm run test:coverage && npm run check:dupes",
32
+ "all": "npm run check:lint && npm run check:format && npm run check:types && npm run check:patram && npm run test:coverage && npm run check:dupes",
23
33
  "check:dupes": "jscpd --min-tokens 100 --min-lines 6 --mode mild --threshold 0 --reporters console --gitignore .",
24
34
  "check:format": "prettier --check .",
25
35
  "check:lint": "eslint .",
26
- "check:staged": "lint-staged --quiet",
36
+ "check:patram": "./bin/patram.js check",
37
+ "check:staged": "lint-staged",
27
38
  "check:types": "tsc",
28
39
  "postversion": "git push && git push --tags",
29
40
  "preversion": "npm run all",
30
41
  "prepare": "husky",
31
- "test": "npm run test:unit && npm run test:coverage",
42
+ "test": "vitest run",
32
43
  "test:coverage": "vitest run --coverage",
33
- "test:unit": "vitest run",
34
44
  "version": "node scripts/update-changelog.js && git add CHANGELOG.md package.json package-lock.json"
35
45
  },
36
46
  "lint-staged": {
@@ -41,13 +51,21 @@
41
51
  ]
42
52
  },
43
53
  "dependencies": {
54
+ "@shikijs/cli": "^4.0.2",
55
+ "@shikijs/vscode-textmate": "^10.0.2",
56
+ "ansis": "^4.2.0",
57
+ "beautiful-mermaid": "^1.1.3",
58
+ "globby": "^16.1.1",
59
+ "md4x": "^0.0.25",
60
+ "shiki": "^4.0.2",
61
+ "string-width": "^8.2.0",
62
+ "wrap-ansi": "^10.0.0",
44
63
  "zod": "^4.3.6"
45
64
  },
46
65
  "devDependencies": {
47
66
  "@eslint/js": "^10.0.1",
48
67
  "@types/node": "^24.12.0",
49
68
  "@vitest/coverage-v8": "^4.1.0",
50
- "ansis": "^4.2.0",
51
69
  "eslint": "^10.0.3",
52
70
  "eslint-plugin-jsdoc": "^62.8.0",
53
71
  "globals": "^17.4.0",
@@ -56,9 +74,7 @@
56
74
  "lint-staged": "^16.2.6",
57
75
  "prettier": "^3.5.3",
58
76
  "slice-ansi": "^8.0.0",
59
- "string-width": "^8.2.0",
60
77
  "typescript": "^5.8.2",
61
- "vitest": "^4.1.0",
62
- "wrap-ansi": "^10.0.0"
78
+ "vitest": "^4.1.0"
63
79
  }
64
80
  }
@@ -1,184 +0,0 @@
1
- import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- import { afterEach, expect, it } from 'vitest';
6
-
7
- import { main } from './patram.js';
8
-
9
- const test_context = createTestContext();
10
-
11
- afterEach(async () => {
12
- await cleanupTestContext(test_context);
13
- });
14
-
15
- it('prints config diagnostics for check failures', async () => {
16
- test_context.project_directory = await createTempProjectDirectory();
17
- const io_context = createIoContext();
18
-
19
- const exit_code = await main(['check', test_context.project_directory], {
20
- stderr: io_context.stderr,
21
- stdout: io_context.stdout,
22
- });
23
-
24
- expect(exit_code).toBe(1);
25
- expect(io_context.stderr_chunks).toEqual([
26
- '.patram.json:1:1 error config.not_found Config file ".patram.json" was not found.\n',
27
- ]);
28
- expect(io_context.stdout_chunks).toEqual([]);
29
- });
30
-
31
- it('prints broken link diagnostics for check failures', async () => {
32
- test_context.project_directory = await createTempProjectDirectory();
33
- const io_context = createIoContext();
34
-
35
- await writeProjectConfig(test_context.project_directory);
36
- await writeProjectFile(
37
- test_context.project_directory,
38
- 'docs/patram.md',
39
- createBrokenLinkSource(),
40
- );
41
-
42
- const exit_code = await main(['check', test_context.project_directory], {
43
- stderr: io_context.stderr,
44
- stdout: io_context.stdout,
45
- });
46
-
47
- expect(exit_code).toBe(1);
48
- expect(io_context.stderr_chunks).toEqual([
49
- 'docs/patram.md:3:5 error graph.link_broken Document link target "docs/missing.md" was not found.\n',
50
- ]);
51
- });
52
-
53
- it('defaults check to the current working directory and exits 0 on valid input', async () => {
54
- test_context.project_directory = await createTempProjectDirectory();
55
- const io_context = createIoContext();
56
-
57
- await writeProjectConfig(test_context.project_directory);
58
- await writeProjectFile(
59
- test_context.project_directory,
60
- 'docs/patram.md',
61
- createValidLinkSource(),
62
- );
63
- await writeProjectFile(
64
- test_context.project_directory,
65
- 'docs/guide.md',
66
- '# Guide\n',
67
- );
68
- process.chdir(test_context.project_directory);
69
-
70
- const exit_code = await main(['check'], {
71
- stderr: io_context.stderr,
72
- stdout: io_context.stdout,
73
- });
74
-
75
- expect(exit_code).toBe(0);
76
- expect(io_context.stderr_chunks).toEqual([]);
77
- expect(io_context.stdout_chunks).toEqual([]);
78
- });
79
-
80
- /**
81
- * @returns {string}
82
- */
83
- function createBrokenLinkSource() {
84
- return ['# Patram', '', 'See [missing](./missing.md).'].join('\n');
85
- }
86
-
87
- /**
88
- * @returns {string}
89
- */
90
- function createValidLinkSource() {
91
- return ['# Patram', '', 'See [guide](./guide.md).'].join('\n');
92
- }
93
-
94
- /**
95
- * @returns {{ original_working_directory: string, project_directory: string | null }}
96
- */
97
- function createTestContext() {
98
- return {
99
- original_working_directory: process.cwd(),
100
- project_directory: null,
101
- };
102
- }
103
-
104
- /**
105
- * @returns {{ stderr: { write(chunk: string): boolean }, stderr_chunks: string[], stdout: { write(chunk: string): boolean }, stdout_chunks: string[] }}
106
- */
107
- function createIoContext() {
108
- /** @type {string[]} */
109
- const stderr_chunks = [];
110
- /** @type {string[]} */
111
- const stdout_chunks = [];
112
-
113
- return {
114
- stderr: {
115
- /**
116
- * @param {string} chunk
117
- * @returns {boolean}
118
- */
119
- write(chunk) {
120
- stderr_chunks.push(chunk);
121
-
122
- return true;
123
- },
124
- },
125
- stderr_chunks,
126
- stdout: {
127
- /**
128
- * @param {string} chunk
129
- * @returns {boolean}
130
- */
131
- write(chunk) {
132
- stdout_chunks.push(chunk);
133
-
134
- return true;
135
- },
136
- },
137
- stdout_chunks,
138
- };
139
- }
140
-
141
- /**
142
- * @returns {Promise<string>}
143
- */
144
- async function createTempProjectDirectory() {
145
- return mkdtemp(join(tmpdir(), 'patram-check-command-'));
146
- }
147
-
148
- /**
149
- * @param {{ original_working_directory: string, project_directory: string | null }} test_context
150
- */
151
- async function cleanupTestContext(test_context) {
152
- process.chdir(test_context.original_working_directory);
153
-
154
- if (test_context.project_directory) {
155
- await rm(test_context.project_directory, { force: true, recursive: true });
156
- test_context.project_directory = null;
157
- }
158
- }
159
-
160
- /**
161
- * @param {string} project_directory
162
- */
163
- async function writeProjectConfig(project_directory) {
164
- await writeFile(
165
- join(project_directory, '.patram.json'),
166
- JSON.stringify({
167
- include: ['docs/**/*.md'],
168
- queries: {},
169
- }),
170
- );
171
- }
172
-
173
- /**
174
- * @param {string} project_directory
175
- * @param {string} relative_path
176
- * @param {string} source_text
177
- */
178
- async function writeProjectFile(project_directory, relative_path, source_text) {
179
- const file_path = join(project_directory, relative_path);
180
- const directory_path = file_path.slice(0, file_path.lastIndexOf('/'));
181
-
182
- await mkdir(directory_path, { recursive: true });
183
- await writeFile(file_path, source_text);
184
- }
@@ -1,141 +0,0 @@
1
- /**
2
- * @import { PatramClaim } from './parse-claims.types.ts';
3
- */
4
-
5
- import { expect, it } from 'vitest';
6
-
7
- import graph_v0_config from '../docs/graph-v0.config.json' with { type: 'json' };
8
- import { buildGraph } from './build-graph.js';
9
- import { parseClaims } from './parse-claims.js';
10
- import { parsePatramConfig } from './patram-config.js';
11
-
12
- const patram_config = parsePatramConfig(graph_v0_config);
13
-
14
- it('builds document nodes, configured target nodes and edges from claims', () => {
15
- const graph = buildGraph(patram_config, createMarkdownClaims());
16
-
17
- expect(graph).toEqual(createExpectedGraph());
18
- });
19
-
20
- it('keeps source document nodes even when a claim has no mapping', () => {
21
- const graph = buildGraph(patram_config, [
22
- {
23
- document_id: 'doc:docs/orphan.md',
24
- id: 'claim:doc:docs/orphan.md:1',
25
- origin: {
26
- column: 1,
27
- line: 2,
28
- path: 'docs/orphan.md',
29
- },
30
- type: 'directive',
31
- value: 'ignored',
32
- },
33
- ]);
34
-
35
- expect(graph).toEqual({
36
- edges: [],
37
- nodes: {
38
- 'doc:docs/orphan.md': {
39
- id: 'doc:docs/orphan.md',
40
- kind: 'document',
41
- path: 'docs/orphan.md',
42
- },
43
- },
44
- });
45
- });
46
-
47
- /**
48
- * Create markdown claims for graph materialization tests.
49
- *
50
- * @returns {PatramClaim[]}
51
- */
52
- function createMarkdownClaims() {
53
- return parseClaims({
54
- path: 'docs/patram.md',
55
- source: createMarkdownSource(),
56
- });
57
- }
58
-
59
- /**
60
- * Create markdown input for graph materialization tests.
61
- *
62
- * @returns {string}
63
- */
64
- function createMarkdownSource() {
65
- return [
66
- '# Patram',
67
- '',
68
- 'Read the [graph design](./graph-v0.md).',
69
- 'Defined by: terms/patram.md',
70
- ].join('\n');
71
- }
72
-
73
- /**
74
- * Create the expected graph for the materialization test.
75
- *
76
- * @returns {object}
77
- */
78
- function createExpectedGraph() {
79
- return {
80
- edges: createExpectedEdges(),
81
- nodes: createExpectedNodes(),
82
- };
83
- }
84
-
85
- /**
86
- * Create the expected edges for the materialization test.
87
- *
88
- * @returns {object[]}
89
- */
90
- function createExpectedEdges() {
91
- return [
92
- {
93
- from: 'doc:docs/patram.md',
94
- id: 'edge:1',
95
- origin: {
96
- column: 10,
97
- line: 3,
98
- path: 'docs/patram.md',
99
- },
100
- relation: 'links_to',
101
- to: 'doc:docs/graph-v0.md',
102
- },
103
- {
104
- from: 'doc:docs/patram.md',
105
- id: 'edge:2',
106
- origin: {
107
- column: 1,
108
- line: 4,
109
- path: 'docs/patram.md',
110
- },
111
- relation: 'defines',
112
- to: 'term:docs/terms/patram.md',
113
- },
114
- ];
115
- }
116
-
117
- /**
118
- * Create the expected nodes for the materialization test.
119
- *
120
- * @returns {object}
121
- */
122
- function createExpectedNodes() {
123
- return {
124
- 'doc:docs/graph-v0.md': {
125
- id: 'doc:docs/graph-v0.md',
126
- kind: 'document',
127
- path: 'docs/graph-v0.md',
128
- },
129
- 'doc:docs/patram.md': {
130
- id: 'doc:docs/patram.md',
131
- kind: 'document',
132
- path: 'docs/patram.md',
133
- title: 'Patram',
134
- },
135
- 'term:docs/terms/patram.md': {
136
- id: 'term:docs/terms/patram.md',
137
- key: 'docs/terms/patram.md',
138
- kind: 'term',
139
- },
140
- };
141
- }
@@ -1,103 +0,0 @@
1
- /**
2
- * @import { BuildGraphResult } from './build-graph.types.ts';
3
- */
4
- import { expect, it } from 'vitest';
5
-
6
- import { checkGraph } from './check-graph.js';
7
-
8
- it('reports broken document links', () => {
9
- const diagnostics = checkGraph(createGraphWithBrokenLink(), [
10
- 'docs/patram.md',
11
- ]);
12
-
13
- expect(diagnostics).toEqual([
14
- {
15
- code: 'graph.link_broken',
16
- column: 10,
17
- level: 'error',
18
- line: 3,
19
- message: 'Document link target "docs/missing.md" was not found.',
20
- path: 'docs/patram.md',
21
- },
22
- ]);
23
- });
24
-
25
- it('reports graph edges that reference missing nodes', () => {
26
- const diagnostics = checkGraph(createGraphWithMissingTargetNode(), [
27
- 'docs/patram.md',
28
- ]);
29
-
30
- expect(diagnostics).toEqual([
31
- {
32
- code: 'graph.edge_missing_to',
33
- column: 8,
34
- level: 'error',
35
- line: 6,
36
- message:
37
- 'Graph edge "edge:1" references missing target node "doc:docs/missing.md".',
38
- path: 'docs/patram.md',
39
- },
40
- ]);
41
- });
42
-
43
- /**
44
- * @returns {BuildGraphResult}
45
- */
46
- function createGraphWithBrokenLink() {
47
- return {
48
- edges: [
49
- {
50
- from: 'doc:docs/patram.md',
51
- id: 'edge:1',
52
- origin: {
53
- column: 10,
54
- line: 3,
55
- path: 'docs/patram.md',
56
- },
57
- relation: 'links_to',
58
- to: 'doc:docs/missing.md',
59
- },
60
- ],
61
- nodes: {
62
- 'doc:docs/missing.md': {
63
- id: 'doc:docs/missing.md',
64
- kind: 'document',
65
- path: 'docs/missing.md',
66
- },
67
- 'doc:docs/patram.md': {
68
- id: 'doc:docs/patram.md',
69
- kind: 'document',
70
- path: 'docs/patram.md',
71
- title: 'Patram',
72
- },
73
- },
74
- };
75
- }
76
-
77
- /**
78
- * @returns {BuildGraphResult}
79
- */
80
- function createGraphWithMissingTargetNode() {
81
- return {
82
- edges: [
83
- {
84
- from: 'doc:docs/patram.md',
85
- id: 'edge:1',
86
- origin: {
87
- column: 8,
88
- line: 6,
89
- path: 'docs/patram.md',
90
- },
91
- relation: 'links_to',
92
- to: 'doc:docs/missing.md',
93
- },
94
- ],
95
- nodes: {
96
- 'doc:docs/patram.md': {
97
- id: 'doc:docs/patram.md',
98
- kind: 'document',
99
- path: 'docs/patram.md',
100
- },
101
- },
102
- };
103
- }
@@ -1,101 +0,0 @@
1
- import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- import { afterEach, expect, it } from 'vitest';
6
-
7
- import { listSourceFiles } from './list-source-files.js';
8
-
9
- const test_context = createTestContext();
10
-
11
- afterEach(async () => {
12
- await cleanupTestContext(test_context);
13
- });
14
-
15
- it('lists matching source files as sorted repo-relative paths', async () => {
16
- test_context.project_directory = await createTempProjectDirectory();
17
-
18
- await writeProjectFile(test_context.project_directory, 'docs/zeta.md');
19
- await writeProjectFile(test_context.project_directory, 'docs/alpha.md');
20
- await writeProjectFile(
21
- test_context.project_directory,
22
- 'docs/nested/overview.md',
23
- );
24
- await writeProjectFile(test_context.project_directory, 'docs/notes.txt');
25
- await writeProjectFile(test_context.project_directory, 'notes/todo.md');
26
-
27
- const source_files = await listSourceFiles(
28
- ['docs/**/*.md'],
29
- test_context.project_directory,
30
- );
31
-
32
- expect(source_files).toEqual([
33
- 'docs/alpha.md',
34
- 'docs/nested/overview.md',
35
- 'docs/zeta.md',
36
- ]);
37
- });
38
-
39
- it('deduplicates paths matched by multiple include globs', async () => {
40
- test_context.project_directory = await createTempProjectDirectory();
41
-
42
- await writeProjectFile(test_context.project_directory, 'docs/patram.md');
43
-
44
- const source_files = await listSourceFiles(
45
- ['docs/**/*.md', 'docs/patram.md'],
46
- test_context.project_directory,
47
- );
48
-
49
- expect(source_files).toEqual(['docs/patram.md']);
50
- });
51
-
52
- /**
53
- * Create a temporary project directory.
54
- *
55
- * @returns {Promise<string>}
56
- */
57
- async function createTempProjectDirectory() {
58
- return mkdtemp(join(tmpdir(), 'patram-list-source-files-'));
59
- }
60
-
61
- /**
62
- * Write a file under the temporary project directory.
63
- *
64
- * @param {string} project_directory
65
- * @param {string} relative_path
66
- */
67
- async function writeProjectFile(project_directory, relative_path) {
68
- const file_path = join(project_directory, relative_path);
69
- const parent_directory = file_path.slice(0, file_path.lastIndexOf('/'));
70
-
71
- await mkdir(parent_directory, { recursive: true });
72
- await writeFile(file_path, `# ${relative_path}\n`);
73
- }
74
-
75
- /**
76
- * Remove a temporary directory tree.
77
- *
78
- * @param {string} project_directory
79
- */
80
- async function removeDirectory(project_directory) {
81
- await rm(project_directory, { force: true, recursive: true });
82
- }
83
-
84
- /**
85
- * @returns {{ project_directory: string | null }}
86
- */
87
- function createTestContext() {
88
- return {
89
- project_directory: null,
90
- };
91
- }
92
-
93
- /**
94
- * @param {{ project_directory: string | null }} test_context
95
- */
96
- async function cleanupTestContext(test_context) {
97
- if (test_context.project_directory) {
98
- await removeDirectory(test_context.project_directory);
99
- test_context.project_directory = null;
100
- }
101
- }