@terrazzo/parser 0.7.2 → 0.8.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/index.d.ts +707 -12
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +4598 -12
  5. package/dist/index.js.map +1 -1
  6. package/package.json +11 -7
  7. package/rolldown.config.ts +24 -0
  8. package/src/build/index.ts +2 -2
  9. package/src/index.ts +76 -1
  10. package/src/parse/index.ts +72 -13
  11. package/src/parse/json.ts +35 -29
  12. package/src/parse/normalize.ts +4 -14
  13. package/src/parse/validate.ts +81 -68
  14. package/dist/build/index.d.ts +0 -20
  15. package/dist/build/index.d.ts.map +0 -1
  16. package/dist/build/index.js +0 -166
  17. package/dist/build/index.js.map +0 -1
  18. package/dist/config.d.ts +0 -8
  19. package/dist/config.d.ts.map +0 -1
  20. package/dist/config.js +0 -290
  21. package/dist/config.js.map +0 -1
  22. package/dist/lib/code-frame.d.ts +0 -31
  23. package/dist/lib/code-frame.d.ts.map +0 -1
  24. package/dist/lib/code-frame.js +0 -108
  25. package/dist/lib/code-frame.js.map +0 -1
  26. package/dist/lint/index.d.ts +0 -12
  27. package/dist/lint/index.d.ts.map +0 -1
  28. package/dist/lint/index.js +0 -105
  29. package/dist/lint/index.js.map +0 -1
  30. package/dist/lint/plugin-core/index.d.ts +0 -13
  31. package/dist/lint/plugin-core/index.d.ts.map +0 -1
  32. package/dist/lint/plugin-core/index.js +0 -40
  33. package/dist/lint/plugin-core/index.js.map +0 -1
  34. package/dist/lint/plugin-core/lib/docs.d.ts +0 -2
  35. package/dist/lint/plugin-core/lib/docs.d.ts.map +0 -1
  36. package/dist/lint/plugin-core/lib/docs.js +0 -4
  37. package/dist/lint/plugin-core/lib/docs.js.map +0 -1
  38. package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +0 -40
  39. package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts.map +0 -1
  40. package/dist/lint/plugin-core/rules/a11y-min-contrast.js +0 -58
  41. package/dist/lint/plugin-core/rules/a11y-min-contrast.js.map +0 -1
  42. package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +0 -14
  43. package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts.map +0 -1
  44. package/dist/lint/plugin-core/rules/a11y-min-font-size.js +0 -45
  45. package/dist/lint/plugin-core/rules/a11y-min-font-size.js.map +0 -1
  46. package/dist/lint/plugin-core/rules/colorspace.d.ts +0 -15
  47. package/dist/lint/plugin-core/rules/colorspace.d.ts.map +0 -1
  48. package/dist/lint/plugin-core/rules/colorspace.js +0 -85
  49. package/dist/lint/plugin-core/rules/colorspace.js.map +0 -1
  50. package/dist/lint/plugin-core/rules/consistent-naming.d.ts +0 -12
  51. package/dist/lint/plugin-core/rules/consistent-naming.d.ts.map +0 -1
  52. package/dist/lint/plugin-core/rules/consistent-naming.js +0 -49
  53. package/dist/lint/plugin-core/rules/consistent-naming.js.map +0 -1
  54. package/dist/lint/plugin-core/rules/descriptions.d.ts +0 -10
  55. package/dist/lint/plugin-core/rules/descriptions.d.ts.map +0 -1
  56. package/dist/lint/plugin-core/rules/descriptions.js +0 -32
  57. package/dist/lint/plugin-core/rules/descriptions.js.map +0 -1
  58. package/dist/lint/plugin-core/rules/duplicate-values.d.ts +0 -10
  59. package/dist/lint/plugin-core/rules/duplicate-values.d.ts.map +0 -1
  60. package/dist/lint/plugin-core/rules/duplicate-values.js +0 -65
  61. package/dist/lint/plugin-core/rules/duplicate-values.js.map +0 -1
  62. package/dist/lint/plugin-core/rules/max-gamut.d.ts +0 -15
  63. package/dist/lint/plugin-core/rules/max-gamut.d.ts.map +0 -1
  64. package/dist/lint/plugin-core/rules/max-gamut.js +0 -101
  65. package/dist/lint/plugin-core/rules/max-gamut.js.map +0 -1
  66. package/dist/lint/plugin-core/rules/required-children.d.ts +0 -19
  67. package/dist/lint/plugin-core/rules/required-children.d.ts.map +0 -1
  68. package/dist/lint/plugin-core/rules/required-children.js +0 -78
  69. package/dist/lint/plugin-core/rules/required-children.js.map +0 -1
  70. package/dist/lint/plugin-core/rules/required-modes.d.ts +0 -14
  71. package/dist/lint/plugin-core/rules/required-modes.d.ts.map +0 -1
  72. package/dist/lint/plugin-core/rules/required-modes.js +0 -52
  73. package/dist/lint/plugin-core/rules/required-modes.js.map +0 -1
  74. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +0 -11
  75. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts.map +0 -1
  76. package/dist/lint/plugin-core/rules/required-typography-properties.js +0 -38
  77. package/dist/lint/plugin-core/rules/required-typography-properties.js.map +0 -1
  78. package/dist/logger.d.ts +0 -77
  79. package/dist/logger.d.ts.map +0 -1
  80. package/dist/logger.js +0 -136
  81. package/dist/logger.js.map +0 -1
  82. package/dist/parse/alias.d.ts +0 -34
  83. package/dist/parse/alias.d.ts.map +0 -1
  84. package/dist/parse/alias.js +0 -302
  85. package/dist/parse/alias.js.map +0 -1
  86. package/dist/parse/index.d.ts +0 -36
  87. package/dist/parse/index.d.ts.map +0 -1
  88. package/dist/parse/index.js +0 -226
  89. package/dist/parse/index.js.map +0 -1
  90. package/dist/parse/json.d.ts +0 -52
  91. package/dist/parse/json.d.ts.map +0 -1
  92. package/dist/parse/json.js +0 -161
  93. package/dist/parse/json.js.map +0 -1
  94. package/dist/parse/normalize.d.ts +0 -24
  95. package/dist/parse/normalize.d.ts.map +0 -1
  96. package/dist/parse/normalize.js +0 -185
  97. package/dist/parse/normalize.js.map +0 -1
  98. package/dist/parse/validate.d.ts +0 -63
  99. package/dist/parse/validate.d.ts.map +0 -1
  100. package/dist/parse/validate.js +0 -798
  101. package/dist/parse/validate.js.map +0 -1
  102. package/dist/types.d.ts +0 -265
  103. package/dist/types.d.ts.map +0 -1
  104. package/dist/types.js +0 -2
  105. package/dist/types.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@terrazzo/parser",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Parser/validator for the Design Tokens Community Group (DTCG) standard.",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "author": {
7
8
  "name": "Drew Powers",
@@ -22,33 +23,36 @@
22
23
  "lint"
23
24
  ],
24
25
  "main": "./dist/index.js",
26
+ "exports": {
27
+ ".": "./dist/index.js",
28
+ "./package.json": "./package.json"
29
+ },
25
30
  "homepage": "https://terrazzo.app/docs/cli/api/js",
26
31
  "repository": {
27
32
  "type": "git",
28
33
  "url": "https://github.com/terrazzoapp/terrazzo.git",
29
34
  "directory": "./packages/parser/"
30
35
  },
31
- "license": "MIT",
32
36
  "dependencies": {
33
37
  "@humanwhocodes/momoa": "^3.3.8",
34
38
  "@types/babel__code-frame": "^7.0.6",
35
39
  "@types/culori": "^4.0.0",
36
40
  "culori": "^4.0.1",
37
- "is-what": "^4.1.16",
38
41
  "merge-anything": "^5.1.7",
39
42
  "picocolors": "^1.1.1",
40
43
  "scule": "^1.3.0",
41
44
  "wildcard-match": "^5.1.4",
42
- "yaml": "^2.8.0",
43
- "@terrazzo/token-tools": "^0.7.3"
45
+ "@terrazzo/token-tools": "^0.8.0"
44
46
  },
45
47
  "devDependencies": {
46
48
  "yaml-to-momoa": "^0.0.3"
47
49
  },
48
50
  "scripts": {
49
- "build": "tsc -p tsconfig.build.json",
51
+ "build": "rolldown -c && attw --profile esm-only --pack .",
50
52
  "dev": "pnpm run build --watch",
51
- "lint": "biome check .",
53
+ "lint": "pnpm --filter @terrazzo/parser run \"/^lint:(js|ts)/\"",
54
+ "lint:js": "biome check .",
55
+ "lint:ts": "tsc --noEmit",
52
56
  "test": "vitest run"
53
57
  }
54
58
  }
@@ -0,0 +1,24 @@
1
+ import { defineConfig } from 'rolldown';
2
+ import { dts } from 'rolldown-plugin-dts';
3
+
4
+ export default defineConfig({
5
+ input: {
6
+ index: './src/index.ts',
7
+ },
8
+ plugins: [dts()],
9
+ external: [
10
+ '@humanwhocodes/mamoa',
11
+ '@terrazzo/token-tools',
12
+ 'culori',
13
+ 'merge-anything',
14
+ 'picocolors',
15
+ 'wildcard-match',
16
+ 'yaml-to-momoa',
17
+ 'yaml',
18
+ ],
19
+ output: {
20
+ dir: 'dist',
21
+ format: 'es',
22
+ sourcemap: true,
23
+ },
24
+ });
@@ -18,7 +18,7 @@ function validateTransformParams({
18
18
  params,
19
19
  logger,
20
20
  pluginName,
21
- }: { params: TokenTransformed; logger: Logger; pluginName: string }) {
21
+ }: { params: TokenTransformed; logger: Logger; pluginName: string }): void {
22
22
  const baseMessage: LogEntry = { group: 'plugin', label: pluginName, message: '' };
23
23
 
24
24
  // validate value is valid for SINGLE_VALUE or MULTI_VALUE
@@ -48,7 +48,7 @@ function validateTransformParams({
48
48
  export default async function build(
49
49
  tokens: Record<string, TokenNormalized>,
50
50
  { sources, logger = new Logger(), config }: BuildRunnerOptions,
51
- ) {
51
+ ): Promise<BuildRunnerResult> {
52
52
  const formats: Record<string, TokenTransformed[]> = {};
53
53
  const result: BuildRunnerResult = { outputFiles: [] };
54
54
 
package/src/index.ts CHANGED
@@ -15,4 +15,79 @@ export * from './parse/index.js';
15
15
 
16
16
  export * from './types.js';
17
17
 
18
- export * from '@terrazzo/token-tools/dist/types.js';
18
+ export type {
19
+ AliasToken,
20
+ AliasValue,
21
+ BooleanToken,
22
+ BooleanTokenNormalized,
23
+ BooleanValue,
24
+ BorderToken,
25
+ BorderTokenNormalized,
26
+ BorderValue,
27
+ BorderValueNormalized,
28
+ ColorSpace,
29
+ ColorToken,
30
+ ColorTokenNormalized,
31
+ ColorValue,
32
+ ColorValueNormalized,
33
+ CubicBezierToken,
34
+ CubicBezierTokenNormalized,
35
+ CubicBezierValue,
36
+ CustomTransformOptions,
37
+ DimensionToken,
38
+ DimensionTokenNormalized,
39
+ DimensionValue,
40
+ DurationToken,
41
+ DurationTokenNormalized,
42
+ DurationValue,
43
+ FontFamilyToken,
44
+ FontFamilyTokenNormalized,
45
+ FontFamilyValue,
46
+ FontFamilyValueNormalized,
47
+ FontWeightToken,
48
+ FontWeightTokenNormalized,
49
+ FontWeightValue,
50
+ FontWeightValueNormalized,
51
+ GradientStop,
52
+ GradientStopNormalized,
53
+ GradientToken,
54
+ GradientTokenNormalized,
55
+ GradientValue,
56
+ GradientValueNormalized,
57
+ Group,
58
+ GroupCore,
59
+ GroupOrToken,
60
+ LinkToken,
61
+ LinkTokenNormalized,
62
+ LinkValue,
63
+ ModeMap,
64
+ NumberToken,
65
+ NumberTokenNormalized,
66
+ NumberValue,
67
+ ShadowToken,
68
+ ShadowTokenNormalized,
69
+ ShadowValue,
70
+ ShadowValueNormalized,
71
+ StringToken,
72
+ StringTokenNormalized,
73
+ StringValue,
74
+ StrokeStyleToken,
75
+ StrokeStyleTokenNormalized,
76
+ StrokeStyleValue,
77
+ StrokeStyleValueExpanded,
78
+ Token,
79
+ TokenCore,
80
+ TokenMode,
81
+ TokenNormalized,
82
+ TokenNormalizedCore,
83
+ TokenNormalizedSet,
84
+ TokensSet,
85
+ TransitionToken,
86
+ TransitionTokenNormalized,
87
+ TransitionValue,
88
+ TransitionValueNormalized,
89
+ TypographyToken,
90
+ TypographyTokenNormalized,
91
+ TypographyValue,
92
+ TypographyValueNormalized,
93
+ } from '@terrazzo/token-tools';
@@ -1,13 +1,13 @@
1
- import type { DocumentNode, ObjectNode } from '@humanwhocodes/momoa';
1
+ import { type DocumentNode, type MemberNode, type ObjectNode, evaluate } from '@humanwhocodes/momoa';
2
2
  import { type Token, type TokenNormalized, pluralize, splitID } from '@terrazzo/token-tools';
3
3
  import type ytm from 'yaml-to-momoa';
4
4
  import lintRunner from '../lint/index.js';
5
5
  import Logger from '../logger.js';
6
6
  import type { ConfigInit, InputSource } from '../types.js';
7
7
  import applyAliases from './alias.js';
8
- import { getObjMembers, toMomoa, traverse } from './json.js';
8
+ import { getObjMembers, parseJSON, replaceObjMembers, toMomoa, traverse } from './json.js';
9
9
  import normalize from './normalize.js';
10
- import validateTokenNode from './validate.js';
10
+ import validateTokenNode, { type Visitors, getInheritedType } from './validate.js';
11
11
 
12
12
  export * from './alias.js';
13
13
  export * from './normalize.js';
@@ -30,6 +30,11 @@ export interface ParseOptions {
30
30
  continueOnError?: boolean;
31
31
  /** Provide yamlToMomoa module to parse YAML (by default, this isn’t shipped to cut down on package weight) */
32
32
  yamlToMomoa?: typeof ytm;
33
+ /**
34
+ * Transform API
35
+ * @see https://terrazzo.app/docs/api/js#transform-api
36
+ */
37
+ transform?: Visitors;
33
38
  /** (internal cache; do not use) */
34
39
  _sources?: Record<string, InputSource>;
35
40
  }
@@ -48,6 +53,7 @@ export default async function parse(
48
53
  config = {} as ConfigInit,
49
54
  continueOnError = false,
50
55
  yamlToMomoa,
56
+ transform,
51
57
  _sources = {},
52
58
  }: ParseOptions = {} as ParseOptions,
53
59
  ): Promise<ParseResult> {
@@ -91,6 +97,7 @@ export default async function parse(
91
97
  skipLint,
92
98
  continueOnError,
93
99
  yamlToMomoa,
100
+ transform,
94
101
  });
95
102
  tokensSet = Object.assign(tokensSet, result.tokens);
96
103
  if (src.filename) {
@@ -166,6 +173,7 @@ async function parseSingle(
166
173
  config,
167
174
  skipLint,
168
175
  continueOnError = false,
176
+ transform,
169
177
  yamlToMomoa, // optional dependency, declared here so the parser itself doesn’t have to load a heavy dep in-browser
170
178
  }: {
171
179
  filename: URL;
@@ -173,12 +181,13 @@ async function parseSingle(
173
181
  config: ConfigInit;
174
182
  skipLint: boolean;
175
183
  continueOnError?: boolean;
184
+ transform: ParseOptions['transform'] | undefined;
176
185
  yamlToMomoa?: typeof ytm;
177
186
  },
178
187
  ): Promise<{ tokens: Record<string, Token>; document: DocumentNode; src?: string }> {
179
188
  // 1. Build AST
180
189
  const startParsing = performance.now();
181
- const { src, document } = toMomoa(input, { filename, logger, continueOnError, yamlToMomoa });
190
+ let { src, document } = toMomoa(input, { filename, logger, continueOnError, yamlToMomoa });
182
191
  logger.debug({
183
192
  group: 'parser',
184
193
  label: 'json',
@@ -187,27 +196,77 @@ async function parseSingle(
187
196
  });
188
197
  const tokensSet: Record<string, TokenNormalized> = {};
189
198
 
199
+ // 1a. if there’s a root() transformer, then re-parse
200
+ if (transform?.root) {
201
+ const json = typeof input === 'string' ? JSON.parse(input) : input;
202
+ const result = transform?.root(json, '.', document);
203
+ if (result) {
204
+ const reRunResult = toMomoa(result, { filename, logger, continueOnError /* YAML not needed in transform() */ });
205
+ src = reRunResult.src;
206
+ document = reRunResult.document;
207
+ }
208
+ }
209
+
190
210
  // 2. Walk AST to validate tokens
191
211
  let tokenCount = 0;
192
212
  const startValidate = performance.now();
193
- const $typeInheritance: Record<string, Token['$type']> = {};
213
+ const $typeInheritance: Record<string, MemberNode> = {};
194
214
  traverse(document, {
195
215
  enter(node, parent, subpath) {
196
216
  // if $type appears at root of tokens.json, collect it
197
217
  if (node.type === 'Document' && node.body.type === 'Object' && node.body.members) {
198
- const members = getObjMembers(node.body);
199
- if (members.$type && members.$type.type === 'String' && !members.$value) {
200
- // @ts-ignore
201
- $typeInheritance['.'] = node.body.members.find((m) => m.name.value === '$type');
218
+ const { members: rootMembers } = node.body;
219
+ const root$type = rootMembers.find((m) => m.name.type === 'String' && m.name.value === '$type');
220
+ const root$value = rootMembers.find((m) => m.name.type === 'String' && m.name.value === '$value');
221
+ if (root$type && !root$value) {
222
+ $typeInheritance['.'] = root$type;
223
+ }
224
+ }
225
+
226
+ // for transform() visitors, all non-tokens MUST be handled at this point (besides "root", which was handled above)
227
+ if (
228
+ node.type === 'Object' && // JSON object
229
+ subpath.length &&
230
+ !node.members.some((m) => m.name.type === 'String' && m.name.value === '$value') && // not the token node itself
231
+ !subpath.includes('$value') && // not a child of a token node, either
232
+ !subpath.includes('$extensions') // not metadata
233
+ ) {
234
+ if (transform?.group) {
235
+ const newJSON = transform?.group(evaluate(node), subpath.join('.'), node);
236
+ if (newJSON) {
237
+ replaceObjMembers(node, parseJSON(newJSON));
238
+ }
202
239
  }
203
240
  }
204
241
 
205
242
  // handle tokens
206
243
  if (node.type === 'Member') {
207
- const token = validateTokenNode(node, { filename, src, config, logger, parent, subpath, $typeInheritance });
208
- if (token) {
209
- tokensSet[token.id] = token;
210
- tokenCount++;
244
+ const inheritedTypeNode = getInheritedType(node, { subpath, $typeInheritance });
245
+ if (node.value.type === 'Object') {
246
+ const $type =
247
+ node.value.members.find((m) => m.name.type === 'String' && m.name.value === '$type') || inheritedTypeNode;
248
+ const $value = node.value.members.find((m) => m.name.type === 'String' && m.name.value === '$value');
249
+ if ($value && $type?.value.type === 'String' && transform?.[$type.value.value]) {
250
+ const result = transform[$type.value.value]?.(evaluate(node.value), subpath.join('.'), node);
251
+ if (result) {
252
+ node.value = parseJSON(result).body;
253
+ }
254
+ }
255
+
256
+ const token = validateTokenNode(node, {
257
+ filename,
258
+ src,
259
+ config,
260
+ logger,
261
+ parent,
262
+ subpath,
263
+ transform,
264
+ inheritedTypeNode,
265
+ });
266
+ if (token) {
267
+ tokensSet[token.id] = token;
268
+ tokenCount++;
269
+ }
211
270
  }
212
271
  }
213
272
  },
package/src/parse/json.ts CHANGED
@@ -3,15 +3,16 @@ import {
3
3
  type DocumentNode,
4
4
  type MemberNode,
5
5
  type ObjectNode,
6
+ type ParseOptions,
6
7
  type ValueNode,
7
- parse as parseJSON,
8
+ parse as momoaParse,
8
9
  print,
9
10
  } from '@humanwhocodes/momoa';
10
11
  import type yamlToMomoa from 'yaml-to-momoa';
11
12
  import type Logger from '../logger.js';
12
13
  import type { InputSource } from '../types.js';
13
14
 
14
- export interface Visitor {
15
+ export interface JSONVisitor {
15
16
  enter?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
16
17
  exit?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
17
18
  }
@@ -54,11 +55,7 @@ export function getObjMembers(node: ObjectNode): Record<string | number, ValueNo
54
55
  return members;
55
56
  }
56
57
 
57
- /**
58
- * Inject members to ObjectNode
59
- * @param {ObjectNode} node
60
- * @param {MemberNode[]} members
61
- */
58
+ /** Inject members to ObjectNode */
62
59
  export function injectObjMembers(node: ObjectNode, members: MemberNode[] = []) {
63
60
  if (node.type !== 'Object') {
64
61
  return;
@@ -66,11 +63,16 @@ export function injectObjMembers(node: ObjectNode, members: MemberNode[] = []) {
66
63
  node.members.push(...members);
67
64
  }
68
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
+
69
71
  /**
70
72
  * Variation of Momoa’s traverse(), which keeps track of global path.
71
73
  * Allows mutation of AST (along with any consequences)
72
74
  */
73
- export function traverse(root: AnyNode, visitor: Visitor) {
75
+ export function traverse(root: AnyNode, visitor: JSONVisitor) {
74
76
  /**
75
77
  * Recursively visits a node.
76
78
  * @param {AnyNode} node The node to visit.
@@ -89,20 +91,15 @@ export function traverse(root: AnyNode, visitor: Visitor) {
89
91
  const childNode = CHILD_KEYS[node.type];
90
92
  for (const key of childNode ?? []) {
91
93
  const value = node[key as keyof typeof node];
92
-
93
- if (value && typeof value === 'object') {
94
- if (Array.isArray(value)) {
95
- for (let i = 0; i < value.length; i++) {
96
- visitNode(
97
- // @ts-expect-error this is safe
98
- value[i],
99
- node,
100
- key === 'elements' ? [...nextPath, String(i)] : nextPath,
101
- );
102
- }
103
- } else if (isNode(value)) {
104
- visitNode(value as unknown as AnyNode, node, nextPath);
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);
105
100
  }
101
+ } else if (isNode(value)) {
102
+ visitNode(value as unknown as AnyNode, node, nextPath);
106
103
  }
107
104
  }
108
105
 
@@ -189,17 +186,26 @@ export function toMomoa(
189
186
  });
190
187
  }
191
188
  } else {
192
- document = parseJSON(
193
- typeof input === 'string' ? input : JSON.stringify(input, undefined, 2), // everything else: assert it’s JSON-serializable
194
- {
195
- mode: 'jsonc',
196
- ranges: true,
197
- tokens: true,
198
- },
199
- );
189
+ document = parseJSON(input);
200
190
  }
201
191
  if (!src) {
202
192
  src = print(document, { indent: 2 });
203
193
  }
204
194
  return { src, document };
205
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
+ }
@@ -34,9 +34,6 @@ export const FONT_WEIGHT_MAP = {
34
34
  'ultra-black': 950,
35
35
  };
36
36
 
37
- // Note: because we’re handling a lot of input values, the type inference gets lost.
38
- // This file is expected to have a lot of `@ts-ignore` comments.
39
-
40
37
  const NUMBER_WITH_UNIT_RE = /(-?\d*\.?\d+)(.*)/;
41
38
 
42
39
  /** Fill in defaults, and return predictable shapes for tokens */
@@ -140,14 +137,10 @@ export default function normalizeValue<T extends Token>(token: T): T['$value'] {
140
137
  (layer) =>
141
138
  ({
142
139
  color: normalizeValue({ $type: 'color', $value: layer.color }),
143
- // @ts-ignore
144
- offsetX: normalizeValue({ $type: 'dimension', $value: layer.offsetX ?? 0 }),
145
- // @ts-ignore
146
- offsetY: normalizeValue({ $type: 'dimension', $value: layer.offsetY ?? 0 }),
147
- // @ts-ignore
148
- blur: normalizeValue({ $type: 'dimension', $value: layer.blur ?? 0 }),
149
- // @ts-ignore
150
- spread: normalizeValue({ $type: 'dimension', $value: layer.spread ?? 0 }),
140
+ offsetX: normalizeValue({ $type: 'dimension', $value: layer.offsetX ?? { value: 0, unit: 'px' } }),
141
+ offsetY: normalizeValue({ $type: 'dimension', $value: layer.offsetY ?? { value: 0, unit: 'px' } }),
142
+ blur: normalizeValue({ $type: 'dimension', $value: layer.blur ?? { value: 0, unit: 'px' } }),
143
+ spread: normalizeValue({ $type: 'dimension', $value: layer.spread ?? { value: 0, unit: 'px' } }),
151
144
  inset: layer.inset === true,
152
145
  }) as ShadowValueNormalized,
153
146
  );
@@ -163,11 +156,8 @@ export default function normalizeValue<T extends Token>(token: T): T['$value'] {
163
156
  return token.$value;
164
157
  }
165
158
  return {
166
- // @ts-ignore
167
159
  duration: normalizeValue({ $type: 'duration', $value: token.$value.duration ?? 0 }),
168
- // @ts-ignore
169
160
  delay: normalizeValue({ $type: 'duration', $value: token.$value.delay ?? 0 }),
170
- // @ts-ignore
171
161
  timingFunction: normalizeValue({ $type: 'cubicBezier', $value: token.$value.timingFunction }),
172
162
  } as TransitionValue;
173
163
  }