@antv/infographic 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/infographic.min.js +110 -105
  2. package/dist/infographic.min.js.map +1 -1
  3. package/esm/constants/element.d.ts +1 -1
  4. package/esm/constants/index.d.ts +1 -0
  5. package/esm/constants/index.js +1 -0
  6. package/esm/constants/service.d.ts +1 -0
  7. package/esm/constants/service.js +1 -0
  8. package/esm/designs/components/Illus.js +1 -1
  9. package/esm/designs/structures/chart-wordcloud.d.ts +11 -0
  10. package/esm/designs/structures/chart-wordcloud.js +156 -0
  11. package/esm/designs/structures/hierarchy-tree.d.ts +2 -0
  12. package/esm/designs/structures/hierarchy-tree.js +179 -50
  13. package/esm/designs/structures/index.d.ts +2 -0
  14. package/esm/designs/structures/index.js +2 -0
  15. package/esm/designs/structures/sequence-stairs-front.d.ts +8 -0
  16. package/esm/designs/structures/sequence-stairs-front.js +116 -0
  17. package/esm/designs/types.d.ts +8 -0
  18. package/esm/editor/managers/state.js +1 -1
  19. package/esm/index.d.ts +2 -0
  20. package/esm/index.js +1 -0
  21. package/esm/options/parser.d.ts +1 -1
  22. package/esm/options/parser.js +33 -15
  23. package/esm/renderer/composites/icon.js +1 -1
  24. package/esm/renderer/composites/illus.js +1 -1
  25. package/esm/resource/loader.d.ts +2 -2
  26. package/esm/resource/loader.js +22 -11
  27. package/esm/resource/loaders/index.d.ts +1 -0
  28. package/esm/resource/loaders/index.js +1 -0
  29. package/esm/resource/loaders/remote.d.ts +1 -1
  30. package/esm/resource/loaders/remote.js +10 -2
  31. package/esm/resource/loaders/search.d.ts +1 -0
  32. package/esm/resource/loaders/search.js +51 -0
  33. package/esm/resource/types/index.d.ts +1 -0
  34. package/esm/resource/types/resource.d.ts +8 -1
  35. package/esm/resource/types/scene.d.ts +1 -0
  36. package/esm/resource/utils/data-uri.js +20 -11
  37. package/esm/resource/utils/parser.js +92 -1
  38. package/esm/resource/utils/ref.js +2 -2
  39. package/esm/runtime/Infographic.d.ts +7 -6
  40. package/esm/runtime/Infographic.js +48 -17
  41. package/esm/runtime/utils.d.ts +4 -2
  42. package/esm/runtime/utils.js +33 -13
  43. package/esm/syntax/index.d.ts +3 -0
  44. package/esm/syntax/index.js +101 -0
  45. package/esm/syntax/mapper.d.ts +3 -0
  46. package/esm/syntax/mapper.js +238 -0
  47. package/esm/syntax/parser.d.ts +14 -0
  48. package/esm/syntax/parser.js +142 -0
  49. package/esm/syntax/schema.d.ts +6 -0
  50. package/esm/syntax/schema.js +74 -0
  51. package/esm/syntax/types.d.ts +61 -0
  52. package/esm/syntax/types.js +1 -0
  53. package/esm/templates/built-in.js +4 -0
  54. package/esm/templates/hierarchy-tree.js +25 -11
  55. package/esm/templates/sequence-stairs.d.ts +2 -0
  56. package/esm/templates/sequence-stairs.js +42 -0
  57. package/esm/templates/word-cloud.d.ts +2 -0
  58. package/esm/templates/word-cloud.js +19 -0
  59. package/esm/themes/types.d.ts +1 -1
  60. package/esm/utils/design.d.ts +2 -0
  61. package/esm/utils/design.js +10 -0
  62. package/esm/utils/font.js +11 -1
  63. package/esm/utils/index.d.ts +1 -0
  64. package/esm/utils/index.js +1 -0
  65. package/lib/constants/element.d.ts +1 -1
  66. package/lib/constants/index.d.ts +1 -0
  67. package/lib/constants/index.js +1 -0
  68. package/lib/constants/service.d.ts +1 -0
  69. package/lib/constants/service.js +4 -0
  70. package/lib/designs/components/Illus.js +1 -1
  71. package/lib/designs/structures/chart-wordcloud.d.ts +11 -0
  72. package/lib/designs/structures/chart-wordcloud.js +160 -0
  73. package/lib/designs/structures/hierarchy-tree.d.ts +2 -0
  74. package/lib/designs/structures/hierarchy-tree.js +179 -50
  75. package/lib/designs/structures/index.d.ts +2 -0
  76. package/lib/designs/structures/index.js +2 -0
  77. package/lib/designs/structures/sequence-stairs-front.d.ts +8 -0
  78. package/lib/designs/structures/sequence-stairs-front.js +120 -0
  79. package/lib/designs/types.d.ts +8 -0
  80. package/lib/editor/managers/state.js +1 -1
  81. package/lib/index.d.ts +2 -0
  82. package/lib/index.js +4 -1
  83. package/lib/options/parser.d.ts +1 -1
  84. package/lib/options/parser.js +32 -14
  85. package/lib/renderer/composites/icon.js +1 -1
  86. package/lib/renderer/composites/illus.js +1 -1
  87. package/lib/resource/loader.d.ts +2 -2
  88. package/lib/resource/loader.js +21 -10
  89. package/lib/resource/loaders/index.d.ts +1 -0
  90. package/lib/resource/loaders/index.js +1 -0
  91. package/lib/resource/loaders/remote.d.ts +1 -1
  92. package/lib/resource/loaders/remote.js +10 -2
  93. package/lib/resource/loaders/search.d.ts +1 -0
  94. package/lib/resource/loaders/search.js +54 -0
  95. package/lib/resource/types/index.d.ts +1 -0
  96. package/lib/resource/types/resource.d.ts +8 -1
  97. package/lib/resource/types/scene.d.ts +1 -0
  98. package/lib/resource/utils/data-uri.js +20 -11
  99. package/lib/resource/utils/parser.js +92 -1
  100. package/lib/resource/utils/ref.js +2 -2
  101. package/lib/runtime/Infographic.d.ts +7 -6
  102. package/lib/runtime/Infographic.js +47 -16
  103. package/lib/runtime/utils.d.ts +4 -2
  104. package/lib/runtime/utils.js +35 -13
  105. package/lib/syntax/index.d.ts +3 -0
  106. package/lib/syntax/index.js +104 -0
  107. package/lib/syntax/mapper.d.ts +3 -0
  108. package/lib/syntax/mapper.js +242 -0
  109. package/lib/syntax/parser.d.ts +14 -0
  110. package/lib/syntax/parser.js +146 -0
  111. package/lib/syntax/schema.d.ts +6 -0
  112. package/lib/syntax/schema.js +77 -0
  113. package/lib/syntax/types.d.ts +61 -0
  114. package/lib/syntax/types.js +2 -0
  115. package/lib/templates/built-in.js +4 -0
  116. package/lib/templates/hierarchy-tree.js +25 -11
  117. package/lib/templates/sequence-stairs.d.ts +2 -0
  118. package/lib/templates/sequence-stairs.js +45 -0
  119. package/lib/templates/word-cloud.d.ts +2 -0
  120. package/lib/templates/word-cloud.js +22 -0
  121. package/lib/themes/types.d.ts +1 -1
  122. package/lib/utils/design.d.ts +2 -0
  123. package/lib/utils/design.js +13 -0
  124. package/lib/utils/font.js +11 -1
  125. package/lib/utils/index.d.ts +1 -0
  126. package/lib/utils/index.js +1 -0
  127. package/package.json +1 -1
  128. package/src/constants/element.ts +1 -1
  129. package/src/constants/index.ts +1 -0
  130. package/src/constants/service.ts +1 -0
  131. package/src/designs/components/Illus.tsx +1 -1
  132. package/src/designs/structures/chart-wordcloud.tsx +278 -0
  133. package/src/designs/structures/hierarchy-tree.tsx +212 -59
  134. package/src/designs/structures/index.ts +2 -0
  135. package/src/designs/structures/sequence-stairs-front.tsx +291 -0
  136. package/src/designs/types.ts +9 -0
  137. package/src/editor/managers/state.ts +1 -1
  138. package/src/index.ts +2 -0
  139. package/src/options/parser.ts +57 -28
  140. package/src/renderer/composites/icon.ts +1 -1
  141. package/src/renderer/composites/illus.ts +1 -1
  142. package/src/resource/loader.ts +22 -8
  143. package/src/resource/loaders/index.ts +1 -0
  144. package/src/resource/loaders/remote.ts +9 -2
  145. package/src/resource/loaders/search.ts +52 -0
  146. package/src/resource/types/index.ts +2 -1
  147. package/src/resource/types/resource.ts +12 -1
  148. package/src/resource/types/scene.ts +1 -0
  149. package/src/resource/utils/data-uri.ts +20 -11
  150. package/src/resource/utils/parser.ts +103 -2
  151. package/src/resource/utils/ref.ts +2 -2
  152. package/src/runtime/Infographic.tsx +74 -22
  153. package/src/runtime/utils.ts +38 -16
  154. package/src/syntax/index.ts +124 -0
  155. package/src/syntax/mapper.ts +362 -0
  156. package/src/syntax/parser.ts +171 -0
  157. package/src/syntax/schema.ts +98 -0
  158. package/src/syntax/types.ts +89 -0
  159. package/src/templates/built-in.ts +4 -0
  160. package/src/templates/hierarchy-tree.ts +34 -11
  161. package/src/templates/sequence-stairs.ts +44 -0
  162. package/src/templates/word-cloud.ts +21 -0
  163. package/src/themes/types.ts +1 -1
  164. package/src/utils/design.ts +14 -0
  165. package/src/utils/font.ts +11 -1
  166. package/src/utils/index.ts +1 -0
  167. package/esm/resource/types/font.d.ts +0 -12
  168. package/lib/resource/types/font.d.ts +0 -12
  169. package/src/resource/types/font.ts +0 -23
  170. /package/esm/resource/types/{font.js → scene.js} +0 -0
  171. /package/lib/resource/types/{font.js → scene.js} +0 -0
@@ -1,40 +1,55 @@
1
1
  import { jsx as _jsx } from "@antv/infographic/jsx-runtime";
2
2
  import EventEmitter from 'eventemitter3';
3
- import { cloneDeep } from 'lodash-es';
4
3
  import { Editor } from '../editor';
5
4
  import { exportToPNGString, exportToSVGString, } from '../exporter';
6
5
  import { renderSVG } from '../jsx';
7
6
  import { parseOptions, } from '../options';
8
7
  import { Renderer } from '../renderer';
8
+ import { parseSyntax } from '../syntax';
9
9
  import { getTypes, parseSVG } from '../utils';
10
10
  import { DEFAULT_OPTIONS } from './options';
11
- import { mergeOptions } from './utils';
11
+ import { cloneOptions, isCompleteParsedInfographicOptions, mergeOptions, } from './utils';
12
12
  export class Infographic {
13
13
  constructor(options) {
14
14
  this.rendered = false;
15
15
  this.emitter = new EventEmitter();
16
16
  this.node = null;
17
- this.options = {
18
- ...options,
19
- data: cloneDeep(options.data),
20
- elements: cloneDeep(options.elements || []),
21
- };
22
- this.parsedOptions = parseOptions(mergeOptions(DEFAULT_OPTIONS, this.options));
17
+ this.setOptions(options);
23
18
  }
24
19
  getOptions() {
25
20
  return this.options;
26
21
  }
22
+ setOptions(options) {
23
+ const { options: parsedOptions, errors, warnings, } = parseSyntaxOptions(options);
24
+ this.options = mergeOptions(this.options || {}, parsedOptions);
25
+ this.parsedOptions = parseOptions(mergeOptions(DEFAULT_OPTIONS, this.options));
26
+ if (warnings.length) {
27
+ this.emitter.emit('warning', warnings);
28
+ }
29
+ if (errors.length) {
30
+ this.emitter.emit('error', errors);
31
+ }
32
+ }
27
33
  /**
28
34
  * Render the infographic into the container
29
35
  */
30
- render() {
36
+ render(options) {
37
+ if (options)
38
+ this.setOptions(options);
39
+ const parsedOptions = this.parsedOptions;
40
+ if (!isCompleteParsedInfographicOptions(parsedOptions)) {
41
+ this.emitter.emit('error', new Error('Incomplete options'));
42
+ return;
43
+ }
31
44
  const { container } = this.parsedOptions;
32
- const template = this.compose();
33
- const renderer = new Renderer(this.parsedOptions, template);
45
+ const template = this.compose(parsedOptions);
46
+ const renderer = new Renderer(parsedOptions, template);
34
47
  this.node = renderer.render();
35
- container.replaceChildren(this.node);
48
+ container?.replaceChildren(this.node);
49
+ this.editor?.destroy();
50
+ this.editor = undefined;
36
51
  if (this.options.editable) {
37
- this.editor = new Editor(this.emitter, this.node, this.parsedOptions);
52
+ this.editor = new Editor(this.emitter, this.node, parsedOptions);
38
53
  }
39
54
  this.rendered = true;
40
55
  this.emitter.emit('rendered', { node: this.node, options: this.options });
@@ -42,14 +57,14 @@ export class Infographic {
42
57
  /**
43
58
  * Compose the SVG template
44
59
  */
45
- compose() {
46
- const { design, data } = this.parsedOptions;
60
+ compose(parsedOptions) {
61
+ const { design, data } = parsedOptions;
47
62
  const { title, item, items, structure } = design;
48
63
  const { component: Structure, props: structureProps } = structure;
49
64
  const Title = title.component;
50
65
  const Item = item.component;
51
66
  const Items = items.map((it) => it.component);
52
- const svg = renderSVG(_jsx(Structure, { data: data, Title: Title, Item: Item, Items: Items, options: this.parsedOptions, ...structureProps }));
67
+ const svg = renderSVG(_jsx(Structure, { data: data, Title: Title, Item: Item, Items: Items, options: parsedOptions, ...structureProps }));
53
68
  const template = parseSVG(svg);
54
69
  if (!template) {
55
70
  throw new Error('Failed to parse SVG template');
@@ -57,7 +72,12 @@ export class Infographic {
57
72
  return template;
58
73
  }
59
74
  getTypes() {
60
- const design = this.parsedOptions.design;
75
+ const parsedOptions = this.parsedOptions;
76
+ if (!isCompleteParsedInfographicOptions(parsedOptions)) {
77
+ this.emitter.emit('error', new Error('Incomplete options'));
78
+ return;
79
+ }
80
+ const design = parsedOptions.design;
61
81
  const structure = design.structure.composites || [];
62
82
  const items = design.items.map((it) => it.composites || []);
63
83
  return getTypes({ structure, items });
@@ -92,3 +112,14 @@ export class Infographic {
92
112
  this.emitter.removeAllListeners();
93
113
  }
94
114
  }
115
+ function parseSyntaxOptions(input) {
116
+ if (typeof input === 'string') {
117
+ const { options, errors, warnings } = parseSyntax(input);
118
+ return { options, errors, warnings };
119
+ }
120
+ return {
121
+ options: cloneOptions(input),
122
+ errors: [],
123
+ warnings: [],
124
+ };
125
+ }
@@ -1,2 +1,4 @@
1
- import { InfographicOptions } from '../options/types';
2
- export declare function mergeOptions(object: Partial<InfographicOptions>, source: Partial<InfographicOptions>): InfographicOptions;
1
+ import type { InfographicOptions, ParsedInfographicOptions } from '../options';
2
+ export declare function mergeOptions(object: Partial<InfographicOptions>, source: Partial<InfographicOptions>): Partial<InfographicOptions>;
3
+ export declare function cloneOptions<T extends Partial<InfographicOptions>>(options: T): T;
4
+ export declare function isCompleteParsedInfographicOptions(options: Partial<ParsedInfographicOptions>): options is ParsedInfographicOptions;
@@ -1,18 +1,38 @@
1
+ import { cloneDeep } from 'lodash-es';
2
+ import { isNonNullableParsedDesignsOptions } from '../utils';
1
3
  export function mergeOptions(object, source) {
2
- return {
4
+ const base = {
3
5
  ...object,
4
6
  ...source,
5
- design: {
6
- ...object.design,
7
- ...source.design,
8
- },
9
- themeConfig: {
10
- ...object.themeConfig,
11
- ...source.themeConfig,
12
- },
13
- svg: {
14
- ...object.svg,
15
- ...source.svg,
16
- },
17
7
  };
8
+ if (object.design || source.design) {
9
+ base.design = { ...object.design, ...source.design };
10
+ }
11
+ if (object.themeConfig || source.themeConfig) {
12
+ base.themeConfig = { ...object.themeConfig, ...source.themeConfig };
13
+ }
14
+ if (object.svg || source.svg) {
15
+ base.svg = { ...object.svg, ...source.svg };
16
+ }
17
+ return base;
18
+ }
19
+ export function cloneOptions(options) {
20
+ const cloned = { ...options };
21
+ if (cloned.data)
22
+ cloned.data = cloneDeep(cloned.data);
23
+ if (cloned.elements)
24
+ cloned.elements = cloneDeep(cloned.elements);
25
+ return cloned;
26
+ }
27
+ export function isCompleteParsedInfographicOptions(options) {
28
+ const { design, data } = options;
29
+ if (!design)
30
+ return false;
31
+ if (!isNonNullableParsedDesignsOptions(design))
32
+ return false;
33
+ if (!data)
34
+ return false;
35
+ if (!Array.isArray(data.items) || data.items.length < 1)
36
+ return false;
37
+ return true;
18
38
  }
@@ -0,0 +1,3 @@
1
+ import type { SyntaxParseResult } from './types';
2
+ export declare function parseSyntax(input: string): SyntaxParseResult;
3
+ export type { SyntaxError, SyntaxNode, SyntaxParseResult } from './types';
@@ -0,0 +1,101 @@
1
+ import { mapWithSchema } from './mapper';
2
+ import { parseSyntaxToAst } from './parser';
3
+ import { DataSchema, DesignSchema, RootSchema, TemplateSchema, ThemeSchema, } from './schema';
4
+ function resolveTemplate(node, errors) {
5
+ if (!node)
6
+ return undefined;
7
+ const mapped = mapWithSchema(node, TemplateSchema, 'template', errors);
8
+ if (!mapped)
9
+ return undefined;
10
+ if (typeof mapped === 'string')
11
+ return mapped;
12
+ if (typeof mapped === 'object' && typeof mapped.type === 'string') {
13
+ return mapped.type;
14
+ }
15
+ return undefined;
16
+ }
17
+ export function parseSyntax(input) {
18
+ const { ast, errors } = parseSyntaxToAst(input);
19
+ const warnings = [];
20
+ const options = {};
21
+ const mergedEntries = { ...ast.entries };
22
+ const infographicNode = ast.entries.infographic;
23
+ let templateFromInfographic;
24
+ if (infographicNode && infographicNode.kind === 'object') {
25
+ if (infographicNode.value)
26
+ templateFromInfographic = infographicNode.value;
27
+ Object.entries(infographicNode.entries).forEach(([key, value]) => {
28
+ if (!(key in mergedEntries))
29
+ mergedEntries[key] = value;
30
+ });
31
+ }
32
+ const allowedRootKeys = new Set([
33
+ 'infographic',
34
+ 'template',
35
+ 'design',
36
+ 'data',
37
+ 'theme',
38
+ 'width',
39
+ 'height',
40
+ ]);
41
+ Object.keys(mergedEntries).forEach((key) => {
42
+ if (!allowedRootKeys.has(key)) {
43
+ errors.push({
44
+ path: key,
45
+ line: mergedEntries[key].line,
46
+ code: 'unknown_key',
47
+ message: 'Unknown top-level key.',
48
+ raw: key,
49
+ });
50
+ }
51
+ });
52
+ const templateNode = mergedEntries.template;
53
+ const templateValue = resolveTemplate(templateNode, errors);
54
+ if (templateValue)
55
+ options.template = templateValue;
56
+ if (!options.template && templateFromInfographic) {
57
+ options.template = templateFromInfographic;
58
+ }
59
+ const designNode = mergedEntries.design;
60
+ if (designNode) {
61
+ const design = mapWithSchema(designNode, DesignSchema, 'design', errors);
62
+ if (design)
63
+ options.design = design;
64
+ }
65
+ const dataNode = mergedEntries.data;
66
+ if (dataNode) {
67
+ const data = mapWithSchema(dataNode, DataSchema, 'data', errors);
68
+ if (data)
69
+ options.data = data;
70
+ }
71
+ const themeNode = mergedEntries.theme;
72
+ if (themeNode) {
73
+ const theme = mapWithSchema(themeNode, ThemeSchema, 'theme', errors);
74
+ if (theme && typeof theme === 'object') {
75
+ const { type, ...rest } = theme;
76
+ if (typeof type === 'string' && type)
77
+ options.theme = type;
78
+ if (Object.keys(rest).length > 0) {
79
+ options.themeConfig = rest;
80
+ }
81
+ }
82
+ }
83
+ const widthNode = mergedEntries.width;
84
+ if (widthNode) {
85
+ const width = mapWithSchema(widthNode, RootSchema.fields.width, 'width', errors);
86
+ if (width !== undefined)
87
+ options.width = width;
88
+ }
89
+ const heightNode = mergedEntries.height;
90
+ if (heightNode) {
91
+ const height = mapWithSchema(heightNode, RootSchema.fields.height, 'height', errors);
92
+ if (height !== undefined)
93
+ options.height = height;
94
+ }
95
+ return {
96
+ options,
97
+ errors,
98
+ warnings,
99
+ ast,
100
+ };
101
+ }
@@ -0,0 +1,3 @@
1
+ import type { SchemaNode, SyntaxError, SyntaxNode } from './types';
2
+ export declare function mapWithSchema(node: SyntaxNode, schema: SchemaNode, path: string, errors: SyntaxError[]): any;
3
+ export declare function mapUnknownToObject(node: SyntaxNode): any;
@@ -0,0 +1,238 @@
1
+ import { parseInlineKeyValue } from './parser';
2
+ function createValueNode(value, line) {
3
+ return { kind: 'value', line, value };
4
+ }
5
+ function parseScalar(value) {
6
+ const trimmed = value.trim();
7
+ if (trimmed === 'true')
8
+ return true;
9
+ if (trimmed === 'false')
10
+ return false;
11
+ if (/^-?\d+(\.\d+)?$/.test(trimmed))
12
+ return parseFloat(trimmed);
13
+ return trimmed;
14
+ }
15
+ function readScalar(node) {
16
+ if (node.kind === 'value')
17
+ return node.value;
18
+ if (node.kind === 'object')
19
+ return node.value;
20
+ return undefined;
21
+ }
22
+ function addError(errors, node, path, code, message, raw) {
23
+ errors.push({
24
+ path,
25
+ line: node.line,
26
+ code,
27
+ message,
28
+ raw,
29
+ });
30
+ }
31
+ function splitArrayValue(value, split = 'any') {
32
+ let text = value.trim();
33
+ if (text.startsWith('[') && text.endsWith(']')) {
34
+ text = text.slice(1, -1).trim();
35
+ }
36
+ if (!text)
37
+ return [];
38
+ let parts;
39
+ if (split === 'comma') {
40
+ parts = text.split(',');
41
+ }
42
+ else if (split === 'space') {
43
+ parts = text.split(/\s+/);
44
+ }
45
+ else {
46
+ parts = text.split(/[,\s]+/);
47
+ }
48
+ return parts.map((part) => part.trim()).filter(Boolean);
49
+ }
50
+ function mapUnknown(node) {
51
+ if (node.kind === 'array') {
52
+ return node.items.map((item) => mapUnknown(item));
53
+ }
54
+ if (node.kind === 'value')
55
+ return parseScalar(node.value);
56
+ const hasEntries = Object.keys(node.entries).length > 0;
57
+ if (!hasEntries && node.value !== undefined) {
58
+ return parseScalar(node.value);
59
+ }
60
+ const result = {};
61
+ if (node.value !== undefined) {
62
+ result.value = parseScalar(node.value);
63
+ }
64
+ Object.entries(node.entries).forEach(([key, child]) => {
65
+ result[key] = mapUnknown(child);
66
+ });
67
+ return result;
68
+ }
69
+ function mapUnion(node, schema, path, errors) {
70
+ let bestValue = undefined;
71
+ let bestErrors = null;
72
+ for (const variant of schema.variants) {
73
+ const variantErrors = [];
74
+ const value = mapWithSchema(node, variant, path, variantErrors);
75
+ if (bestErrors === null || variantErrors.length < bestErrors.length) {
76
+ bestErrors = variantErrors;
77
+ bestValue = value;
78
+ }
79
+ }
80
+ if (bestErrors)
81
+ errors.push(...bestErrors);
82
+ return bestValue;
83
+ }
84
+ export function mapWithSchema(node, schema, path, errors) {
85
+ switch (schema.kind) {
86
+ case 'union':
87
+ return mapUnion(node, schema, path, errors);
88
+ case 'string': {
89
+ const value = readScalar(node);
90
+ if (value === undefined) {
91
+ addError(errors, node, path, 'schema_mismatch', 'Expected string value.');
92
+ return undefined;
93
+ }
94
+ return value;
95
+ }
96
+ case 'number': {
97
+ const value = readScalar(node);
98
+ if (value === undefined) {
99
+ addError(errors, node, path, 'schema_mismatch', 'Expected number value.');
100
+ return undefined;
101
+ }
102
+ const trimmed = value.trim();
103
+ const match = trimmed.match(/^(-?\d+(\.\d+)?)(?:\s*(#|\/\/).*)?$/);
104
+ if (!match) {
105
+ addError(errors, node, path, 'invalid_value', 'Invalid number value.', value);
106
+ return undefined;
107
+ }
108
+ return parseFloat(match[1]);
109
+ }
110
+ case 'boolean': {
111
+ const value = readScalar(node);
112
+ if (value === undefined) {
113
+ addError(errors, node, path, 'schema_mismatch', 'Expected boolean value.');
114
+ return undefined;
115
+ }
116
+ if (value !== 'true' && value !== 'false') {
117
+ addError(errors, node, path, 'invalid_value', 'Invalid boolean value.', value);
118
+ return undefined;
119
+ }
120
+ return value === 'true';
121
+ }
122
+ case 'enum': {
123
+ const value = readScalar(node);
124
+ if (value === undefined) {
125
+ addError(errors, node, path, 'schema_mismatch', 'Expected enum value.');
126
+ return undefined;
127
+ }
128
+ if (!schema.values.includes(value)) {
129
+ addError(errors, node, path, 'invalid_value', 'Invalid enum value.', value);
130
+ return undefined;
131
+ }
132
+ return value;
133
+ }
134
+ case 'array': {
135
+ if (node.kind === 'array') {
136
+ return node.items
137
+ .map((item, index) => mapWithSchema(item, schema.item, `${path}[${index}]`, errors))
138
+ .filter((value) => value !== undefined);
139
+ }
140
+ if (node.kind === 'object' && Object.keys(node.entries).length > 0) {
141
+ addError(errors, node, path, 'schema_mismatch', 'Expected array value.');
142
+ return undefined;
143
+ }
144
+ const scalar = readScalar(node);
145
+ if (scalar === undefined) {
146
+ addError(errors, node, path, 'schema_mismatch', 'Expected array value.');
147
+ return undefined;
148
+ }
149
+ const trimmed = scalar.trim();
150
+ if (trimmed.startsWith('[') && !trimmed.endsWith(']')) {
151
+ return undefined;
152
+ }
153
+ const parts = splitArrayValue(scalar, schema.split);
154
+ return parts
155
+ .map((part, index) => mapWithSchema(createValueNode(part, node.line), schema.item, `${path}[${index}]`, errors))
156
+ .filter((value) => value !== undefined);
157
+ }
158
+ case 'object': {
159
+ if (node.kind === 'array') {
160
+ addError(errors, node, path, 'schema_mismatch', 'Expected object value.');
161
+ return undefined;
162
+ }
163
+ const result = {};
164
+ if (node.kind === 'value') {
165
+ if (schema.shorthandKey) {
166
+ result[schema.shorthandKey] = node.value;
167
+ return result;
168
+ }
169
+ const inline = parseInlineKeyValue(node.value);
170
+ if (inline?.value !== undefined) {
171
+ if (schema.fields[inline.key]) {
172
+ const value = mapWithSchema(createValueNode(inline.value, node.line), schema.fields[inline.key], `${path}.${inline.key}`, errors);
173
+ if (value !== undefined)
174
+ result[inline.key] = value;
175
+ return result;
176
+ }
177
+ if (schema.allowUnknown) {
178
+ result[inline.key] = parseScalar(inline.value);
179
+ return result;
180
+ }
181
+ addError(errors, node, `${path}.${inline.key}`, 'unknown_key', 'Unknown key in object.', inline.key);
182
+ return result;
183
+ }
184
+ if (schema.allowUnknown) {
185
+ result.value = parseScalar(node.value);
186
+ return result;
187
+ }
188
+ addError(errors, node, path, 'invalid_value', 'Expected object value.');
189
+ return undefined;
190
+ }
191
+ if (node.value !== undefined) {
192
+ if (schema.shorthandKey && result[schema.shorthandKey] === undefined) {
193
+ result[schema.shorthandKey] = node.value;
194
+ }
195
+ else {
196
+ const inline = parseInlineKeyValue(node.value);
197
+ if (inline?.value !== undefined) {
198
+ if (schema.fields[inline.key]) {
199
+ const value = mapWithSchema(createValueNode(inline.value, node.line), schema.fields[inline.key], `${path}.${inline.key}`, errors);
200
+ if (value !== undefined && result[inline.key] === undefined) {
201
+ result[inline.key] = value;
202
+ }
203
+ }
204
+ else if (schema.allowUnknown) {
205
+ result[inline.key] = parseScalar(inline.value);
206
+ }
207
+ else {
208
+ addError(errors, node, `${path}.${inline.key}`, 'unknown_key', 'Unknown key in object.', inline.key);
209
+ }
210
+ }
211
+ else if (schema.allowUnknown) {
212
+ result.value = parseScalar(node.value);
213
+ }
214
+ }
215
+ }
216
+ Object.entries(node.entries).forEach(([key, child]) => {
217
+ const fieldSchema = schema.fields[key];
218
+ if (!fieldSchema) {
219
+ if (schema.allowUnknown) {
220
+ result[key] = mapUnknown(child);
221
+ return;
222
+ }
223
+ addError(errors, child, `${path}.${key}`, 'unknown_key', 'Unknown key in object.', key);
224
+ return;
225
+ }
226
+ const value = mapWithSchema(child, fieldSchema, `${path}.${key}`, errors);
227
+ if (value !== undefined)
228
+ result[key] = value;
229
+ });
230
+ return result;
231
+ }
232
+ default:
233
+ return undefined;
234
+ }
235
+ }
236
+ export function mapUnknownToObject(node) {
237
+ return mapUnknown(node);
238
+ }
@@ -0,0 +1,14 @@
1
+ import type { ObjectNode, SyntaxError } from './types';
2
+ interface ParseResult {
3
+ ast: ObjectNode;
4
+ errors: SyntaxError[];
5
+ }
6
+ export declare function parseSyntaxToAst(input: string): ParseResult;
7
+ export declare function parseInlineKeyValue(value: string): {
8
+ key: string;
9
+ value: string;
10
+ } | {
11
+ key: string;
12
+ value: undefined;
13
+ } | null;
14
+ export {};
@@ -0,0 +1,142 @@
1
+ function isWhitespace(char) {
2
+ return char === ' ' || char === '\t';
3
+ }
4
+ function getIndentInfo(line) {
5
+ let indent = 0;
6
+ let index = 0;
7
+ while (index < line.length) {
8
+ const char = line[index];
9
+ if (char === ' ') {
10
+ indent += 1;
11
+ index += 1;
12
+ continue;
13
+ }
14
+ if (char === '\t') {
15
+ indent += 2;
16
+ index += 1;
17
+ continue;
18
+ }
19
+ break;
20
+ }
21
+ return { indent, content: line.slice(index) };
22
+ }
23
+ function stripComments(content) {
24
+ return content.trimEnd();
25
+ }
26
+ function parseKeyValue(raw) {
27
+ const text = raw.trim();
28
+ if (!text)
29
+ return null;
30
+ const match = text.match(/^([^:\s=]+)\s*[:=]\s*(.*)$/);
31
+ if (match) {
32
+ return { key: match[1], value: match[2].trim() };
33
+ }
34
+ const matchSpace = text.match(/^([^\s]+)\s+(.*)$/);
35
+ if (matchSpace) {
36
+ return { key: matchSpace[1], value: matchSpace[2].trim() };
37
+ }
38
+ return { key: text, value: undefined };
39
+ }
40
+ function createObjectNode(line, value) {
41
+ return { kind: 'object', line, value, entries: {} };
42
+ }
43
+ function createArrayNode(line) {
44
+ return { kind: 'array', line, items: [] };
45
+ }
46
+ export function parseSyntaxToAst(input) {
47
+ const errors = [];
48
+ const root = createObjectNode(0);
49
+ const stack = [
50
+ { indent: -1, node: root, parent: null, key: null },
51
+ ];
52
+ const lines = input.split(/\r?\n/);
53
+ lines.forEach((line, index) => {
54
+ const lineNumber = index + 1;
55
+ if (!line.trim())
56
+ return;
57
+ const { indent, content } = getIndentInfo(line);
58
+ const stripped = stripComments(content);
59
+ if (!stripped.trim())
60
+ return;
61
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
62
+ stack.pop();
63
+ }
64
+ const parentFrame = stack[stack.length - 1];
65
+ let parentNode = parentFrame.node;
66
+ const trimmed = stripped.trim();
67
+ if (trimmed.startsWith('-') &&
68
+ (trimmed.length === 1 || isWhitespace(trimmed[1]))) {
69
+ if (parentNode.kind !== 'array') {
70
+ if (parentNode.kind === 'object' &&
71
+ Object.keys(parentNode.entries).length === 0 &&
72
+ parentNode.value === undefined &&
73
+ parentFrame.parent &&
74
+ parentFrame.key) {
75
+ const arrayNode = createArrayNode(parentNode.line);
76
+ if (parentFrame.parent.kind === 'object') {
77
+ parentFrame.parent.entries[parentFrame.key] = arrayNode;
78
+ }
79
+ else if (parentFrame.parent.kind === 'array') {
80
+ const indexInParent = parentFrame.parent.items.indexOf(parentNode);
81
+ if (indexInParent >= 0)
82
+ parentFrame.parent.items[indexInParent] = arrayNode;
83
+ }
84
+ parentFrame.node = arrayNode;
85
+ parentNode = arrayNode;
86
+ }
87
+ else {
88
+ errors.push({
89
+ path: '',
90
+ line: lineNumber,
91
+ code: 'bad_list',
92
+ message: 'List item is not under an array container.',
93
+ raw: trimmed,
94
+ });
95
+ return;
96
+ }
97
+ }
98
+ const itemContent = trimmed.slice(1).trim();
99
+ const itemNode = createObjectNode(lineNumber, itemContent || undefined);
100
+ parentNode.items.push(itemNode);
101
+ stack.push({
102
+ indent,
103
+ node: itemNode,
104
+ parent: parentNode,
105
+ });
106
+ return;
107
+ }
108
+ const parsed = parseKeyValue(trimmed);
109
+ if (!parsed) {
110
+ errors.push({
111
+ path: '',
112
+ line: lineNumber,
113
+ code: 'bad_syntax',
114
+ message: 'Invalid syntax line.',
115
+ raw: trimmed,
116
+ });
117
+ return;
118
+ }
119
+ if (parentNode.kind !== 'object') {
120
+ errors.push({
121
+ path: '',
122
+ line: lineNumber,
123
+ code: 'bad_syntax',
124
+ message: 'Key-value pair is not under an object container.',
125
+ raw: trimmed,
126
+ });
127
+ return;
128
+ }
129
+ const node = createObjectNode(lineNumber, parsed.value);
130
+ parentNode.entries[parsed.key] = node;
131
+ stack.push({
132
+ indent,
133
+ node,
134
+ parent: parentNode,
135
+ key: parsed.key,
136
+ });
137
+ });
138
+ return { ast: root, errors };
139
+ }
140
+ export function parseInlineKeyValue(value) {
141
+ return parseKeyValue(value);
142
+ }
@@ -0,0 +1,6 @@
1
+ import type { ObjectSchema } from './types';
2
+ export declare const ThemeSchema: ObjectSchema;
3
+ export declare const DesignSchema: ObjectSchema;
4
+ export declare const DataSchema: ObjectSchema;
5
+ export declare const TemplateSchema: ObjectSchema;
6
+ export declare const RootSchema: ObjectSchema;