@reqlan/cli 0.1.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 +25 -0
- package/bin/cli.js +6 -0
- package/out/commands/analyse.d.ts +11 -0
- package/out/commands/analyse.js +123 -0
- package/out/commands/analyse.js.map +1 -0
- package/out/commands/parse.d.ts +8 -0
- package/out/commands/parse.js +63 -0
- package/out/commands/parse.js.map +1 -0
- package/out/commands/search.d.ts +10 -0
- package/out/commands/search.js +61 -0
- package/out/commands/search.js.map +1 -0
- package/out/generator.d.ts +2 -0
- package/out/generator.js +19 -0
- package/out/generator.js.map +1 -0
- package/out/main.d.ts +1 -0
- package/out/main.js +21 -0
- package/out/main.js.map +1 -0
- package/out/output.d.ts +1 -0
- package/out/output.js +12 -0
- package/out/output.js.map +1 -0
- package/out/runtime.d.ts +3 -0
- package/out/runtime.js +35 -0
- package/out/runtime.js.map +1 -0
- package/out/util.d.ts +14 -0
- package/out/util.js +35 -0
- package/out/util.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# reqlan CLI (`reqlan` / `rq`)
|
|
2
|
+
|
|
3
|
+
Command-line interface for parsing `.rq` files and analysing the workspace requirement graph.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
reqlan parse <file> [--json]
|
|
9
|
+
reqlan analyse [--file <path> | --idea <name>] [--depth <n>] [--cwd <dir>] [--json]
|
|
10
|
+
reqlan analyze # alias of analyse
|
|
11
|
+
reqlan search <query> [--limit <n>] [--cwd <dir>] [--json]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Workspace-scoped commands use `@reqlan/analytical`'s `AnalysisApi` (same headless path as MCP). Set `REQLAN_WORKSPACE` or pass `--cwd` to override the workspace root.
|
|
15
|
+
|
|
16
|
+
## Build
|
|
17
|
+
|
|
18
|
+
From the monorepo root:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm run build
|
|
22
|
+
# or
|
|
23
|
+
pnpm --filter @reqlan/cli run build
|
|
24
|
+
node packages/cli/bin/cli.js --help
|
|
25
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from 'clipanion';
|
|
2
|
+
export declare class AnalyseCommand extends Command {
|
|
3
|
+
static paths: string[][];
|
|
4
|
+
static usage: import("clipanion").Usage;
|
|
5
|
+
file: string | undefined;
|
|
6
|
+
idea: string | undefined;
|
|
7
|
+
depth: string;
|
|
8
|
+
cwd: string | undefined;
|
|
9
|
+
json: boolean;
|
|
10
|
+
execute(): Promise<number>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Command, Option } from 'clipanion';
|
|
2
|
+
import { withAnalysisApi } from '../runtime.js';
|
|
3
|
+
import { emit } from '../output.js';
|
|
4
|
+
function formatIdeaList(apiFormat, ideas, heading) {
|
|
5
|
+
if (ideas.length === 0) {
|
|
6
|
+
return `${heading}\n(none)`;
|
|
7
|
+
}
|
|
8
|
+
return `${heading}\n${ideas.map(idea => apiFormat(idea)).join('\n\n')}`;
|
|
9
|
+
}
|
|
10
|
+
function formatFileContext(formatIdea, related) {
|
|
11
|
+
return [
|
|
12
|
+
formatIdeaList(formatIdea, related.ideasInFile, '## Ideas in file'),
|
|
13
|
+
formatIdeaList(formatIdea, related.referencingIdeas, '## Referencing ideas'),
|
|
14
|
+
formatIdeaList(formatIdea, related.commentLinkedIdeas, '## Comment-linked ideas'),
|
|
15
|
+
formatIdeaList(formatIdea, related.folderReferencingIdeas, '## Folder-referencing ideas')
|
|
16
|
+
].join('\n\n');
|
|
17
|
+
}
|
|
18
|
+
function formatGraph(formatIdea, slice) {
|
|
19
|
+
const nodes = slice.nodes.map(idea => formatIdea(idea)).join('\n\n');
|
|
20
|
+
const edges = slice.edges
|
|
21
|
+
.map(edge => `${edge.sourceId} -[${edge.kind}]-> ${edge.targetId ?? edge.targetFile ?? '?'}`)
|
|
22
|
+
.join('\n');
|
|
23
|
+
return `## Graph (center ${slice.centerId}, depth ${slice.depth})\n\n### Nodes\n${nodes || '(none)'}\n\n### Edges\n${edges || '(none)'}`;
|
|
24
|
+
}
|
|
25
|
+
function formatCompletion(formatIdea, summary) {
|
|
26
|
+
const byStatus = Object.entries(summary.byStatus)
|
|
27
|
+
.map(([status, count]) => ` ${status}: ${count}`)
|
|
28
|
+
.join('\n');
|
|
29
|
+
return [
|
|
30
|
+
`## Workspace completion`,
|
|
31
|
+
`Total: ${summary.total}`,
|
|
32
|
+
`By status:\n${byStatus || ' (none)'}`,
|
|
33
|
+
formatIdeaList(formatIdea, summary.outstanding.slice(0, 12), '## Outstanding (sample)'),
|
|
34
|
+
formatIdeaList(formatIdea, summary.deprecated.slice(0, 12), '## Deprecated (sample)')
|
|
35
|
+
].join('\n\n');
|
|
36
|
+
}
|
|
37
|
+
export class AnalyseCommand extends Command {
|
|
38
|
+
constructor() {
|
|
39
|
+
super(...arguments);
|
|
40
|
+
this.file = Option.String('-f,--file', {
|
|
41
|
+
description: 'Analyse requirements related to a file path'
|
|
42
|
+
});
|
|
43
|
+
this.idea = Option.String('-i,--idea', {
|
|
44
|
+
description: 'Analyse the local graph around a named requirement'
|
|
45
|
+
});
|
|
46
|
+
this.depth = Option.String('--depth', '2', {
|
|
47
|
+
description: 'Hop depth for --idea graph (default 2)'
|
|
48
|
+
});
|
|
49
|
+
this.cwd = Option.String('--cwd', {
|
|
50
|
+
description: 'Workspace root (default: walk from cwd, or REQLAN_WORKSPACE)'
|
|
51
|
+
});
|
|
52
|
+
this.json = Option.Boolean('--json', false, {
|
|
53
|
+
description: 'Emit machine-readable JSON'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async execute() {
|
|
57
|
+
if (this.file && this.idea) {
|
|
58
|
+
this.context.stderr.write('Specify only one of --file or --idea.\n');
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
await withAnalysisApi(this.cwd, async (api) => {
|
|
63
|
+
if (this.file) {
|
|
64
|
+
const related = await api.getFileContext(this.file);
|
|
65
|
+
if (this.json) {
|
|
66
|
+
emit(related, true);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
emit(formatFileContext(idea => api.formatIdea(idea), related), false);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (this.idea) {
|
|
74
|
+
const depth = Number.parseInt(this.depth, 10);
|
|
75
|
+
const slice = await api.summarizeSubtree(this.idea, Number.isFinite(depth) ? depth : 2);
|
|
76
|
+
if (!slice) {
|
|
77
|
+
if (this.json) {
|
|
78
|
+
emit({ idea: this.idea, found: false }, true);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.context.stdout.write(`No requirement matched "${this.idea}".\n`);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (this.json) {
|
|
86
|
+
emit(slice, true);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
emit(formatGraph(idea => api.formatIdea(idea), slice), false);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const summary = await api.getCompletionStatus();
|
|
94
|
+
if (this.json) {
|
|
95
|
+
emit(summary, true);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
emit(formatCompletion(idea => api.formatIdea(idea), summary), false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
this.context.stderr.write(`${message}\n`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
AnalyseCommand.paths = [['analyse'], ['analyze']];
|
|
111
|
+
AnalyseCommand.usage = Command.Usage({
|
|
112
|
+
description: 'Analyse a file, idea, or the whole workspace requirement graph.',
|
|
113
|
+
details: `
|
|
114
|
+
Without flags, reports workspace completion status.
|
|
115
|
+
Use --file for file-related requirements, or --idea for a named requirement subtree.
|
|
116
|
+
`,
|
|
117
|
+
examples: [
|
|
118
|
+
['Workspace completion', '$0 analyse'],
|
|
119
|
+
['File context', '$0 analyse --file ./reqlan\\ rq/cli/cli_package.rq'],
|
|
120
|
+
['Idea subtree', '$0 analyze --idea cli_package']
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=analyse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyse.js","sourceRoot":"","sources":["../../src/commands/analyse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command, Option } from 'clipanion';
|
|
2
|
+
import { createReqlanServices } from '@reqlan/language';
|
|
3
|
+
import { NodeFileSystem } from 'langium/node';
|
|
4
|
+
import { parseDocument } from '../util.js';
|
|
5
|
+
import { emit } from '../output.js';
|
|
6
|
+
function elementSummary(model) {
|
|
7
|
+
return model.elements.map(element => ({
|
|
8
|
+
type: element.$type,
|
|
9
|
+
name: 'name' in element ? element.name : undefined
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export class ParseCommand extends Command {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.file = Option.String({ required: true });
|
|
16
|
+
this.json = Option.Boolean('--json', false, {
|
|
17
|
+
description: 'Emit machine-readable JSON'
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async execute() {
|
|
21
|
+
const services = createReqlanServices(NodeFileSystem).Reqlan;
|
|
22
|
+
try {
|
|
23
|
+
const { document, diagnostics, errorCount } = await parseDocument(this.file, services);
|
|
24
|
+
const model = document.parseResult?.value;
|
|
25
|
+
const payload = {
|
|
26
|
+
file: this.file,
|
|
27
|
+
ok: errorCount === 0,
|
|
28
|
+
errorCount,
|
|
29
|
+
diagnostics,
|
|
30
|
+
elements: model ? elementSummary(model) : []
|
|
31
|
+
};
|
|
32
|
+
if (this.json) {
|
|
33
|
+
emit(payload, true);
|
|
34
|
+
}
|
|
35
|
+
else if (errorCount > 0) {
|
|
36
|
+
this.context.stderr.write('There are validation errors:\n');
|
|
37
|
+
for (const d of diagnostics.filter(x => x.severity === 1)) {
|
|
38
|
+
this.context.stderr.write(`line ${d.line}: ${d.message} [${d.text}]\n`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const lines = payload.elements.map(el => el.name ? `${el.type} ${el.name}` : el.type);
|
|
43
|
+
this.context.stdout.write(`Parsed ${this.file}: ${lines.length} top-level element(s)\n` +
|
|
44
|
+
(lines.length > 0 ? lines.map(l => ` - ${l}`).join('\n') + '\n' : ''));
|
|
45
|
+
}
|
|
46
|
+
return errorCount > 0 ? 1 : 0;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
this.context.stderr.write(`${message}\n`);
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
ParseCommand.paths = [['parse']];
|
|
56
|
+
ParseCommand.usage = Command.Usage({
|
|
57
|
+
description: 'Parse a .rq file and print diagnostics or an AST summary.',
|
|
58
|
+
examples: [
|
|
59
|
+
['Parse a requirement file', '$0 parse ./path/to/file.rq'],
|
|
60
|
+
['JSON output', '$0 parse ./path/to/file.rq --json']
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/commands/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'clipanion';
|
|
2
|
+
export declare class SearchCommand extends Command {
|
|
3
|
+
static paths: string[][];
|
|
4
|
+
static usage: import("clipanion").Usage;
|
|
5
|
+
query: string;
|
|
6
|
+
limit: string;
|
|
7
|
+
cwd: string | undefined;
|
|
8
|
+
json: boolean;
|
|
9
|
+
execute(): Promise<number>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command, Option } from 'clipanion';
|
|
2
|
+
import { withAnalysisApi } from '../runtime.js';
|
|
3
|
+
import { emit } from '../output.js';
|
|
4
|
+
export class SearchCommand extends Command {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.query = Option.String({ required: true });
|
|
8
|
+
this.limit = Option.String('--limit', '8', {
|
|
9
|
+
description: 'Maximum number of matches (default 8)'
|
|
10
|
+
});
|
|
11
|
+
this.cwd = Option.String('--cwd', {
|
|
12
|
+
description: 'Workspace root (default: walk from cwd, or REQLAN_WORKSPACE)'
|
|
13
|
+
});
|
|
14
|
+
this.json = Option.Boolean('--json', false, {
|
|
15
|
+
description: 'Emit machine-readable JSON'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async execute() {
|
|
19
|
+
const limit = Number.parseInt(this.limit, 10);
|
|
20
|
+
try {
|
|
21
|
+
await withAnalysisApi(this.cwd, async (api) => {
|
|
22
|
+
const matches = await api.searchRequirements(this.query, Number.isFinite(limit) && limit > 0 ? limit : 8);
|
|
23
|
+
if (this.json) {
|
|
24
|
+
emit(matches.map(match => ({
|
|
25
|
+
idea: match.idea,
|
|
26
|
+
score: match.score,
|
|
27
|
+
reasons: match.reasons
|
|
28
|
+
})), true);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (matches.length === 0) {
|
|
32
|
+
this.context.stdout.write(`No requirements matched "${this.query}".\n`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const body = matches
|
|
36
|
+
.map(match => {
|
|
37
|
+
const reasons = match.reasons?.length ? `\nReasons: ${match.reasons.join(', ')}` : '';
|
|
38
|
+
const score = match.score !== undefined ? `\nScore: ${match.score}` : '';
|
|
39
|
+
return `${api.formatIdea(match.idea)}${score}${reasons}`;
|
|
40
|
+
})
|
|
41
|
+
.join('\n\n');
|
|
42
|
+
emit(body, false);
|
|
43
|
+
});
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
this.context.stderr.write(`${message}\n`);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
SearchCommand.paths = [['search']];
|
|
54
|
+
SearchCommand.usage = Command.Usage({
|
|
55
|
+
description: 'Search requirements by keyword across names, summaries, tags, and references.',
|
|
56
|
+
examples: [
|
|
57
|
+
['Search ideas', '$0 search "cli package"'],
|
|
58
|
+
['Limit results', '$0 search parse --limit 5 --json']
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"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"}
|
package/out/generator.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expandToNode, joinToNode, toString } from 'langium/generate';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { extractDestinationAndName } from './util.js';
|
|
5
|
+
export function generateJavaScript(model, filePath, destination) {
|
|
6
|
+
const data = extractDestinationAndName(filePath, destination);
|
|
7
|
+
const generatedFilePath = `${path.join(data.destination, data.name)}.js`;
|
|
8
|
+
const fileNode = expandToNode `
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
${joinToNode(model.elements, (element) => `// ${element.$type}`, { appendNewLineIfNotEmpty: true })}
|
|
12
|
+
`.appendNewLineIfNotEmpty();
|
|
13
|
+
if (!fs.existsSync(data.destination)) {
|
|
14
|
+
fs.mkdirSync(data.destination, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
fs.writeFileSync(generatedFilePath, toString(fileNode));
|
|
17
|
+
return generatedFilePath;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,UAAU,kBAAkB,CAAC,KAAY,EAAE,QAAgB,EAAE,WAA+B;IAC9F,MAAM,IAAI,GAAG,yBAAyB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC9D,MAAM,iBAAiB,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IAEzE,MAAM,QAAQ,GAAG,YAAY,CAAA;;;UAGvB,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAwB,EAAE,EAAE,CAAC,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC;KACvH,CAAC,uBAAuB,EAAE,CAAC;IAE5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxD,OAAO,iBAAiB,CAAC;AAC7B,CAAC"}
|
package/out/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/out/main.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Cli, Builtins } from 'clipanion';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { ParseCommand } from './commands/parse.js';
|
|
6
|
+
import { AnalyseCommand } from './commands/analyse.js';
|
|
7
|
+
import { SearchCommand } from './commands/search.js';
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
|
|
10
|
+
const cli = new Cli({
|
|
11
|
+
binaryLabel: 'reqlan',
|
|
12
|
+
binaryName: 'reqlan',
|
|
13
|
+
binaryVersion: packageJson.version
|
|
14
|
+
});
|
|
15
|
+
cli.register(Builtins.HelpCommand);
|
|
16
|
+
cli.register(Builtins.VersionCommand);
|
|
17
|
+
cli.register(ParseCommand);
|
|
18
|
+
cli.register(AnalyseCommand);
|
|
19
|
+
cli.register(SearchCommand);
|
|
20
|
+
await cli.runExit(process.argv.slice(2));
|
|
21
|
+
//# sourceMappingURL=main.js.map
|
package/out/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE5F,CAAC;AAEF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAChB,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,aAAa,EAAE,WAAW,CAAC,OAAO;CACrC,CAAC,CAAC;AAEH,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACnC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACtC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3B,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AAC7B,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAE5B,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
package/out/output.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function emit(value: unknown, asJson: boolean): void;
|
package/out/output.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function emit(value, asJson) {
|
|
2
|
+
if (asJson) {
|
|
3
|
+
console.log(JSON.stringify(value, null, 2));
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === 'string') {
|
|
7
|
+
console.log(value);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
console.log(String(value));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,IAAI,CAAC,KAAc,EAAE,MAAe;IAChD,IAAI,MAAM,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,OAAO;IACX,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO;IACX,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
package/out/runtime.d.ts
ADDED
package/out/runtime.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { activateAnalysisRuntime, AnalysisApi, createAnalysisRuntime, deactivateAnalysisRuntime } from '@reqlan/analytical';
|
|
4
|
+
export function resolveWorkspaceRoot(cwd) {
|
|
5
|
+
const fromEnv = process.env.REQLAN_WORKSPACE?.trim();
|
|
6
|
+
if (fromEnv) {
|
|
7
|
+
return resolve(fromEnv);
|
|
8
|
+
}
|
|
9
|
+
let dir = resolve(cwd ?? process.cwd());
|
|
10
|
+
for (;;) {
|
|
11
|
+
if (existsSync(resolve(dir, '.git')) || existsSync(resolve(dir, 'pnpm-workspace.yaml'))) {
|
|
12
|
+
return dir;
|
|
13
|
+
}
|
|
14
|
+
const parent = dirname(dir);
|
|
15
|
+
if (parent === dir) {
|
|
16
|
+
return resolve(cwd ?? process.cwd());
|
|
17
|
+
}
|
|
18
|
+
dir = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function withAnalysisApi(cwd, run) {
|
|
22
|
+
const workspaceRoot = resolveWorkspaceRoot(cwd);
|
|
23
|
+
const runtime = createAnalysisRuntime({
|
|
24
|
+
workspaceRoot,
|
|
25
|
+
storagePath: process.env.REQLAN_INDEX_PATH
|
|
26
|
+
});
|
|
27
|
+
await activateAnalysisRuntime(runtime);
|
|
28
|
+
try {
|
|
29
|
+
return await run(new AnalysisApi(runtime));
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await deactivateAnalysisRuntime(runtime);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EACH,uBAAuB,EACvB,WAAW,EACX,qBAAqB,EACrB,yBAAyB,EAE5B,MAAM,oBAAoB,CAAC;AAE5B,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC;IACrD,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,SAAS,CAAC;QACN,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC;YACtF,OAAO,GAAG,CAAC;QACf,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACjB,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACjC,GAAuB,EACvB,GAAqC;IAErC,MAAM,aAAa,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,OAAO,GAAoB,qBAAqB,CAAC;QACnD,aAAa;QACb,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;KAC7C,CAAC,CAAC;IACH,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC;QACD,OAAO,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,CAAC;YAAS,CAAC;QACP,MAAM,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC"}
|
package/out/util.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AstNode, LangiumCoreServices, LangiumDocument } from 'langium';
|
|
2
|
+
export interface ParseResult {
|
|
3
|
+
document: LangiumDocument;
|
|
4
|
+
diagnostics: Array<{
|
|
5
|
+
severity: number;
|
|
6
|
+
line: number;
|
|
7
|
+
character: number;
|
|
8
|
+
message: string;
|
|
9
|
+
text: string;
|
|
10
|
+
}>;
|
|
11
|
+
errorCount: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function parseDocument(fileName: string, services: LangiumCoreServices): Promise<ParseResult>;
|
|
14
|
+
export declare function extractAstNode<T extends AstNode>(fileName: string, services: LangiumCoreServices): Promise<T>;
|
package/out/util.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { URI } from 'langium';
|
|
4
|
+
export async function parseDocument(fileName, services) {
|
|
5
|
+
const extensions = services.LanguageMetaData.fileExtensions;
|
|
6
|
+
if (!extensions.includes(path.extname(fileName))) {
|
|
7
|
+
throw new Error(`Please choose a file with one of these extensions: ${extensions.join(', ')}.`);
|
|
8
|
+
}
|
|
9
|
+
if (!fs.existsSync(fileName)) {
|
|
10
|
+
throw new Error(`File ${fileName} does not exist.`);
|
|
11
|
+
}
|
|
12
|
+
const document = await services.shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName)));
|
|
13
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
14
|
+
const diagnostics = (document.diagnostics ?? []).map(d => ({
|
|
15
|
+
severity: d.severity ?? 0,
|
|
16
|
+
line: d.range.start.line + 1,
|
|
17
|
+
character: d.range.start.character + 1,
|
|
18
|
+
message: typeof d.message === 'string' ? d.message : d.message.value,
|
|
19
|
+
text: document.textDocument.getText(d.range)
|
|
20
|
+
}));
|
|
21
|
+
const errorCount = diagnostics.filter(d => d.severity === 1).length;
|
|
22
|
+
return { document, diagnostics, errorCount };
|
|
23
|
+
}
|
|
24
|
+
export async function extractAstNode(fileName, services) {
|
|
25
|
+
const { document, errorCount, diagnostics } = await parseDocument(fileName, services);
|
|
26
|
+
if (errorCount > 0) {
|
|
27
|
+
const details = diagnostics
|
|
28
|
+
.filter(d => d.severity === 1)
|
|
29
|
+
.map(d => `line ${d.line}: ${d.message}`)
|
|
30
|
+
.join('\n');
|
|
31
|
+
throw new Error(`Validation errors:\n${details}`);
|
|
32
|
+
}
|
|
33
|
+
return document.parseResult?.value;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=util.js.map
|
package/out/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAc9B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,QAA6B;IAC/E,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,cAAc,CAAC;IAC5D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,sDAAsD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpG,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,kBAAkB,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,mBAAmB,CACjF,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CACnC,CAAC;IACF,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAExF,MAAM,WAAW,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvD,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;QAC5B,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC;QACtC,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK;QACpE,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;KAC/C,CAAC,CAAC,CAAC;IACJ,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IAEpE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAChC,QAAgB,EAChB,QAA6B;IAE7B,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtF,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,WAAW;aACtB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACxC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,KAAU,CAAC;AAC5C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reqlan/cli",
|
|
3
|
+
"description": "CLI for parsing and analysing reqlan requirement graphs",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "AGPL-3.0-only",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/littletuna4/reqlan.git"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20.10.0",
|
|
13
|
+
"npm": ">=10.2.3"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"out"
|
|
18
|
+
],
|
|
19
|
+
"bin": {
|
|
20
|
+
"reqlan": "./bin/cli.js",
|
|
21
|
+
"rq": "./bin/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"clipanion": "3.2.1",
|
|
28
|
+
"langium": "~4.3.0",
|
|
29
|
+
"@reqlan/language": "1.4.0",
|
|
30
|
+
"@reqlan/analytical": "0.4.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"shx": "~0.4.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"clean": "shx rm -fr *.tsbuildinfo out",
|
|
37
|
+
"build": "tsc -b tsconfig.json",
|
|
38
|
+
"build:clean": "pnpm run clean && pnpm run build"
|
|
39
|
+
}
|
|
40
|
+
}
|