@arkadia/data 0.1.7 → 0.1.8
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/.prettierrc +8 -0
- package/README.md +166 -112
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/Decoder.d.ts.map +1 -1
- package/dist/core/Decoder.js +123 -97
- package/dist/core/Decoder.js.map +1 -1
- package/dist/core/Encoder.d.ts +1 -2
- package/dist/core/Encoder.d.ts.map +1 -1
- package/dist/core/Encoder.js +74 -76
- package/dist/core/Encoder.js.map +1 -1
- package/dist/core/Parser.d.ts +1 -1
- package/dist/core/Parser.d.ts.map +1 -1
- package/dist/core/Parser.js +11 -11
- package/dist/core/Parser.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/models/Meta.d.ts +3 -2
- package/dist/models/Meta.d.ts.map +1 -1
- package/dist/models/Meta.js +3 -3
- package/dist/models/Meta.js.map +1 -1
- package/dist/models/Node.d.ts +4 -3
- package/dist/models/Node.d.ts.map +1 -1
- package/dist/models/Node.js +29 -23
- package/dist/models/Node.js.map +1 -1
- package/dist/models/Schema.d.ts.map +1 -1
- package/dist/models/Schema.js +27 -21
- package/dist/models/Schema.js.map +1 -1
- package/eslint.config.mjs +42 -0
- package/package.json +11 -1
- package/scripts/verify-build.js +95 -92
- package/src/config.ts +75 -75
- package/src/core/Decoder.ts +984 -922
- package/src/core/Encoder.ts +364 -371
- package/src/core/Parser.ts +112 -112
- package/src/index.ts +18 -20
- package/src/models/Meta.ts +107 -107
- package/src/models/Node.ts +190 -185
- package/src/models/Schema.ts +198 -193
- package/tests/00.meta.test.ts +19 -25
- package/tests/00.node.test.ts +40 -48
- package/tests/00.primitive.test.ts +121 -95
- package/tests/00.schema.test.ts +28 -35
- package/tests/01.schema.test.ts +42 -52
- package/tests/02.data.test.ts +69 -75
- package/tests/03.errors.test.ts +53 -55
- package/tests/04.list.test.ts +192 -193
- package/tests/05.record.test.ts +54 -56
- package/tests/06.meta.test.ts +393 -389
- package/tests/utils.ts +47 -44
- package/tsconfig.json +27 -29
- package/vitest.config.ts +1 -1
package/src/core/Parser.ts
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
import { Node } from '../models/Node';
|
|
1
|
+
import { Node, Primitive } from '../models/Node';
|
|
2
2
|
import { Schema, SchemaKind } from '../models/Schema';
|
|
3
3
|
|
|
4
4
|
export class EncodingError extends Error {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
constructor(message: string) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'EncodingError';
|
|
8
|
+
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// --------------------------------------------------------------
|
|
12
12
|
// Helper: Type Wrapper
|
|
13
13
|
// --------------------------------------------------------------
|
|
14
|
-
const TYPE_STRING =
|
|
15
|
-
const TYPE_NUMBER =
|
|
16
|
-
const TYPE_BOOL =
|
|
17
|
-
const TYPE_NULL =
|
|
14
|
+
const TYPE_STRING = 'string';
|
|
15
|
+
const TYPE_NUMBER = 'number';
|
|
16
|
+
const TYPE_BOOL = 'bool';
|
|
17
|
+
const TYPE_NULL = 'null';
|
|
18
18
|
// const TYPE_ANY = "any";
|
|
19
19
|
|
|
20
20
|
// --------------------------------------------------------------
|
|
21
21
|
// Primitive -> Node(primitive)
|
|
22
22
|
// --------------------------------------------------------------
|
|
23
23
|
|
|
24
|
-
function parsePrimitive(v:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
function parsePrimitive(v: Primitive): Node {
|
|
25
|
+
let schema: Schema;
|
|
26
|
+
|
|
27
|
+
if (typeof v === 'string') {
|
|
28
|
+
schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_STRING });
|
|
29
|
+
} else if (typeof v === 'boolean') {
|
|
30
|
+
schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_BOOL });
|
|
31
|
+
} else if (typeof v === 'number') {
|
|
32
|
+
schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NUMBER });
|
|
33
|
+
} else if (v === null || v === undefined) {
|
|
34
|
+
schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NULL });
|
|
35
|
+
} else {
|
|
36
|
+
throw new EncodingError(`Unsupported primitive: ${v}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new Node(schema, { value: v });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// --------------------------------------------------------------
|
|
43
43
|
// List -> Node(list)
|
|
44
44
|
// --------------------------------------------------------------
|
|
45
45
|
|
|
46
|
-
function parseList(arr:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
}
|
|
46
|
+
function parseList(arr: unknown[]): Node {
|
|
47
|
+
// 1. EMPTY LIST
|
|
48
|
+
if (arr.length === 0) {
|
|
49
|
+
const elementSchema = new Schema(SchemaKind.PRIMITIVE, { typeName: 'any' });
|
|
50
|
+
const listSchema = new Schema(SchemaKind.LIST, { element: elementSchema });
|
|
51
|
+
return new Node(listSchema, { elements: [] });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. PARSE ALL ITEMS
|
|
55
|
+
const parsedItems: Node[] = arr.map((v) => parseDataToNode(v));
|
|
56
|
+
|
|
57
|
+
// Use the first element as the baseline
|
|
58
|
+
const firstSchema = parsedItems[0].schema;
|
|
59
|
+
const isListOfRecords = firstSchema.kind === SchemaKind.RECORD;
|
|
60
|
+
|
|
61
|
+
let unifiedElementSchema: Schema;
|
|
62
|
+
|
|
63
|
+
// 3. DETERMINE ELEMENT SCHEMA
|
|
64
|
+
if (isListOfRecords) {
|
|
65
|
+
// RECORDS: Create a unified schema containing ALL fields from ALL items.
|
|
66
|
+
unifiedElementSchema = new Schema(SchemaKind.RECORD, { typeName: 'record' });
|
|
67
|
+
const seenFields = new Set<string>();
|
|
68
|
+
|
|
69
|
+
for (const item of parsedItems) {
|
|
70
|
+
// Skip non-record items if mixed list (or handle as error depending on strictness)
|
|
71
|
+
if (item.schema.kind === SchemaKind.RECORD) {
|
|
72
|
+
for (const field of item.schema.fields) {
|
|
73
|
+
if (!seenFields.has(field.name)) {
|
|
74
|
+
unifiedElementSchema.addField(field);
|
|
75
|
+
seenFields.add(field.name);
|
|
76
|
+
}
|
|
79
77
|
}
|
|
80
|
-
|
|
81
|
-
// PRIMITIVES: Simply take the schema of the first element.
|
|
82
|
-
// We assume the list is homogeneous based on the first item.
|
|
83
|
-
unifiedElementSchema = firstSchema;
|
|
78
|
+
}
|
|
84
79
|
}
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
} else {
|
|
81
|
+
// PRIMITIVES: Simply take the schema of the first element.
|
|
82
|
+
// We assume the list is homogeneous based on the first item.
|
|
83
|
+
unifiedElementSchema = firstSchema;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 4. FINALIZE
|
|
87
|
+
const listSchema = new Schema(SchemaKind.LIST, {
|
|
88
|
+
typeName: 'list',
|
|
89
|
+
element: unifiedElementSchema,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return new Node(listSchema, { elements: parsedItems });
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// --------------------------------------------------------------
|
|
96
96
|
// Dict -> Node(record)
|
|
97
97
|
// --------------------------------------------------------------
|
|
98
98
|
|
|
99
|
-
function parseDict(obj: Record<string,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
function parseDict(obj: Record<string, unknown>): Node {
|
|
100
|
+
/**
|
|
101
|
+
* JSON objects -> named records.
|
|
102
|
+
*/
|
|
103
|
+
const fieldsData: Record<string, Node> = {};
|
|
104
|
+
const schema = new Schema(SchemaKind.RECORD);
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
for (const [key, rawValue] of Object.entries(obj)) {
|
|
107
|
+
const childNode = parseDataToNode(rawValue);
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
// Assign field name to the child's schema so it knows it is a field
|
|
110
|
+
childNode.schema.name = key;
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
fieldsData[key] = childNode;
|
|
113
|
+
schema.addField(childNode.schema);
|
|
114
|
+
}
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
return new Node(schema, { fields: fieldsData });
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// --------------------------------------------------------------
|
|
120
120
|
// Main entrypoint
|
|
121
121
|
// --------------------------------------------------------------
|
|
122
122
|
|
|
123
|
-
export function parseDataToNode(value:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
123
|
+
export function parseDataToNode(value: Node | unknown): Node {
|
|
124
|
+
if (value instanceof Node) {
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 1. Primitive Check
|
|
129
|
+
if (
|
|
130
|
+
value === null ||
|
|
131
|
+
value === undefined ||
|
|
132
|
+
typeof value === 'string' ||
|
|
133
|
+
typeof value === 'number' ||
|
|
134
|
+
typeof value === 'boolean'
|
|
135
|
+
) {
|
|
136
|
+
return parsePrimitive(value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 2. List Check
|
|
140
|
+
if (Array.isArray(value)) {
|
|
141
|
+
return parseList(value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 3. Object Check (Dict)
|
|
145
|
+
if (typeof value === 'object') {
|
|
146
|
+
return parseDict(value as Record<string, unknown>);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
throw new EncodingError(`Unsupported structure type: ${typeof value}`);
|
|
150
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Encoder } from './core/Encoder';
|
|
1
|
+
import { DEFAULT_CONFIG, EncoderConfig } from './config';
|
|
3
2
|
import { Decoder, DecodeResult } from './core/Decoder';
|
|
3
|
+
import { Encoder } from './core/Encoder';
|
|
4
4
|
import { parseDataToNode } from './core/Parser';
|
|
5
|
+
import { Meta, MetaInfo, MetaProps } from './models/Meta';
|
|
5
6
|
import { Node } from './models/Node';
|
|
6
7
|
import { Schema, SchemaKind } from './models/Schema';
|
|
7
|
-
import { MetaInfo, Meta, MetaProps } from './models/Meta';
|
|
8
8
|
|
|
9
9
|
// Re-export types and classes
|
|
10
|
-
export {
|
|
11
|
-
Meta, MetaInfo, MetaProps };
|
|
10
|
+
export { DecodeResult, EncoderConfig, Meta, MetaInfo, MetaProps, Node, Schema, SchemaKind };
|
|
12
11
|
|
|
13
12
|
// =============================================================
|
|
14
13
|
// PUBLIC API
|
|
@@ -17,30 +16,29 @@ export { Node, Schema, SchemaKind, DecodeResult, EncoderConfig,
|
|
|
17
16
|
/**
|
|
18
17
|
* Encode input data into valid **AK Data** format.
|
|
19
18
|
*/
|
|
20
|
-
export function encode(data:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return encoder.encode(node);
|
|
19
|
+
export function encode(data: Node | unknown, config: Partial<EncoderConfig> = {}): string {
|
|
20
|
+
const node = parseDataToNode(data);
|
|
21
|
+
const finalConfig: EncoderConfig = { ...DEFAULT_CONFIG, ...config };
|
|
22
|
+
const encoder = new Encoder(finalConfig);
|
|
23
|
+
return encoder.encode(node);
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
27
|
* Decode AK Data format text into a Node structure.
|
|
30
28
|
*/
|
|
31
29
|
export function decode(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
text: string,
|
|
31
|
+
options: { removeAnsiColors?: boolean; debug?: boolean } = {},
|
|
32
|
+
schema: string = '',
|
|
35
33
|
): DecodeResult {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const { removeAnsiColors = false, debug = false } = options;
|
|
35
|
+
const decoder = new Decoder(text, schema, removeAnsiColors, debug);
|
|
36
|
+
return decoder.decode();
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
/**
|
|
42
40
|
* Convert raw JavaScript object/value into a Node.
|
|
43
41
|
*/
|
|
44
|
-
export function parse(data:
|
|
45
|
-
|
|
46
|
-
}
|
|
42
|
+
export function parse(data: unknown): Node {
|
|
43
|
+
return parseDataToNode(data);
|
|
44
|
+
}
|
package/src/models/Meta.ts
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
+
import { Primitive } from './Node';
|
|
1
2
|
|
|
2
3
|
export interface MetaProps {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
comments?: string[];
|
|
5
|
+
attr?: Record<string, Primitive>;
|
|
6
|
+
tags?: string[];
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Mixin/Base class that adds metadata storage capabilities to Node and Schema.
|
|
10
11
|
*/
|
|
11
12
|
export class Meta {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
comments: string[];
|
|
14
|
+
attr: Record<string, Primitive>;
|
|
15
|
+
tags: string[];
|
|
16
|
+
|
|
17
|
+
constructor({ comments, attr, tags }: MetaProps = {}) {
|
|
18
|
+
this.comments = comments ? [...comments] : [];
|
|
19
|
+
this.attr = attr ? { ...attr } : {};
|
|
20
|
+
this.tags = tags ? [...tags] : [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Clears ALL metadata (comments, attributes, tags).
|
|
25
|
+
*/
|
|
26
|
+
clearCommonMeta(): void {
|
|
27
|
+
this.comments = [];
|
|
28
|
+
this.attr = {};
|
|
29
|
+
this.tags = [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Merges only the common fields (attributes, comments, tags)
|
|
34
|
+
* from a MetaInfo object. Safe for both Node and Schema.
|
|
35
|
+
*/
|
|
36
|
+
applyCommonMeta(info: Meta): void {
|
|
37
|
+
// Append comments
|
|
38
|
+
if (info.comments.length > 0) {
|
|
39
|
+
this.comments.push(...info.comments);
|
|
20
40
|
}
|
|
21
41
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
clearCommonMeta(): void {
|
|
26
|
-
this.comments = [];
|
|
27
|
-
this.attr = {};
|
|
28
|
-
this.tags = [];
|
|
42
|
+
// Merge attributes ($key=value)
|
|
43
|
+
if (Object.keys(info.attr).length > 0) {
|
|
44
|
+
Object.assign(this.attr, info.attr);
|
|
29
45
|
}
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*/
|
|
35
|
-
applyCommonMeta(info: Meta): void {
|
|
36
|
-
// Append comments
|
|
37
|
-
if (info.comments.length > 0) {
|
|
38
|
-
this.comments.push(...info.comments);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Merge attributes ($key=value)
|
|
42
|
-
if (Object.keys(info.attr).length > 0) {
|
|
43
|
-
Object.assign(this.attr, info.attr);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Append tags
|
|
47
|
-
if (info.tags.length > 0) {
|
|
48
|
-
this.tags.push(...info.tags);
|
|
49
|
-
}
|
|
47
|
+
// Append tags
|
|
48
|
+
if (info.tags.length > 0) {
|
|
49
|
+
this.tags.push(...info.tags);
|
|
50
50
|
}
|
|
51
|
+
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -55,81 +56,80 @@ export class Meta {
|
|
|
55
56
|
* It contains BOTH Schema constraints (!required) and Node attributes ($key=val).
|
|
56
57
|
*/
|
|
57
58
|
export class MetaInfo extends Meta {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
required: boolean;
|
|
60
|
+
|
|
61
|
+
constructor(props: MetaProps & { required?: boolean } = {}) {
|
|
62
|
+
super(props);
|
|
63
|
+
this.required = props.required || false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Merges everything from another MetaInfo object, including 'required' flag.
|
|
68
|
+
*/
|
|
69
|
+
applyMeta(info: MetaInfo): void {
|
|
70
|
+
// Apply common fields (comments, attr, tags)
|
|
71
|
+
this.applyCommonMeta(info);
|
|
72
|
+
|
|
73
|
+
// Override required meta (Schema Only)
|
|
74
|
+
if (info.required) {
|
|
75
|
+
this.required = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Allows usage checks like 'if meta.isEmpty()' to see if any metadata was collected.
|
|
81
|
+
* Equivalent to Python's __bool__.
|
|
82
|
+
*/
|
|
83
|
+
isEmpty(): boolean {
|
|
84
|
+
return (
|
|
85
|
+
this.comments.length === 0 &&
|
|
86
|
+
Object.keys(this.attr).length === 0 &&
|
|
87
|
+
this.tags.length === 0 &&
|
|
88
|
+
!this.required
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Debug representation mimicking the actual ADF format style.
|
|
94
|
+
* Example: <MetaInfo !required #tag $key=val >
|
|
95
|
+
*/
|
|
96
|
+
toString(): string {
|
|
97
|
+
const parts: string[] = [];
|
|
98
|
+
|
|
99
|
+
// 1. Flags
|
|
100
|
+
if (this.required) {
|
|
101
|
+
parts.push('!required');
|
|
63
102
|
}
|
|
64
103
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
applyMeta(info: MetaInfo): void {
|
|
69
|
-
// Apply common fields (comments, attr, tags)
|
|
70
|
-
this.applyCommonMeta(info);
|
|
71
|
-
|
|
72
|
-
// Override required meta (Schema Only)
|
|
73
|
-
if (info.required) {
|
|
74
|
-
this.required = true;
|
|
75
|
-
}
|
|
104
|
+
// 2. Tags
|
|
105
|
+
for (const t of this.tags) {
|
|
106
|
+
parts.push(`#${t}`);
|
|
76
107
|
}
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
);
|
|
109
|
+
// 3. Attributes
|
|
110
|
+
for (const [k, v] of Object.entries(this.attr)) {
|
|
111
|
+
// Simplistic value repr for debug
|
|
112
|
+
let valStr: string;
|
|
113
|
+
if (typeof v === 'string') {
|
|
114
|
+
valStr = `"${v}"`;
|
|
115
|
+
} else {
|
|
116
|
+
valStr = String(v);
|
|
117
|
+
}
|
|
118
|
+
parts.push(`$${k}=${valStr}`);
|
|
89
119
|
}
|
|
90
120
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
parts.push("!required");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 2. Tags
|
|
104
|
-
for (const t of this.tags) {
|
|
105
|
-
parts.push(`#${t}`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 3. Attributes
|
|
109
|
-
for (const [k, v] of Object.entries(this.attr)) {
|
|
110
|
-
// Simplistic value repr for debug
|
|
111
|
-
let valStr: string;
|
|
112
|
-
if (typeof v === 'string') {
|
|
113
|
-
valStr = `"${v}"`;
|
|
114
|
-
} else {
|
|
115
|
-
valStr = String(v);
|
|
116
|
-
}
|
|
117
|
-
parts.push(`$${k}=${valStr}`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 4. Comments (Summary)
|
|
121
|
-
if (this.comments.length > 0) {
|
|
122
|
-
if (this.comments.length === 1) {
|
|
123
|
-
const c = this.comments[0];
|
|
124
|
-
const preview = c.length > 15 ? c.substring(0, 15) + '..' : c;
|
|
125
|
-
parts.push(`/* ${preview} */`);
|
|
126
|
-
} else {
|
|
127
|
-
parts.push(`/* ${this.comments.length} comments */`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const content = parts.join(" ");
|
|
132
|
-
return content ? `<MetaInfo ${content}>` : "<MetaInfo (empty)>";
|
|
121
|
+
// 4. Comments (Summary)
|
|
122
|
+
if (this.comments.length > 0) {
|
|
123
|
+
if (this.comments.length === 1) {
|
|
124
|
+
const c = this.comments[0];
|
|
125
|
+
const preview = c.length > 15 ? c.substring(0, 15) + '..' : c;
|
|
126
|
+
parts.push(`/* ${preview} */`);
|
|
127
|
+
} else {
|
|
128
|
+
parts.push(`/* ${this.comments.length} comments */`);
|
|
129
|
+
}
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
|
|
132
|
+
const content = parts.join(' ');
|
|
133
|
+
return content ? `<MetaInfo ${content}>` : '<MetaInfo (empty)>';
|
|
134
|
+
}
|
|
135
|
+
}
|