@terrazzo/parser 0.10.4 → 2.0.0-alpha.1

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 (43) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/index.d.ts +112 -325
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2194 -3621
  5. package/dist/index.js.map +1 -1
  6. package/package.json +4 -3
  7. package/src/build/index.ts +42 -42
  8. package/src/config.ts +13 -6
  9. package/src/lib/code-frame.ts +3 -0
  10. package/src/lib/momoa.ts +10 -0
  11. package/src/lint/index.ts +41 -37
  12. package/src/lint/plugin-core/index.ts +73 -16
  13. package/src/lint/plugin-core/rules/colorspace.ts +4 -0
  14. package/src/lint/plugin-core/rules/duplicate-values.ts +2 -0
  15. package/src/lint/plugin-core/rules/max-gamut.ts +24 -4
  16. package/src/lint/plugin-core/rules/no-type-on-alias.ts +29 -0
  17. package/src/lint/plugin-core/rules/required-modes.ts +2 -0
  18. package/src/lint/plugin-core/rules/required-typography-properties.ts +13 -3
  19. package/src/lint/plugin-core/rules/valid-boolean.ts +41 -0
  20. package/src/lint/plugin-core/rules/valid-border.ts +57 -0
  21. package/src/lint/plugin-core/rules/valid-color.ts +265 -0
  22. package/src/lint/plugin-core/rules/valid-cubic-bezier.ts +83 -0
  23. package/src/lint/plugin-core/rules/valid-dimension.ts +199 -0
  24. package/src/lint/plugin-core/rules/valid-duration.ts +123 -0
  25. package/src/lint/plugin-core/rules/valid-font-family.ts +68 -0
  26. package/src/lint/plugin-core/rules/valid-font-weight.ts +89 -0
  27. package/src/lint/plugin-core/rules/valid-gradient.ts +79 -0
  28. package/src/lint/plugin-core/rules/valid-link.ts +41 -0
  29. package/src/lint/plugin-core/rules/valid-number.ts +63 -0
  30. package/src/lint/plugin-core/rules/valid-shadow.ts +67 -0
  31. package/src/lint/plugin-core/rules/valid-string.ts +41 -0
  32. package/src/lint/plugin-core/rules/valid-stroke-style.ts +104 -0
  33. package/src/lint/plugin-core/rules/valid-transition.ts +61 -0
  34. package/src/lint/plugin-core/rules/valid-typography.ts +67 -0
  35. package/src/logger.ts +70 -59
  36. package/src/parse/index.ts +23 -328
  37. package/src/parse/load.ts +257 -0
  38. package/src/parse/normalize.ts +134 -170
  39. package/src/parse/token.ts +530 -0
  40. package/src/types.ts +106 -28
  41. package/src/parse/alias.ts +0 -369
  42. package/src/parse/json.ts +0 -211
  43. package/src/parse/validate.ts +0 -961
package/src/parse/json.ts DELETED
@@ -1,211 +0,0 @@
1
- import {
2
- type AnyNode,
3
- type DocumentNode,
4
- type MemberNode,
5
- parse as momoaParse,
6
- type ObjectNode,
7
- type ParseOptions,
8
- print,
9
- type ValueNode,
10
- } from '@humanwhocodes/momoa';
11
- import type yamlToMomoa from 'yaml-to-momoa';
12
- import type Logger from '../logger.js';
13
- import type { InputSource } from '../types.js';
14
-
15
- export interface JSONVisitor {
16
- enter?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
17
- exit?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
18
- }
19
-
20
- export const CHILD_KEYS = {
21
- Document: ['body'] as const,
22
- Object: ['members'] as const,
23
- Member: ['name', 'value'] as const,
24
- Element: ['value'] as const,
25
- Array: ['elements'] as const,
26
- String: [] as const,
27
- Number: [] as const,
28
- Boolean: [] as const,
29
- Null: [] as const,
30
- Identifier: [] as const,
31
- NaN: [] as const,
32
- Infinity: [] as const,
33
- };
34
-
35
- /** Determines if a given value is an AST node. */
36
- export function isNode(value: unknown): boolean {
37
- return !!value && typeof value === 'object' && 'type' in value && typeof value.type === 'string';
38
- }
39
-
40
- export type ValueNodeWithIndex = ValueNode & { index: number };
41
-
42
- /** Get ObjectNode members as object */
43
- export function getObjMembers(node: ObjectNode): Record<string | number, ValueNodeWithIndex> {
44
- const members: Record<string | number, ValueNodeWithIndex> = {};
45
- if (node.type !== 'Object') {
46
- return members;
47
- }
48
- for (let i = 0; i < node.members.length; i++) {
49
- const m = node.members[i]!;
50
- if (m.name.type !== 'String') {
51
- continue;
52
- }
53
- members[m.name.value] = { ...m.value, index: i };
54
- }
55
- return members;
56
- }
57
-
58
- /** Inject members to ObjectNode */
59
- export function injectObjMembers(node: ObjectNode, members: MemberNode[] = []) {
60
- if (node.type !== 'Object') {
61
- return;
62
- }
63
- node.members.push(...members);
64
- }
65
-
66
- /** Replace an ObjectNode’s contents outright with another */
67
- export function replaceObjMembers(a: ObjectNode, b: DocumentNode | ObjectNode) {
68
- a.members = (b.type === 'Document' && (b.body as ObjectNode)?.members) || (b as ObjectNode).members;
69
- }
70
-
71
- /**
72
- * Variation of Momoa’s traverse(), which keeps track of global path.
73
- * Allows mutation of AST (along with any consequences)
74
- */
75
- export function traverse(root: AnyNode, visitor: JSONVisitor) {
76
- /**
77
- * Recursively visits a node.
78
- * @param {AnyNode} node The node to visit.
79
- * @param {AnyNode} [parent] The parent of the node to visit.
80
- * @return {void}
81
- */
82
- function visitNode(node: AnyNode, parent: AnyNode | undefined, path: string[] = []) {
83
- const nextPath = [...path];
84
- if (node.type === 'Member') {
85
- const { name } = node;
86
- nextPath.push('value' in name ? name.value : String(name));
87
- }
88
-
89
- visitor.enter?.(node, parent, nextPath);
90
-
91
- const childNode = CHILD_KEYS[node.type];
92
- for (const key of childNode ?? []) {
93
- const value = node[key as keyof typeof node];
94
- if (!value) {
95
- continue;
96
- }
97
- if (Array.isArray(value)) {
98
- for (let i = 0; i < value.length; i++) {
99
- visitNode(value[i] as unknown as AnyNode, node, key === 'elements' ? [...nextPath, String(i)] : nextPath);
100
- }
101
- } else if (isNode(value)) {
102
- visitNode(value as unknown as AnyNode, node, nextPath);
103
- }
104
- }
105
-
106
- visitor.exit?.(node, parent, nextPath);
107
- }
108
-
109
- visitNode(root, undefined, []);
110
- }
111
-
112
- /** Determine if an input is likely a JSON string */
113
- export function maybeRawJSON(input: string): boolean {
114
- return typeof input === 'string' && input.trim().startsWith('{');
115
- }
116
-
117
- /** Find Momoa node by traversing paths */
118
- export function findNode<T = AnyNode>(node: AnyNode, path: string[]): T | undefined {
119
- if (!path.length) {
120
- return;
121
- }
122
-
123
- let nextNode: AnyNode | undefined;
124
-
125
- switch (node.type) {
126
- // for Document nodes, dive into body for “free” (not part of the path)
127
- case 'Document': {
128
- return findNode(node.body, path);
129
- }
130
- case 'Object': {
131
- const [member, ...rest] = path;
132
- nextNode = node.members.find((m) => m.name.type === 'String' && m.name.value === member)?.value;
133
- if (nextNode && rest.length) {
134
- return findNode(nextNode, path.slice(1));
135
- }
136
- break;
137
- }
138
- case 'Array': {
139
- const [_index, ...rest] = path;
140
- const index = Number.parseInt(_index!, 10);
141
- nextNode = node.elements[index]?.value;
142
- if (nextNode && rest.length) {
143
- return findNode(nextNode, path.slice(1));
144
- }
145
- break;
146
- }
147
- }
148
-
149
- return nextNode as T;
150
- }
151
-
152
- export interface ToMomoaOptions {
153
- filename?: URL;
154
- continueOnError?: boolean;
155
- logger: Logger;
156
- yamlToMomoa?: typeof yamlToMomoa;
157
- }
158
-
159
- export function toMomoa(
160
- input: string | Record<string, any>,
161
- { continueOnError, filename, logger, yamlToMomoa }: ToMomoaOptions,
162
- ): InputSource {
163
- let src = '';
164
- if (typeof input === 'string') {
165
- src = input;
166
- }
167
- let document = {} as DocumentNode;
168
- if (typeof input === 'string' && !maybeRawJSON(input)) {
169
- if (yamlToMomoa) {
170
- try {
171
- document = yamlToMomoa(input); // if string, but not JSON, attempt YAML
172
- } catch (err) {
173
- logger.error({ group: 'parser', label: 'json', message: String(err), filename, src: input, continueOnError });
174
- }
175
- } else {
176
- logger.error({
177
- group: 'parser',
178
- label: 'yaml',
179
- message: `Install \`yaml-to-momoa\` package to parse YAML, and pass in as option, e.g.:
180
-
181
- import { parse } from '@terrazzo/parser';
182
- import yamlToMomoa from 'yaml-to-momoa';
183
-
184
- parse(yamlString, { yamlToMomoa });`,
185
- continueOnError: false, // fail here; no point in continuing
186
- });
187
- }
188
- } else {
189
- document = parseJSON(input);
190
- }
191
- if (!src) {
192
- src = print(document, { indent: 2 });
193
- }
194
- return { src, document };
195
- }
196
-
197
- /** Momoa, just with default options pre-set */
198
- export function parseJSON(input: string | Record<string, any>, options?: ParseOptions): any {
199
- return momoaParse(
200
- // note: it seems silly, at first glance, to have JSON.stringify() inside an actual JSON parser. But
201
- // this provides a common interface to generate a Momoa AST for JSON created in-memory, which we already
202
- // know is 100% valid because it’s already deserialized.
203
- typeof input === 'string' ? input : JSON.stringify(input, undefined, 2),
204
- {
205
- mode: 'jsonc',
206
- ranges: true,
207
- tokens: true,
208
- ...options,
209
- },
210
- );
211
- }