@nowline/core 0.6.0 → 0.7.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.
@@ -0,0 +1,7 @@
1
+ import { type JsonAstNode, type NowlineDocument } from './schema.js';
2
+ export interface ParseJsonResult {
3
+ document: NowlineDocument;
4
+ ast: JsonAstNode;
5
+ }
6
+ export declare function parseNowlineJson(text: string, filePath: string): ParseJsonResult;
7
+ //# sourceMappingURL=parse-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-json.d.ts","sourceRoot":"","sources":["../../src/convert/parse-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAA0B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7F,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,WAAW,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAgChF"}
@@ -0,0 +1,33 @@
1
+ import { NOWLINE_SCHEMA_VERSION } from './schema.js';
2
+ export function parseNowlineJson(text, filePath) {
3
+ let parsed;
4
+ try {
5
+ parsed = JSON.parse(text);
6
+ }
7
+ catch (err) {
8
+ throw new Error(`${filePath}: invalid JSON — ${err instanceof Error ? err.message : String(err)}`);
9
+ }
10
+ const doc = parsed;
11
+ if (!isRecord(doc)) {
12
+ throw new Error(`${filePath}: JSON root must be an object with $nowlineSchema and ast.`);
13
+ }
14
+ const schema = doc.$nowlineSchema;
15
+ if (typeof schema !== 'string') {
16
+ throw new Error(`${filePath}: missing "$nowlineSchema" at document root.`);
17
+ }
18
+ if (schema !== NOWLINE_SCHEMA_VERSION) {
19
+ throw new Error(`${filePath}: unsupported $nowlineSchema "${schema}" (this tool supports "${NOWLINE_SCHEMA_VERSION}").`);
20
+ }
21
+ const ast = doc.ast;
22
+ if (!isRecord(ast) || typeof ast.$type !== 'string') {
23
+ throw new Error(`${filePath}: document.ast must be an object with a "$type" field.`);
24
+ }
25
+ if (ast.$type !== 'NowlineFile') {
26
+ throw new Error(`${filePath}: document.ast.$type must be "NowlineFile" (got "${String(ast.$type)}").`);
27
+ }
28
+ return { document: doc, ast: ast };
29
+ }
30
+ function isRecord(value) {
31
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
32
+ }
33
+ //# sourceMappingURL=parse-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../src/convert/parse-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,sBAAsB,EAAwB,MAAM,aAAa,CAAC;AAO7F,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,QAAgB;IAC3D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACX,GAAG,QAAQ,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACpF,CAAC;IACN,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC;IACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,4DAA4D,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,cAAc,CAAC;IAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,8CAA8C,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,MAAM,KAAK,sBAAsB,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACX,GAAG,QAAQ,iCAAiC,MAAM,0BAA0B,sBAAsB,KAAK,CAC1G,CAAC;IACN,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,wDAAwD,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,GAAG,QAAQ,oDAAoD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CACxF,CAAC;IACN,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,GAAiC,EAAE,GAAG,EAAE,GAA6B,EAAE,CAAC;AAC/F,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { JsonAstNode } from './schema.js';
2
+ export interface PrintOptions {
3
+ indent?: string;
4
+ }
5
+ export declare function printNowlineFile(ast: JsonAstNode, options?: PrintOptions): string;
6
+ //# sourceMappingURL=printer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../../src/convert/printer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA4B/C,MAAM,WAAW,YAAY;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAIrF"}
@@ -0,0 +1,331 @@
1
+ // Keyed-property canonical order. Keys not in this list sort alphabetically after.
2
+ const KEY_ORDER = [
3
+ 'date',
4
+ 'effort',
5
+ 'on',
6
+ 'size',
7
+ 'duration',
8
+ 'status',
9
+ 'owner',
10
+ 'after',
11
+ 'before',
12
+ 'remaining',
13
+ 'labels',
14
+ 'style',
15
+ 'link',
16
+ 'author',
17
+ 'start',
18
+ 'scale',
19
+ 'calendar',
20
+ 'header-position',
21
+ 'timeline-position',
22
+ 'minor-grid',
23
+ ];
24
+ const INDENT = ' ';
25
+ export function printNowlineFile(ast, options = {}) {
26
+ const printer = new Printer(options.indent ?? INDENT);
27
+ printer.file(ast);
28
+ return printer.toString();
29
+ }
30
+ class Printer {
31
+ indent;
32
+ lines = [];
33
+ constructor(indent) {
34
+ this.indent = indent;
35
+ }
36
+ toString() {
37
+ const text = this.lines.join('\n');
38
+ return text.endsWith('\n') ? text : `${text}\n`;
39
+ }
40
+ file(file) {
41
+ assertType(file, 'NowlineFile');
42
+ const directive = file.directive;
43
+ if (directive) {
44
+ const props = asArray(directive.properties);
45
+ const tail = props.length > 0 ? ` ${props.map(renderProperty).join(' ')}` : '';
46
+ this.line(0, `nowline ${getString(directive, 'version')}${tail}`);
47
+ this.blank();
48
+ }
49
+ for (const inc of asArray(file.includes)) {
50
+ this.include(inc);
51
+ }
52
+ if (asArray(file.includes).length > 0)
53
+ this.blank();
54
+ if (file.hasConfig) {
55
+ this.line(0, 'config');
56
+ this.blank();
57
+ for (const entry of asArray(file.configEntries)) {
58
+ this.configEntry(entry);
59
+ this.blank();
60
+ }
61
+ }
62
+ if (file.roadmapDecl) {
63
+ this.roadmap(file.roadmapDecl);
64
+ this.blank();
65
+ }
66
+ for (const entry of asArray(file.roadmapEntries)) {
67
+ this.roadmapEntry(entry, 0);
68
+ }
69
+ }
70
+ include(inc) {
71
+ assertType(inc, 'IncludeDeclaration');
72
+ const p = getString(inc, 'path');
73
+ const options = asArray(inc.options)
74
+ .map((o) => `${getString(o, 'key')}:${getString(o, 'value')}`)
75
+ .join(' ');
76
+ const tail = options ? ` ${options}` : '';
77
+ this.line(0, `include ${JSON.stringify(p)}${tail}`);
78
+ }
79
+ configEntry(entry) {
80
+ switch (entry.$type) {
81
+ case 'ScaleBlock':
82
+ return this.blockDecl('scale', asArray(entry.properties));
83
+ case 'CalendarBlock':
84
+ return this.blockDecl('calendar', asArray(entry.properties));
85
+ case 'StyleDeclaration':
86
+ return this.styleDecl(entry);
87
+ case 'DefaultDeclaration':
88
+ return this.defaultDecl(entry);
89
+ default:
90
+ throw new Error(`Unknown config entry type: ${String(entry.$type)}`);
91
+ }
92
+ }
93
+ blockDecl(keyword, properties) {
94
+ this.line(0, keyword);
95
+ for (const p of properties) {
96
+ this.line(1, renderBlockProperty(p));
97
+ }
98
+ }
99
+ styleDecl(entry) {
100
+ assertType(entry, 'StyleDeclaration');
101
+ const header = declarationHeader('style', entry, []);
102
+ this.line(0, header);
103
+ for (const p of asArray(entry.properties)) {
104
+ this.line(1, renderBlockProperty(p));
105
+ }
106
+ }
107
+ defaultDecl(entry) {
108
+ assertType(entry, 'DefaultDeclaration');
109
+ const entityType = getString(entry, 'entityType');
110
+ const props = renderProperties(asArray(entry.properties));
111
+ const tail = props ? ` ${props}` : '';
112
+ this.line(0, `default ${entityType}${tail}`);
113
+ }
114
+ roadmap(decl) {
115
+ assertType(decl, 'RoadmapDeclaration');
116
+ this.line(0, declarationHeader('roadmap', decl, asArray(decl.properties)));
117
+ }
118
+ roadmapEntry(entry, depth) {
119
+ switch (entry.$type) {
120
+ case 'PersonDeclaration':
121
+ return this.simpleEntity('person', entry, depth);
122
+ case 'TeamDeclaration':
123
+ return this.team(entry, depth);
124
+ case 'AnchorDeclaration':
125
+ return this.simpleEntity('anchor', entry, depth);
126
+ case 'SizeDeclaration':
127
+ return this.simpleEntity('size', entry, depth);
128
+ case 'StatusDeclaration':
129
+ return this.simpleEntity('status', entry, depth);
130
+ case 'LabelDeclaration':
131
+ return this.simpleEntity('label', entry, depth);
132
+ case 'MilestoneDeclaration':
133
+ return this.simpleEntity('milestone', entry, depth);
134
+ case 'FootnoteDeclaration':
135
+ return this.simpleEntity('footnote', entry, depth);
136
+ case 'SwimlaneDeclaration':
137
+ return this.swimlane(entry, depth);
138
+ default:
139
+ throw new Error(`Unknown roadmap entry type: ${String(entry.$type)}`);
140
+ }
141
+ }
142
+ simpleEntity(keyword, entry, depth) {
143
+ this.line(depth, declarationHeader(keyword, entry, asArray(entry.properties)));
144
+ this.maybeDescription(entry, depth + 1);
145
+ }
146
+ team(entry, depth) {
147
+ this.line(depth, declarationHeader('team', entry, asArray(entry.properties)));
148
+ for (const child of asArray(entry.content)) {
149
+ this.teamContent(child, depth + 1);
150
+ }
151
+ }
152
+ teamContent(node, depth) {
153
+ if (node.$type === 'PersonMemberRef') {
154
+ this.line(depth, `person ${getString(node, 'ref')}`);
155
+ return;
156
+ }
157
+ if (node.$type === 'TeamDeclaration') {
158
+ return this.team(node, depth);
159
+ }
160
+ if (node.$type === 'PersonDeclaration') {
161
+ return this.simpleEntity('person', node, depth);
162
+ }
163
+ if (node.$type === 'DescriptionDirective') {
164
+ return this.descriptionDirective(node, depth);
165
+ }
166
+ throw new Error(`Unknown team content type: ${String(node.$type)}`);
167
+ }
168
+ swimlane(entry, depth) {
169
+ this.line(depth, declarationHeader('swimlane', entry, asArray(entry.properties)));
170
+ for (const child of asArray(entry.content)) {
171
+ this.swimlaneContent(child, depth + 1);
172
+ }
173
+ }
174
+ swimlaneContent(node, depth) {
175
+ switch (node.$type) {
176
+ case 'ItemDeclaration':
177
+ this.simpleEntity('item', node, depth);
178
+ return;
179
+ case 'ParallelBlock':
180
+ this.parallelBlock(node, depth);
181
+ return;
182
+ case 'GroupBlock':
183
+ this.groupBlock(node, depth);
184
+ return;
185
+ case 'DescriptionDirective':
186
+ this.descriptionDirective(node, depth);
187
+ return;
188
+ default:
189
+ throw new Error(`Unknown swimlane content type: ${String(node.$type)}`);
190
+ }
191
+ }
192
+ parallelBlock(entry, depth) {
193
+ this.line(depth, declarationHeader('parallel', entry, asArray(entry.properties)));
194
+ for (const child of asArray(entry.content)) {
195
+ this.swimlaneContent(child, depth + 1);
196
+ }
197
+ }
198
+ groupBlock(entry, depth) {
199
+ this.line(depth, declarationHeader('group', entry, asArray(entry.properties)));
200
+ for (const child of asArray(entry.content)) {
201
+ this.swimlaneContent(child, depth + 1);
202
+ }
203
+ }
204
+ descriptionDirective(node, depth) {
205
+ assertType(node, 'DescriptionDirective');
206
+ this.line(depth, `description ${JSON.stringify(getString(node, 'text'))}`);
207
+ }
208
+ maybeDescription(entry, depth) {
209
+ const desc = entry.description;
210
+ if (desc)
211
+ this.descriptionDirective(desc, depth);
212
+ }
213
+ line(depth, text) {
214
+ this.lines.push(this.indent.repeat(depth) + text);
215
+ }
216
+ blank() {
217
+ if (this.lines.length === 0)
218
+ return;
219
+ if (this.lines[this.lines.length - 1] === '')
220
+ return;
221
+ this.lines.push('');
222
+ }
223
+ }
224
+ function declarationHeader(keyword, entry, properties) {
225
+ const id = entry.name;
226
+ const title = entry.title;
227
+ const parts = [keyword];
228
+ if (id)
229
+ parts.push(id);
230
+ if (title)
231
+ parts.push(JSON.stringify(title));
232
+ const props = renderProperties(properties);
233
+ if (props)
234
+ parts.push(props);
235
+ return parts.join(' ');
236
+ }
237
+ function renderProperties(properties) {
238
+ return orderProperties(properties).map(renderProperty).join(' ');
239
+ }
240
+ function orderProperties(properties) {
241
+ const indexOf = new Map(KEY_ORDER.map((k, i) => [k, i]));
242
+ return [...properties].sort((a, b) => {
243
+ const ak = normalizeKey(getString(a, 'key'));
244
+ const bk = normalizeKey(getString(b, 'key'));
245
+ const ai = indexOf.get(ak);
246
+ const bi = indexOf.get(bk);
247
+ if (ai !== undefined && bi !== undefined)
248
+ return ai - bi;
249
+ if (ai !== undefined)
250
+ return -1;
251
+ if (bi !== undefined)
252
+ return 1;
253
+ return ak.localeCompare(bk);
254
+ });
255
+ }
256
+ function renderProperty(prop) {
257
+ const key = normalizeKey(getString(prop, 'key'));
258
+ const values = asStringArray(prop.values);
259
+ const value = prop.value;
260
+ if (values.length >= 2) {
261
+ return `${key}:[${values.map(formatAtom).join(', ')}]`;
262
+ }
263
+ if (values.length === 1) {
264
+ return `${key}:${formatAtom(values[0])}`;
265
+ }
266
+ if (value !== undefined && value !== '') {
267
+ return `${key}:${formatAtom(value)}`;
268
+ }
269
+ return `${key}:`;
270
+ }
271
+ // Block-style property (one per line, rendered as `key: value`). Used inside
272
+ // indented blocks like `scale`, `calendar`, and `style`.
273
+ function renderBlockProperty(prop) {
274
+ const key = normalizeKey(getString(prop, 'key'));
275
+ const values = asStringArray(prop.values);
276
+ const value = prop.value;
277
+ if (values.length >= 2) {
278
+ return `${key}: [${values.map(formatAtom).join(', ')}]`;
279
+ }
280
+ if (values.length === 1) {
281
+ return `${key}: ${formatAtom(values[0])}`;
282
+ }
283
+ if (value !== undefined && value !== '') {
284
+ return `${key}: ${formatAtom(value)}`;
285
+ }
286
+ return `${key}:`;
287
+ }
288
+ function normalizeKey(key) {
289
+ return key.endsWith(':') ? key.slice(0, -1) : key;
290
+ }
291
+ const URL_RE = /^https?:\/\//;
292
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
293
+ const DURATION_RE = /^\d+(?:\.\d+)?[dwmqy]$/;
294
+ const PERCENTAGE_RE = /^\d+%$/;
295
+ const HEX_COLOR_RE = /^#[0-9a-fA-F]{3,8}$/;
296
+ const INTEGER_RE = /^\d+$/;
297
+ const ID_RE = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
298
+ function formatAtom(atom) {
299
+ if (URL_RE.test(atom) ||
300
+ DATE_RE.test(atom) ||
301
+ DURATION_RE.test(atom) ||
302
+ PERCENTAGE_RE.test(atom) ||
303
+ HEX_COLOR_RE.test(atom) ||
304
+ INTEGER_RE.test(atom) ||
305
+ ID_RE.test(atom)) {
306
+ return atom;
307
+ }
308
+ if (atom.startsWith('"') && atom.endsWith('"'))
309
+ return atom;
310
+ return JSON.stringify(atom);
311
+ }
312
+ function asArray(value) {
313
+ if (Array.isArray(value))
314
+ return value;
315
+ return [];
316
+ }
317
+ function asStringArray(value) {
318
+ if (!Array.isArray(value))
319
+ return [];
320
+ return value.filter((v) => typeof v === 'string');
321
+ }
322
+ function getString(node, key) {
323
+ const value = node[key];
324
+ return typeof value === 'string' ? value : '';
325
+ }
326
+ function assertType(node, expected) {
327
+ if (node.$type !== expected) {
328
+ throw new Error(`Expected $type "${expected}", got "${String(node.$type)}"`);
329
+ }
330
+ }
331
+ //# sourceMappingURL=printer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.js","sourceRoot":"","sources":["../../src/convert/printer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { LangiumDocument } from 'langium';
2
+ import type { NowlineFile } from '../generated/ast.js';
3
+ export declare const NOWLINE_SCHEMA_VERSION = "1";
4
+ export interface NowlineDocument {
5
+ $nowlineSchema: string;
6
+ file: {
7
+ uri: string;
8
+ source: string;
9
+ };
10
+ ast: JsonAstNode;
11
+ }
12
+ export interface Position {
13
+ start: {
14
+ line: number;
15
+ column: number;
16
+ offset: number;
17
+ };
18
+ end: {
19
+ line: number;
20
+ column: number;
21
+ offset: number;
22
+ };
23
+ }
24
+ export interface JsonAstNode {
25
+ $type: string;
26
+ $position?: Position;
27
+ [key: string]: unknown;
28
+ }
29
+ export interface SerializeOptions {
30
+ includePositions?: boolean;
31
+ }
32
+ export declare function serializeToJson(document: LangiumDocument<NowlineFile>, source: string, options?: SerializeOptions): NowlineDocument;
33
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/convert/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,GAAG,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACrB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACzD;AAED,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAOD,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,eAAe,CAAC,WAAW,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC/B,eAAe,CAUjB"}
@@ -0,0 +1,77 @@
1
+ export const NOWLINE_SCHEMA_VERSION = '1';
2
+ // Keys that Langium adds to AST nodes that we don't want in the JSON form.
3
+ const CONTAINER_KEYS = new Set(['$container', '$containerProperty', '$containerIndex']);
4
+ // $cstNode / $document are runtime-only. $type and $position are emitted.
5
+ const RUNTIME_KEYS = new Set(['$cstNode', '$document']);
6
+ export function serializeToJson(document, source, options = {}) {
7
+ const includePositions = options.includePositions ?? true;
8
+ return {
9
+ $nowlineSchema: NOWLINE_SCHEMA_VERSION,
10
+ file: {
11
+ uri: document.uri.toString(),
12
+ source,
13
+ },
14
+ ast: serializeNode(document.parseResult.value, includePositions),
15
+ };
16
+ }
17
+ function serializeNode(node, includePositions) {
18
+ const out = { $type: node.$type };
19
+ if (includePositions) {
20
+ const pos = cstPosition(node.$cstNode);
21
+ if (pos)
22
+ out.$position = pos;
23
+ }
24
+ for (const [key, value] of Object.entries(node)) {
25
+ if (key.startsWith('$'))
26
+ continue;
27
+ if (CONTAINER_KEYS.has(key) || RUNTIME_KEYS.has(key))
28
+ continue;
29
+ out[key] = serializeValue(value, includePositions);
30
+ }
31
+ return out;
32
+ }
33
+ function serializeValue(value, includePositions) {
34
+ if (value === null || value === undefined)
35
+ return value;
36
+ if (Array.isArray(value)) {
37
+ return value.map((v) => serializeValue(v, includePositions));
38
+ }
39
+ if (isAstNode(value)) {
40
+ return serializeNode(value, includePositions);
41
+ }
42
+ if (typeof value === 'object') {
43
+ const record = value;
44
+ const out = {};
45
+ for (const [k, v] of Object.entries(record)) {
46
+ if (k.startsWith('$'))
47
+ continue;
48
+ if (CONTAINER_KEYS.has(k) || RUNTIME_KEYS.has(k))
49
+ continue;
50
+ out[k] = serializeValue(v, includePositions);
51
+ }
52
+ return out;
53
+ }
54
+ return value;
55
+ }
56
+ function isAstNode(value) {
57
+ return (value !== null &&
58
+ typeof value === 'object' &&
59
+ typeof value.$type === 'string');
60
+ }
61
+ function cstPosition(cst) {
62
+ if (!cst)
63
+ return undefined;
64
+ return {
65
+ start: {
66
+ line: cst.range.start.line + 1,
67
+ column: cst.range.start.character + 1,
68
+ offset: cst.offset,
69
+ },
70
+ end: {
71
+ line: cst.range.end.line + 1,
72
+ column: cst.range.end.character + 1,
73
+ offset: cst.end,
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/convert/schema.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { type ParseJsonResult, parseNowlineJson } from './convert/parse-json.js';
2
+ export { type PrintOptions, printNowlineFile } from './convert/printer.js';
3
+ export { type JsonAstNode, NOWLINE_SCHEMA_VERSION, type NowlineDocument, type Position, type SerializeOptions, serializeToJson, } from './convert/schema.js';
1
4
  export { collectDocumentDiagnostics, extractSuggestion, inferCodeFromMessage, isBuiltinParseDiagnostic, LANGIUM_LEXING_ERROR, LANGIUM_PARSING_ERROR, type LangiumLikeDiagnostic, type LexerErrorLike, type ParserErrorLike, type RawDiagnostic, type RawDiagnosticOrigin, resolveDiagnosticCode, stableValidatorCode, } from './diagnostics/index.js';
2
5
  export * from './generated/ast.js';
3
6
  export * from './generated/module.js';
@@ -7,4 +10,5 @@ export { type IncludeMode, type IsolatedRegion, type ResolveDiagnostic, type Res
7
10
  export type { NowlineAddedServices, NowlineServices } from './language/nowline-module.js';
8
11
  export { createNowlineServices, NowlineModule } from './language/nowline-module.js';
9
12
  export { NowlineValidator, registerValidationChecks } from './language/nowline-validator.js';
13
+ export { TEMPLATE_NAMES, type TemplateName } from './templates.js';
10
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACH,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,cAAc,EACd,EAAE,GACL,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACH,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,eAAe,GAClB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,KAAK,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACH,KAAK,WAAW,EAChB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,eAAe,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACH,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,cAAc,EACd,EAAE,GACL,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACH,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,eAAe,GAClB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ export { parseNowlineJson } from './convert/parse-json.js';
2
+ export { printNowlineFile } from './convert/printer.js';
3
+ export { NOWLINE_SCHEMA_VERSION, serializeToJson, } from './convert/schema.js';
1
4
  export { collectDocumentDiagnostics, extractSuggestion, inferCodeFromMessage, isBuiltinParseDiagnostic, LANGIUM_LEXING_ERROR, LANGIUM_PARSING_ERROR, resolveDiagnosticCode, stableValidatorCode, } from './diagnostics/index.js';
2
5
  export * from './generated/ast.js';
3
6
  export * from './generated/module.js';
@@ -6,4 +9,5 @@ export { registerBundle, tr, } from './i18n/index.js';
6
9
  export { resolveIncludes, } from './language/include-resolver.js';
7
10
  export { createNowlineServices, NowlineModule } from './language/nowline-module.js';
8
11
  export { NowlineValidator, registerValidationChecks } from './language/nowline-validator.js';
12
+ export { TEMPLATE_NAMES } from './templates.js';
9
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EAMrB,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAGH,cAAc,EACd,EAAE,GACL,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAQH,eAAe,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAqB,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAEH,sBAAsB,EAItB,eAAe,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EAMrB,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAGH,cAAc,EACd,EAAE,GACL,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAQH,eAAe,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAqB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type TemplateName = 'minimal' | 'teams' | 'product' | 'showcase';
2
+ export declare const TEMPLATE_NAMES: readonly TemplateName[];
3
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAExE,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAAgD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const TEMPLATE_NAMES = ['minimal', 'teams', 'product', 'showcase'];
2
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nowline/core",
3
- "version": "0.6.0",
4
- "description": "Nowline DSL parser, AST, and validation",
3
+ "version": "0.7.0",
4
+ "description": "Reads and checks the Nowline roadmap language — the parser and validator every Nowline tool builds on.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
7
7
  "node": ">=22",
@@ -0,0 +1,44 @@
1
+ import { type JsonAstNode, NOWLINE_SCHEMA_VERSION, type NowlineDocument } from './schema.js';
2
+
3
+ export interface ParseJsonResult {
4
+ document: NowlineDocument;
5
+ ast: JsonAstNode;
6
+ }
7
+
8
+ export function parseNowlineJson(text: string, filePath: string): ParseJsonResult {
9
+ let parsed: unknown;
10
+ try {
11
+ parsed = JSON.parse(text);
12
+ } catch (err) {
13
+ throw new Error(
14
+ `${filePath}: invalid JSON — ${err instanceof Error ? err.message : String(err)}`,
15
+ );
16
+ }
17
+ const doc = parsed;
18
+ if (!isRecord(doc)) {
19
+ throw new Error(`${filePath}: JSON root must be an object with $nowlineSchema and ast.`);
20
+ }
21
+ const schema = doc.$nowlineSchema;
22
+ if (typeof schema !== 'string') {
23
+ throw new Error(`${filePath}: missing "$nowlineSchema" at document root.`);
24
+ }
25
+ if (schema !== NOWLINE_SCHEMA_VERSION) {
26
+ throw new Error(
27
+ `${filePath}: unsupported $nowlineSchema "${schema}" (this tool supports "${NOWLINE_SCHEMA_VERSION}").`,
28
+ );
29
+ }
30
+ const ast = doc.ast;
31
+ if (!isRecord(ast) || typeof ast.$type !== 'string') {
32
+ throw new Error(`${filePath}: document.ast must be an object with a "$type" field.`);
33
+ }
34
+ if (ast.$type !== 'NowlineFile') {
35
+ throw new Error(
36
+ `${filePath}: document.ast.$type must be "NowlineFile" (got "${String(ast.$type)}").`,
37
+ );
38
+ }
39
+ return { document: doc as unknown as NowlineDocument, ast: ast as unknown as JsonAstNode };
40
+ }
41
+
42
+ function isRecord(value: unknown): value is Record<string, unknown> {
43
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
44
+ }
@@ -0,0 +1,358 @@
1
+ import type { JsonAstNode } from './schema.js';
2
+
3
+ // Keyed-property canonical order. Keys not in this list sort alphabetically after.
4
+ const KEY_ORDER = [
5
+ 'date',
6
+ 'effort',
7
+ 'on',
8
+ 'size',
9
+ 'duration',
10
+ 'status',
11
+ 'owner',
12
+ 'after',
13
+ 'before',
14
+ 'remaining',
15
+ 'labels',
16
+ 'style',
17
+ 'link',
18
+ 'author',
19
+ 'start',
20
+ 'scale',
21
+ 'calendar',
22
+ 'header-position',
23
+ 'timeline-position',
24
+ 'minor-grid',
25
+ ];
26
+
27
+ const INDENT = ' ';
28
+
29
+ export interface PrintOptions {
30
+ indent?: string;
31
+ }
32
+
33
+ export function printNowlineFile(ast: JsonAstNode, options: PrintOptions = {}): string {
34
+ const printer = new Printer(options.indent ?? INDENT);
35
+ printer.file(ast);
36
+ return printer.toString();
37
+ }
38
+
39
+ class Printer {
40
+ private readonly lines: string[] = [];
41
+
42
+ constructor(private readonly indent: string) {}
43
+
44
+ toString(): string {
45
+ const text = this.lines.join('\n');
46
+ return text.endsWith('\n') ? text : `${text}\n`;
47
+ }
48
+
49
+ file(file: JsonAstNode): void {
50
+ assertType(file, 'NowlineFile');
51
+ const directive = file.directive as JsonAstNode | undefined;
52
+ if (directive) {
53
+ const props = asArray(directive.properties);
54
+ const tail = props.length > 0 ? ` ${props.map(renderProperty).join(' ')}` : '';
55
+ this.line(0, `nowline ${getString(directive, 'version')}${tail}`);
56
+ this.blank();
57
+ }
58
+ for (const inc of asArray(file.includes)) {
59
+ this.include(inc);
60
+ }
61
+ if (asArray(file.includes).length > 0) this.blank();
62
+ if (file.hasConfig) {
63
+ this.line(0, 'config');
64
+ this.blank();
65
+ for (const entry of asArray(file.configEntries)) {
66
+ this.configEntry(entry);
67
+ this.blank();
68
+ }
69
+ }
70
+ if (file.roadmapDecl) {
71
+ this.roadmap(file.roadmapDecl as JsonAstNode);
72
+ this.blank();
73
+ }
74
+ for (const entry of asArray(file.roadmapEntries)) {
75
+ this.roadmapEntry(entry, 0);
76
+ }
77
+ }
78
+
79
+ include(inc: JsonAstNode): void {
80
+ assertType(inc, 'IncludeDeclaration');
81
+ const p = getString(inc, 'path');
82
+ const options = asArray(inc.options)
83
+ .map((o) => `${getString(o, 'key')}:${getString(o, 'value')}`)
84
+ .join(' ');
85
+ const tail = options ? ` ${options}` : '';
86
+ this.line(0, `include ${JSON.stringify(p)}${tail}`);
87
+ }
88
+
89
+ configEntry(entry: JsonAstNode): void {
90
+ switch (entry.$type) {
91
+ case 'ScaleBlock':
92
+ return this.blockDecl('scale', asArray(entry.properties));
93
+ case 'CalendarBlock':
94
+ return this.blockDecl('calendar', asArray(entry.properties));
95
+ case 'StyleDeclaration':
96
+ return this.styleDecl(entry);
97
+ case 'DefaultDeclaration':
98
+ return this.defaultDecl(entry);
99
+ default:
100
+ throw new Error(`Unknown config entry type: ${String(entry.$type)}`);
101
+ }
102
+ }
103
+
104
+ blockDecl(keyword: string, properties: JsonAstNode[]): void {
105
+ this.line(0, keyword);
106
+ for (const p of properties) {
107
+ this.line(1, renderBlockProperty(p));
108
+ }
109
+ }
110
+
111
+ styleDecl(entry: JsonAstNode): void {
112
+ assertType(entry, 'StyleDeclaration');
113
+ const header = declarationHeader('style', entry, []);
114
+ this.line(0, header);
115
+ for (const p of asArray(entry.properties)) {
116
+ this.line(1, renderBlockProperty(p));
117
+ }
118
+ }
119
+
120
+ defaultDecl(entry: JsonAstNode): void {
121
+ assertType(entry, 'DefaultDeclaration');
122
+ const entityType = getString(entry, 'entityType');
123
+ const props = renderProperties(asArray(entry.properties));
124
+ const tail = props ? ` ${props}` : '';
125
+ this.line(0, `default ${entityType}${tail}`);
126
+ }
127
+
128
+ roadmap(decl: JsonAstNode): void {
129
+ assertType(decl, 'RoadmapDeclaration');
130
+ this.line(0, declarationHeader('roadmap', decl, asArray(decl.properties)));
131
+ }
132
+
133
+ roadmapEntry(entry: JsonAstNode, depth: number): void {
134
+ switch (entry.$type) {
135
+ case 'PersonDeclaration':
136
+ return this.simpleEntity('person', entry, depth);
137
+ case 'TeamDeclaration':
138
+ return this.team(entry, depth);
139
+ case 'AnchorDeclaration':
140
+ return this.simpleEntity('anchor', entry, depth);
141
+ case 'SizeDeclaration':
142
+ return this.simpleEntity('size', entry, depth);
143
+ case 'StatusDeclaration':
144
+ return this.simpleEntity('status', entry, depth);
145
+ case 'LabelDeclaration':
146
+ return this.simpleEntity('label', entry, depth);
147
+ case 'MilestoneDeclaration':
148
+ return this.simpleEntity('milestone', entry, depth);
149
+ case 'FootnoteDeclaration':
150
+ return this.simpleEntity('footnote', entry, depth);
151
+ case 'SwimlaneDeclaration':
152
+ return this.swimlane(entry, depth);
153
+ default:
154
+ throw new Error(`Unknown roadmap entry type: ${String(entry.$type)}`);
155
+ }
156
+ }
157
+
158
+ simpleEntity(keyword: string, entry: JsonAstNode, depth: number): void {
159
+ this.line(depth, declarationHeader(keyword, entry, asArray(entry.properties)));
160
+ this.maybeDescription(entry, depth + 1);
161
+ }
162
+
163
+ team(entry: JsonAstNode, depth: number): void {
164
+ this.line(depth, declarationHeader('team', entry, asArray(entry.properties)));
165
+ for (const child of asArray(entry.content)) {
166
+ this.teamContent(child, depth + 1);
167
+ }
168
+ }
169
+
170
+ teamContent(node: JsonAstNode, depth: number): void {
171
+ if (node.$type === 'PersonMemberRef') {
172
+ this.line(depth, `person ${getString(node, 'ref')}`);
173
+ return;
174
+ }
175
+ if (node.$type === 'TeamDeclaration') {
176
+ return this.team(node, depth);
177
+ }
178
+ if (node.$type === 'PersonDeclaration') {
179
+ return this.simpleEntity('person', node, depth);
180
+ }
181
+ if (node.$type === 'DescriptionDirective') {
182
+ return this.descriptionDirective(node, depth);
183
+ }
184
+ throw new Error(`Unknown team content type: ${String(node.$type)}`);
185
+ }
186
+
187
+ swimlane(entry: JsonAstNode, depth: number): void {
188
+ this.line(depth, declarationHeader('swimlane', entry, asArray(entry.properties)));
189
+ for (const child of asArray(entry.content)) {
190
+ this.swimlaneContent(child, depth + 1);
191
+ }
192
+ }
193
+
194
+ swimlaneContent(node: JsonAstNode, depth: number): void {
195
+ switch (node.$type) {
196
+ case 'ItemDeclaration':
197
+ this.simpleEntity('item', node, depth);
198
+ return;
199
+ case 'ParallelBlock':
200
+ this.parallelBlock(node, depth);
201
+ return;
202
+ case 'GroupBlock':
203
+ this.groupBlock(node, depth);
204
+ return;
205
+ case 'DescriptionDirective':
206
+ this.descriptionDirective(node, depth);
207
+ return;
208
+ default:
209
+ throw new Error(`Unknown swimlane content type: ${String(node.$type)}`);
210
+ }
211
+ }
212
+
213
+ parallelBlock(entry: JsonAstNode, depth: number): void {
214
+ this.line(depth, declarationHeader('parallel', entry, asArray(entry.properties)));
215
+ for (const child of asArray(entry.content)) {
216
+ this.swimlaneContent(child, depth + 1);
217
+ }
218
+ }
219
+
220
+ groupBlock(entry: JsonAstNode, depth: number): void {
221
+ this.line(depth, declarationHeader('group', entry, asArray(entry.properties)));
222
+ for (const child of asArray(entry.content)) {
223
+ this.swimlaneContent(child, depth + 1);
224
+ }
225
+ }
226
+
227
+ descriptionDirective(node: JsonAstNode, depth: number): void {
228
+ assertType(node, 'DescriptionDirective');
229
+ this.line(depth, `description ${JSON.stringify(getString(node, 'text'))}`);
230
+ }
231
+
232
+ maybeDescription(entry: JsonAstNode, depth: number): void {
233
+ const desc = entry.description as JsonAstNode | undefined;
234
+ if (desc) this.descriptionDirective(desc, depth);
235
+ }
236
+
237
+ private line(depth: number, text: string): void {
238
+ this.lines.push(this.indent.repeat(depth) + text);
239
+ }
240
+
241
+ private blank(): void {
242
+ if (this.lines.length === 0) return;
243
+ if (this.lines[this.lines.length - 1] === '') return;
244
+ this.lines.push('');
245
+ }
246
+ }
247
+
248
+ function declarationHeader(keyword: string, entry: JsonAstNode, properties: JsonAstNode[]): string {
249
+ const id = entry.name as string | undefined;
250
+ const title = entry.title as string | undefined;
251
+ const parts = [keyword];
252
+ if (id) parts.push(id);
253
+ if (title) parts.push(JSON.stringify(title));
254
+ const props = renderProperties(properties);
255
+ if (props) parts.push(props);
256
+ return parts.join(' ');
257
+ }
258
+
259
+ function renderProperties(properties: JsonAstNode[]): string {
260
+ return orderProperties(properties).map(renderProperty).join(' ');
261
+ }
262
+
263
+ function orderProperties(properties: JsonAstNode[]): JsonAstNode[] {
264
+ const indexOf = new Map(KEY_ORDER.map((k, i) => [k, i] as const));
265
+ return [...properties].sort((a, b) => {
266
+ const ak = normalizeKey(getString(a, 'key'));
267
+ const bk = normalizeKey(getString(b, 'key'));
268
+ const ai = indexOf.get(ak);
269
+ const bi = indexOf.get(bk);
270
+ if (ai !== undefined && bi !== undefined) return ai - bi;
271
+ if (ai !== undefined) return -1;
272
+ if (bi !== undefined) return 1;
273
+ return ak.localeCompare(bk);
274
+ });
275
+ }
276
+
277
+ function renderProperty(prop: JsonAstNode): string {
278
+ const key = normalizeKey(getString(prop, 'key'));
279
+ const values = asStringArray(prop.values);
280
+ const value = prop.value as string | undefined;
281
+ if (values.length >= 2) {
282
+ return `${key}:[${values.map(formatAtom).join(', ')}]`;
283
+ }
284
+ if (values.length === 1) {
285
+ return `${key}:${formatAtom(values[0])}`;
286
+ }
287
+ if (value !== undefined && value !== '') {
288
+ return `${key}:${formatAtom(value)}`;
289
+ }
290
+ return `${key}:`;
291
+ }
292
+
293
+ // Block-style property (one per line, rendered as `key: value`). Used inside
294
+ // indented blocks like `scale`, `calendar`, and `style`.
295
+ function renderBlockProperty(prop: JsonAstNode): string {
296
+ const key = normalizeKey(getString(prop, 'key'));
297
+ const values = asStringArray(prop.values);
298
+ const value = prop.value as string | undefined;
299
+ if (values.length >= 2) {
300
+ return `${key}: [${values.map(formatAtom).join(', ')}]`;
301
+ }
302
+ if (values.length === 1) {
303
+ return `${key}: ${formatAtom(values[0])}`;
304
+ }
305
+ if (value !== undefined && value !== '') {
306
+ return `${key}: ${formatAtom(value)}`;
307
+ }
308
+ return `${key}:`;
309
+ }
310
+
311
+ function normalizeKey(key: string): string {
312
+ return key.endsWith(':') ? key.slice(0, -1) : key;
313
+ }
314
+
315
+ const URL_RE = /^https?:\/\//;
316
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
317
+ const DURATION_RE = /^\d+(?:\.\d+)?[dwmqy]$/;
318
+ const PERCENTAGE_RE = /^\d+%$/;
319
+ const HEX_COLOR_RE = /^#[0-9a-fA-F]{3,8}$/;
320
+ const INTEGER_RE = /^\d+$/;
321
+ const ID_RE = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
322
+
323
+ function formatAtom(atom: string): string {
324
+ if (
325
+ URL_RE.test(atom) ||
326
+ DATE_RE.test(atom) ||
327
+ DURATION_RE.test(atom) ||
328
+ PERCENTAGE_RE.test(atom) ||
329
+ HEX_COLOR_RE.test(atom) ||
330
+ INTEGER_RE.test(atom) ||
331
+ ID_RE.test(atom)
332
+ ) {
333
+ return atom;
334
+ }
335
+ if (atom.startsWith('"') && atom.endsWith('"')) return atom;
336
+ return JSON.stringify(atom);
337
+ }
338
+
339
+ function asArray(value: unknown): JsonAstNode[] {
340
+ if (Array.isArray(value)) return value as JsonAstNode[];
341
+ return [];
342
+ }
343
+
344
+ function asStringArray(value: unknown): string[] {
345
+ if (!Array.isArray(value)) return [];
346
+ return value.filter((v): v is string => typeof v === 'string');
347
+ }
348
+
349
+ function getString(node: JsonAstNode | Record<string, unknown>, key: string): string {
350
+ const value = (node as Record<string, unknown>)[key];
351
+ return typeof value === 'string' ? value : '';
352
+ }
353
+
354
+ function assertType(node: JsonAstNode, expected: string): void {
355
+ if (node.$type !== expected) {
356
+ throw new Error(`Expected $type "${expected}", got "${String(node.$type)}"`);
357
+ }
358
+ }
@@ -0,0 +1,105 @@
1
+ import type { AstNode, CstNode, LangiumDocument } from 'langium';
2
+ import type { NowlineFile } from '../generated/ast.js';
3
+
4
+ export const NOWLINE_SCHEMA_VERSION = '1';
5
+
6
+ export interface NowlineDocument {
7
+ $nowlineSchema: string;
8
+ file: { uri: string; source: string };
9
+ ast: JsonAstNode;
10
+ }
11
+
12
+ export interface Position {
13
+ start: { line: number; column: number; offset: number };
14
+ end: { line: number; column: number; offset: number };
15
+ }
16
+
17
+ export interface JsonAstNode {
18
+ $type: string;
19
+ $position?: Position;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ export interface SerializeOptions {
24
+ includePositions?: boolean;
25
+ }
26
+
27
+ // Keys that Langium adds to AST nodes that we don't want in the JSON form.
28
+ const CONTAINER_KEYS = new Set(['$container', '$containerProperty', '$containerIndex']);
29
+ // $cstNode / $document are runtime-only. $type and $position are emitted.
30
+ const RUNTIME_KEYS = new Set(['$cstNode', '$document']);
31
+
32
+ export function serializeToJson(
33
+ document: LangiumDocument<NowlineFile>,
34
+ source: string,
35
+ options: SerializeOptions = {},
36
+ ): NowlineDocument {
37
+ const includePositions = options.includePositions ?? true;
38
+ return {
39
+ $nowlineSchema: NOWLINE_SCHEMA_VERSION,
40
+ file: {
41
+ uri: document.uri.toString(),
42
+ source,
43
+ },
44
+ ast: serializeNode(document.parseResult.value, includePositions),
45
+ };
46
+ }
47
+
48
+ function serializeNode(node: AstNode, includePositions: boolean): JsonAstNode {
49
+ const out: JsonAstNode = { $type: node.$type };
50
+ if (includePositions) {
51
+ const pos = cstPosition(node.$cstNode);
52
+ if (pos) out.$position = pos;
53
+ }
54
+ for (const [key, value] of Object.entries(node)) {
55
+ if (key.startsWith('$')) continue;
56
+ if (CONTAINER_KEYS.has(key) || RUNTIME_KEYS.has(key)) continue;
57
+ out[key] = serializeValue(value, includePositions);
58
+ }
59
+ return out;
60
+ }
61
+
62
+ function serializeValue(value: unknown, includePositions: boolean): unknown {
63
+ if (value === null || value === undefined) return value;
64
+ if (Array.isArray(value)) {
65
+ return value.map((v) => serializeValue(v, includePositions));
66
+ }
67
+ if (isAstNode(value)) {
68
+ return serializeNode(value, includePositions);
69
+ }
70
+ if (typeof value === 'object') {
71
+ const record = value as Record<string, unknown>;
72
+ const out: Record<string, unknown> = {};
73
+ for (const [k, v] of Object.entries(record)) {
74
+ if (k.startsWith('$')) continue;
75
+ if (CONTAINER_KEYS.has(k) || RUNTIME_KEYS.has(k)) continue;
76
+ out[k] = serializeValue(v, includePositions);
77
+ }
78
+ return out;
79
+ }
80
+ return value;
81
+ }
82
+
83
+ function isAstNode(value: unknown): value is AstNode {
84
+ return (
85
+ value !== null &&
86
+ typeof value === 'object' &&
87
+ typeof (value as { $type?: unknown }).$type === 'string'
88
+ );
89
+ }
90
+
91
+ function cstPosition(cst: CstNode | undefined): Position | undefined {
92
+ if (!cst) return undefined;
93
+ return {
94
+ start: {
95
+ line: cst.range.start.line + 1,
96
+ column: cst.range.start.character + 1,
97
+ offset: cst.offset,
98
+ },
99
+ end: {
100
+ line: cst.range.end.line + 1,
101
+ column: cst.range.end.character + 1,
102
+ offset: cst.end,
103
+ },
104
+ };
105
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,13 @@
1
+ export { type ParseJsonResult, parseNowlineJson } from './convert/parse-json.js';
2
+ export { type PrintOptions, printNowlineFile } from './convert/printer.js';
3
+ export {
4
+ type JsonAstNode,
5
+ NOWLINE_SCHEMA_VERSION,
6
+ type NowlineDocument,
7
+ type Position,
8
+ type SerializeOptions,
9
+ serializeToJson,
10
+ } from './convert/schema.js';
1
11
  export {
2
12
  collectDocumentDiagnostics,
3
13
  extractSuggestion,
@@ -35,3 +45,4 @@ export {
35
45
  export type { NowlineAddedServices, NowlineServices } from './language/nowline-module.js';
36
46
  export { createNowlineServices, NowlineModule } from './language/nowline-module.js';
37
47
  export { NowlineValidator, registerValidationChecks } from './language/nowline-validator.js';
48
+ export { TEMPLATE_NAMES, type TemplateName } from './templates.js';
@@ -0,0 +1,3 @@
1
+ export type TemplateName = 'minimal' | 'teams' | 'product' | 'showcase';
2
+
3
+ export const TEMPLATE_NAMES: readonly TemplateName[] = ['minimal', 'teams', 'product', 'showcase'];