@likec4/generators 1.59.2 → 1.59.3
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/dist/chunks/newline.mjs +4 -0
- package/dist/index.d.mts +18 -1
- package/dist/index.mjs +27 -27
- package/dist/likec4/index.d.mts +61 -19
- package/dist/likec4/index.mjs +2 -3
- package/package.json +10 -10
- package/src/d2/generate-d2.ts +3 -2
- package/src/index.ts +1 -0
- package/src/likec4/operators/base.ts +2 -3
- package/src/likec4/operators/views.ts +5 -1
- package/src/likec4/schemas/specification.ts +1 -5
- package/src/likec4/schemas/views.ts +8 -3
- package/src/markdown/generate-markdown.ts +52 -0
- package/src/markdown/index.ts +1 -0
- package/src/mmd/generate-mmd.ts +3 -2
- package/src/newline.ts +21 -0
- package/src/puml/generate-puml.ts +3 -2
- package/src/views-data-ts/generate-views-data.ts +8 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/generators",
|
|
3
|
-
"version": "1.59.
|
|
3
|
+
"version": "1.59.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -45,27 +45,27 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@likec4/core": "1.59.3",
|
|
49
|
+
"@likec4/config": "1.59.3",
|
|
50
|
+
"@likec4/log": "1.59.3",
|
|
48
51
|
"langium": "3.5.0",
|
|
49
52
|
"pako": "^2.1.0",
|
|
50
|
-
"remeda": "^2.
|
|
51
|
-
"immer": "^11.1.
|
|
53
|
+
"remeda": "^2.45.0",
|
|
54
|
+
"immer": "^11.1.18",
|
|
52
55
|
"json5": "^2.2.3",
|
|
53
56
|
"type-fest": "^4.41.0",
|
|
54
57
|
"indent-string": "^5.0.0",
|
|
55
58
|
"strip-indent": "^4.1.1",
|
|
56
|
-
"zod": "^4.4.3"
|
|
57
|
-
"@likec4/core": "1.59.2",
|
|
58
|
-
"@likec4/log": "1.59.2",
|
|
59
|
-
"@likec4/config": "1.59.2"
|
|
59
|
+
"zod": "^4.4.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
+
"@likec4/tsconfig": "1.59.3",
|
|
63
|
+
"@likec4/devops": "1.59.3",
|
|
62
64
|
"@types/node": "~22.19.19",
|
|
63
65
|
"@types/pako": "^2.0.4",
|
|
64
66
|
"typescript": "6.0.3",
|
|
65
67
|
"tsdown": "^0.22.9",
|
|
66
|
-
"vitest": "4.1.
|
|
67
|
-
"@likec4/tsconfig": "1.59.2",
|
|
68
|
-
"@likec4/devops": "1.59.2"
|
|
68
|
+
"vitest": "4.1.11"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"typecheck": "tsc -b --verbose",
|
package/src/d2/generate-d2.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { LikeC4ViewModel } from '@likec4/core/model'
|
|
2
2
|
import type { aux, ComputedNode, NodeId, ProcessedView as AnyView } from '@likec4/core/types'
|
|
3
|
-
import { CompositeGeneratorNode, joinToNode
|
|
3
|
+
import { CompositeGeneratorNode, joinToNode } from 'langium/generate'
|
|
4
4
|
import { isNullish as isNil } from 'remeda'
|
|
5
|
+
import { NL, toStringLF } from '../newline'
|
|
5
6
|
|
|
6
7
|
const capitalizeFirstLetter = (value: string) => value.charAt(0).toLocaleUpperCase() + value.slice(1)
|
|
7
8
|
|
|
@@ -93,7 +94,7 @@ export function generateD2(viewmodel: LikeC4ViewModel<aux.Unknown>) {
|
|
|
93
94
|
.append(out => edge.label && out.append(': ', JSON.stringify(edge.label)))
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
return
|
|
97
|
+
return toStringLF(
|
|
97
98
|
new CompositeGeneratorNode()
|
|
98
99
|
.append('direction: ', d2direction(view), NL, NL)
|
|
99
100
|
.append(
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ export {
|
|
|
17
17
|
parseDrawioToLikeC4,
|
|
18
18
|
parseDrawioToLikeC4Multi,
|
|
19
19
|
} from './drawio/parse-drawio'
|
|
20
|
+
export { generateMarkdown } from './markdown/generate-markdown'
|
|
20
21
|
export { generateMermaid } from './mmd/generate-mmd'
|
|
21
22
|
export { generateLikeC4Model } from './model/generate-likec4-model'
|
|
22
23
|
export { generatePuml } from './puml/generate-puml'
|
|
@@ -6,8 +6,6 @@ import {
|
|
|
6
6
|
CompositeGeneratorNode,
|
|
7
7
|
joinToNode,
|
|
8
8
|
NewLineNode,
|
|
9
|
-
NL,
|
|
10
|
-
toString,
|
|
11
9
|
} from 'langium/generate'
|
|
12
10
|
import {
|
|
13
11
|
filter,
|
|
@@ -28,6 +26,7 @@ import { dedent } from 'strip-indent'
|
|
|
28
26
|
import type { IfAny, Or } from 'type-fest'
|
|
29
27
|
import * as z from 'zod/v4'
|
|
30
28
|
import type * as z4 from 'zod/v4/core'
|
|
29
|
+
import { NL, toStringLF } from '../../newline'
|
|
31
30
|
|
|
32
31
|
function hasContent(out: Generated): boolean {
|
|
33
32
|
if (typeof out === 'string') {
|
|
@@ -109,7 +108,7 @@ export function fresh(ctx?: unknown) {
|
|
|
109
108
|
*/
|
|
110
109
|
export function materialize(ctx: AnyCtx | Op<any>, defaultIndentation: string | number = 2): string {
|
|
111
110
|
const out = isFunction(ctx) ? executeOnFresh(undefined, [ctx]).out : ctx.out
|
|
112
|
-
return
|
|
111
|
+
return toStringLF(out, defaultIndentation)
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
/**
|
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
invariant,
|
|
4
4
|
nonexhaustive,
|
|
5
5
|
} from '@likec4/core'
|
|
6
|
-
import { CompositeGeneratorNode
|
|
6
|
+
import { CompositeGeneratorNode } from 'langium/generate'
|
|
7
7
|
import { hasAtLeast, values } from 'remeda'
|
|
8
|
+
import { NL } from '../../newline'
|
|
8
9
|
import { schemas } from '../schemas'
|
|
9
10
|
import {
|
|
10
11
|
type Op,
|
|
@@ -187,6 +188,7 @@ export const elementView = zodOp(schemas.views.elementView.partial({ _type: true
|
|
|
187
188
|
lines(
|
|
188
189
|
tagsProperty(),
|
|
189
190
|
viewTitleProperty(),
|
|
191
|
+
property('order'),
|
|
190
192
|
descriptionProperty(),
|
|
191
193
|
linksProperty(),
|
|
192
194
|
),
|
|
@@ -237,6 +239,7 @@ export const deploymentView = zodOp(schemas.views.deploymentView.partial({ _type
|
|
|
237
239
|
lines(
|
|
238
240
|
tagsProperty(),
|
|
239
241
|
viewTitleProperty(),
|
|
242
|
+
property('order'),
|
|
240
243
|
descriptionProperty(),
|
|
241
244
|
linksProperty(),
|
|
242
245
|
),
|
|
@@ -460,6 +463,7 @@ export const dynamicView = zodOp(schemas.views.dynamicView.partial({ _type: true
|
|
|
460
463
|
lines(
|
|
461
464
|
tagsProperty(),
|
|
462
465
|
viewTitleProperty(),
|
|
466
|
+
property('order'),
|
|
463
467
|
descriptionProperty(),
|
|
464
468
|
linksProperty(),
|
|
465
469
|
property('variant'),
|
|
@@ -38,11 +38,7 @@ export const relationship = z
|
|
|
38
38
|
|
|
39
39
|
export const tagSpec = z
|
|
40
40
|
.object({
|
|
41
|
-
color:
|
|
42
|
-
.string()
|
|
43
|
-
.regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)
|
|
44
|
-
.optional()
|
|
45
|
-
.catch(undefined),
|
|
41
|
+
color: common.color.nullable(),
|
|
46
42
|
})
|
|
47
43
|
.partial()
|
|
48
44
|
.transform(pickBy(isNonNullish))
|
|
@@ -62,7 +62,9 @@ export const viewRuleRank = z
|
|
|
62
62
|
|
|
63
63
|
// Manually typed due to recursive z.lazy schema
|
|
64
64
|
export type ViewRuleGroupInput = {
|
|
65
|
-
groupRules: Array<
|
|
65
|
+
groupRules: Array<
|
|
66
|
+
z.input<typeof viewRulePredicate> | z.input<typeof viewRuleGlobalPredicate> | ViewRuleGroupInput
|
|
67
|
+
>
|
|
66
68
|
title: string | null
|
|
67
69
|
color?: z.input<typeof common.color> | undefined
|
|
68
70
|
border?: z.input<typeof common.border> | undefined
|
|
@@ -74,7 +76,9 @@ export type ViewRuleGroupInput = {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export type ViewRuleGroupOutput = {
|
|
77
|
-
groupRules: Array<
|
|
79
|
+
groupRules: Array<
|
|
80
|
+
z.infer<typeof viewRulePredicate> | z.infer<typeof viewRuleGlobalPredicate> | ViewRuleGroupOutput
|
|
81
|
+
>
|
|
78
82
|
title: string | null
|
|
79
83
|
color?: z.infer<typeof common.color> | undefined
|
|
80
84
|
border?: z.infer<typeof common.border> | undefined
|
|
@@ -87,7 +91,7 @@ export type ViewRuleGroupOutput = {
|
|
|
87
91
|
|
|
88
92
|
export const viewRuleGroup: z.ZodType<ViewRuleGroupOutput, ViewRuleGroupInput> = z.lazy(() =>
|
|
89
93
|
z.object({
|
|
90
|
-
groupRules: z.array(z.union([viewRulePredicate, viewRuleGroup])),
|
|
94
|
+
groupRules: z.array(z.union([viewRulePredicate, viewRuleGlobalPredicate, viewRuleGroup])),
|
|
91
95
|
title: z.string().nullable(),
|
|
92
96
|
color: common.color.optional(),
|
|
93
97
|
border: common.border.optional(),
|
|
@@ -114,6 +118,7 @@ const viewProps = z.object({
|
|
|
114
118
|
id: common.viewId,
|
|
115
119
|
title: z.string().nullish(),
|
|
116
120
|
description: common.markdownOrString.nullish(),
|
|
121
|
+
order: z.number().int().nonnegative().optional(),
|
|
117
122
|
tags: common.tags.nullish(),
|
|
118
123
|
links: common.links.nullish(),
|
|
119
124
|
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LikeC4Model, LikeC4ViewModel } from '@likec4/core/model'
|
|
2
|
+
import type { aux } from '@likec4/core/types'
|
|
3
|
+
import { CompositeGeneratorNode } from 'langium/generate'
|
|
4
|
+
import { generateMermaid } from '../mmd/generate-mmd'
|
|
5
|
+
import { NL, toStringLF } from '../newline'
|
|
6
|
+
|
|
7
|
+
type ViewModel = LikeC4ViewModel<aux.Unknown>
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Appends a single view to the document as a level-3 heading, its optional
|
|
11
|
+
* description, and a fenced Mermaid diagram.
|
|
12
|
+
*/
|
|
13
|
+
function appendView(doc: CompositeGeneratorNode, view: ViewModel): void {
|
|
14
|
+
doc.append('### ', view.titleOrId, NL, NL)
|
|
15
|
+
if (view.description.nonEmpty) {
|
|
16
|
+
doc.append(view.description.md, NL, NL)
|
|
17
|
+
}
|
|
18
|
+
doc.append('```mermaid', NL, generateMermaid(view).trimEnd(), NL, '```', NL, NL)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type GenerateMarkdownOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* `LikeC4Project` has no description field of its own yet, so callers source this from
|
|
24
|
+
* wherever they keep it (e.g. the project config's `metadata` bag) and pass it through.
|
|
25
|
+
* If a first-class description ever lands on `LikeC4Project`, read it from the model
|
|
26
|
+
* directly instead and drop this option.
|
|
27
|
+
*/
|
|
28
|
+
description?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Renders a LikeC4 model as a Markdown document: the project title, an optional
|
|
33
|
+
* description, and every authored view as a Mermaid diagram. Views without a
|
|
34
|
+
* `sourcePath` (e.g. auto-generated index views) are skipped.
|
|
35
|
+
*/
|
|
36
|
+
export function generateMarkdown(
|
|
37
|
+
model: LikeC4Model<aux.Unknown>,
|
|
38
|
+
options: GenerateMarkdownOptions = {},
|
|
39
|
+
): string {
|
|
40
|
+
const doc = new CompositeGeneratorNode()
|
|
41
|
+
doc.append('# ', model.project.title ?? model.projectId, NL, NL)
|
|
42
|
+
if (options.description) {
|
|
43
|
+
doc.append(options.description, NL, NL)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const view of model.views()) {
|
|
47
|
+
if (view.$view.sourcePath === undefined) continue
|
|
48
|
+
appendView(doc, view)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return toStringLF(doc)
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './generate-markdown'
|
package/src/mmd/generate-mmd.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { nonexhaustive } from '@likec4/core'
|
|
2
2
|
import type { LikeC4ViewModel } from '@likec4/core/model'
|
|
3
3
|
import type { aux, NodeId, ProcessedView as AnyView } from '@likec4/core/types'
|
|
4
|
-
import { CompositeGeneratorNode, joinToNode
|
|
4
|
+
import { CompositeGeneratorNode, joinToNode } from 'langium/generate'
|
|
5
5
|
import { isNullish as isNil } from 'remeda'
|
|
6
|
+
import { NL, toStringLF } from '../newline'
|
|
6
7
|
|
|
7
8
|
const capitalizeFirstLetter = (value: string) => value.charAt(0).toLocaleUpperCase() + value.slice(1)
|
|
8
9
|
|
|
@@ -97,7 +98,7 @@ export function generateMermaid(viewmodel: LikeC4ViewModel<aux.Unknown>) {
|
|
|
97
98
|
)
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
return
|
|
101
|
+
return toStringLF(
|
|
101
102
|
new CompositeGeneratorNode()
|
|
102
103
|
.append(
|
|
103
104
|
'---',
|
package/src/newline.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GeneratorNode } from 'langium/generate'
|
|
2
|
+
import { NewLineNode, toString } from 'langium/generate'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Line separator for all generated output.
|
|
6
|
+
*
|
|
7
|
+
* Langium's own `NL` resolves to `os.EOL`, which emits CRLF on Windows and makes
|
|
8
|
+
* generated artifacts (Markdown, Mermaid, D2, PlantUML, LikeC4 DSL) differ per platform.
|
|
9
|
+
* Generators must be deterministic, so always emit LF.
|
|
10
|
+
*/
|
|
11
|
+
export const NL = new NewLineNode('\n')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Renders a generator node with LF line endings.
|
|
15
|
+
*
|
|
16
|
+
* Some Langium helpers insert `os.EOL` internally (for example `joinToNode`'s
|
|
17
|
+
* `appendNewLineIfNotEmpty`), so normalize the rendered output as well as using {@link NL}.
|
|
18
|
+
*/
|
|
19
|
+
export function toStringLF(node: GeneratorNode | undefined, defaultIndentation?: string | number): string {
|
|
20
|
+
return toString(node, defaultIndentation).replaceAll('\r\n', '\n')
|
|
21
|
+
}
|
|
@@ -13,8 +13,9 @@ import type {
|
|
|
13
13
|
ThemeColor,
|
|
14
14
|
} from '@likec4/core/types'
|
|
15
15
|
import { RichText } from '@likec4/core/types'
|
|
16
|
-
import { CompositeGeneratorNode, joinToNode
|
|
16
|
+
import { CompositeGeneratorNode, joinToNode } from 'langium/generate'
|
|
17
17
|
import { isEmptyish, isNullish as isNil } from 'remeda'
|
|
18
|
+
import { NL, toStringLF } from '../newline'
|
|
18
19
|
|
|
19
20
|
const capitalizeFirstLetter = (value: string) => value.charAt(0).toLocaleUpperCase() + value.slice(1)
|
|
20
21
|
|
|
@@ -233,7 +234,7 @@ export function generatePuml(viewmodel: LikeC4ViewModel<aux.Unknown>) {
|
|
|
233
234
|
return out.append(NL)
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
return
|
|
237
|
+
return toStringLF(
|
|
237
238
|
new CompositeGeneratorNode()
|
|
238
239
|
.append('@startuml', NL)
|
|
239
240
|
.append(printHeader(), NL)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DiagramView } from '@likec4/core'
|
|
2
2
|
import JSON5 from 'json5'
|
|
3
|
-
import { CompositeGeneratorNode, expandToNode, joinToNode
|
|
3
|
+
import { CompositeGeneratorNode, expandToNode, joinToNode } from 'langium/generate'
|
|
4
|
+
import { NL, toStringLF } from '../newline'
|
|
4
5
|
import { generateViewId } from './generateViewId'
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -21,7 +22,7 @@ export function generateViewsDataJs(diagrams: Iterable<DiagramView>) {
|
|
|
21
22
|
|
|
22
23
|
if (views.length == 0) {
|
|
23
24
|
out.append('export const LikeC4Views = {}', NL)
|
|
24
|
-
return
|
|
25
|
+
return toStringLF(out)
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
out.appendTemplate`
|
|
@@ -54,7 +55,7 @@ export function generateViewsDataJs(diagrams: Iterable<DiagramView>) {
|
|
|
54
55
|
|
|
55
56
|
/* prettier-ignore-end */
|
|
56
57
|
`.append(NL)
|
|
57
|
-
return
|
|
58
|
+
return toStringLF(out)
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -78,7 +79,7 @@ export function generateViewsDataTs(diagrams: Iterable<DiagramView>) {
|
|
|
78
79
|
|
|
79
80
|
if (views.length === 0) {
|
|
80
81
|
out.append('export {}', NL)
|
|
81
|
-
return
|
|
82
|
+
return toStringLF(out)
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
out.appendTemplate`
|
|
@@ -118,7 +119,7 @@ export function generateViewsDataTs(diagrams: Iterable<DiagramView>) {
|
|
|
118
119
|
|
|
119
120
|
/* prettier-ignore-end */
|
|
120
121
|
`.append(NL)
|
|
121
|
-
return
|
|
122
|
+
return toStringLF(out)
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
/**
|
|
@@ -140,7 +141,7 @@ export function generateViewsDataDTs(diagrams: Iterable<DiagramView>) {
|
|
|
140
141
|
|
|
141
142
|
if (views.length == 0) {
|
|
142
143
|
out.append('export {}', NL)
|
|
143
|
-
return
|
|
144
|
+
return toStringLF(out)
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
out.appendTemplate`
|
|
@@ -152,5 +153,5 @@ export function generateViewsDataDTs(diagrams: Iterable<DiagramView>) {
|
|
|
152
153
|
|
|
153
154
|
/* prettier-ignore-end */
|
|
154
155
|
`.append(NL)
|
|
155
|
-
return
|
|
156
|
+
return toStringLF(out)
|
|
156
157
|
}
|