@terrazzo/parser 2.0.0-alpha.4 → 2.0.0-alpha.5
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/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -95
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
- package/src/build/index.ts +5 -2
- package/src/config.ts +1 -8
- package/src/logger.ts +19 -7
- package/src/parse/index.ts +10 -1
- package/src/parse/process.ts +2 -3
- package/src/parse/token.ts +3 -2
- package/src/resolver/load.ts +2 -2
- package/src/resolver/validate.ts +14 -2
- package/src/types.ts +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrazzo/parser",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.5",
|
|
4
4
|
"description": "Parser/validator for the Design Tokens Community Group (DTCG) standard.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,15 @@
|
|
|
44
44
|
"scule": "^1.3.0",
|
|
45
45
|
"wildcard-match": "^5.1.4",
|
|
46
46
|
"@terrazzo/json-schema-tools": "^0.1.0-alpha.0",
|
|
47
|
-
"@terrazzo/token-tools": "^2.0.0-alpha.
|
|
47
|
+
"@terrazzo/token-tools": "^2.0.0-alpha.5"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"yaml-to-momoa": "0.0.8"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"yaml-to-momoa": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
48
56
|
},
|
|
49
57
|
"devDependencies": {
|
|
50
58
|
"yaml-to-momoa": "0.0.8"
|
|
@@ -52,6 +60,7 @@
|
|
|
52
60
|
"scripts": {
|
|
53
61
|
"build": "rolldown -c && attw --profile esm-only --pack .",
|
|
54
62
|
"dev": "rolldown -w -c",
|
|
63
|
+
"format": "biome check --fix --unsafe .",
|
|
55
64
|
"lint": "pnpm --filter @terrazzo/parser run \"/^lint:(js|ts)/\"",
|
|
56
65
|
"lint:js": "biome check .",
|
|
57
66
|
"lint:ts": "tsc --noEmit",
|
package/src/build/index.ts
CHANGED
|
@@ -2,11 +2,12 @@ import type { InputSourceWithDocument } from '@terrazzo/json-schema-tools';
|
|
|
2
2
|
import type { TokenNormalized } from '@terrazzo/token-tools';
|
|
3
3
|
import wcmatch from 'wildcard-match';
|
|
4
4
|
import Logger, { type LogEntry } from '../logger.js';
|
|
5
|
-
import type { BuildRunnerResult, ConfigInit, TokenTransformed, TransformParams } from '../types.js';
|
|
5
|
+
import type { BuildRunnerResult, ConfigInit, Resolver, TokenTransformed, TransformParams } from '../types.js';
|
|
6
6
|
|
|
7
7
|
export interface BuildRunnerOptions {
|
|
8
8
|
sources: InputSourceWithDocument[];
|
|
9
9
|
config: ConfigInit;
|
|
10
|
+
resolver: Resolver;
|
|
10
11
|
logger?: Logger;
|
|
11
12
|
}
|
|
12
13
|
export const SINGLE_VALUE = 'SINGLE_VALUE';
|
|
@@ -48,7 +49,7 @@ function validateTransformParams({
|
|
|
48
49
|
/** Run build stage */
|
|
49
50
|
export default async function build(
|
|
50
51
|
tokens: Record<string, TokenNormalized>,
|
|
51
|
-
{ sources, logger = new Logger(), config }: BuildRunnerOptions,
|
|
52
|
+
{ resolver, sources, logger = new Logger(), config }: BuildRunnerOptions,
|
|
52
53
|
): Promise<BuildRunnerResult> {
|
|
53
54
|
const formats: Record<string, TokenTransformed[]> = {};
|
|
54
55
|
const result: BuildRunnerResult = { outputFiles: [] };
|
|
@@ -133,6 +134,7 @@ export default async function build(
|
|
|
133
134
|
formats[params.format]![foundTokenI]!.type = typeof cleanValue === 'string' ? SINGLE_VALUE : MULTI_VALUE;
|
|
134
135
|
}
|
|
135
136
|
},
|
|
137
|
+
resolver,
|
|
136
138
|
});
|
|
137
139
|
}
|
|
138
140
|
}
|
|
@@ -155,6 +157,7 @@ export default async function build(
|
|
|
155
157
|
tokens,
|
|
156
158
|
sources,
|
|
157
159
|
getTransforms,
|
|
160
|
+
resolver,
|
|
158
161
|
outputFile(filename, contents) {
|
|
159
162
|
const resolved = new URL(filename, config.outDir);
|
|
160
163
|
if (result.outputFiles.some((f) => new URL(f.filename, config.outDir).href === resolved.href)) {
|
package/src/config.ts
CHANGED
|
@@ -29,7 +29,7 @@ export default function defineConfig(
|
|
|
29
29
|
|
|
30
30
|
// 2. Start build by calling config()
|
|
31
31
|
for (const plugin of config.plugins) {
|
|
32
|
-
plugin.config?.({ ...config });
|
|
32
|
+
plugin.config?.({ ...config }, { logger });
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// 3. finish
|
|
@@ -267,13 +267,6 @@ function normalizeLint({ config, logger }: { config: ConfigInit; logger: Logger
|
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
-
|
|
271
|
-
// Apply recommended config in places user hasn’t explicitly opted-out
|
|
272
|
-
for (const [id, severity] of Object.entries(RECOMMENDED_CONFIG)) {
|
|
273
|
-
if (!(id in config.lint.rules)) {
|
|
274
|
-
config.lint.rules[id] = severity;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
270
|
}
|
|
278
271
|
} else {
|
|
279
272
|
config.lint = {
|
package/src/logger.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface LogEntry {
|
|
|
26
26
|
node?: momoa.AnyNode;
|
|
27
27
|
/** To show a code frame, provide the original source code */
|
|
28
28
|
src?: string;
|
|
29
|
+
/** Display performance timing */
|
|
30
|
+
timing?: number;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export interface DebugEntry {
|
|
@@ -61,6 +63,9 @@ export function formatMessage(entry: LogEntry, severity: LogSeverity) {
|
|
|
61
63
|
if (severity in MESSAGE_COLOR) {
|
|
62
64
|
message = MESSAGE_COLOR[severity]!(message);
|
|
63
65
|
}
|
|
66
|
+
if (typeof entry.timing === 'number') {
|
|
67
|
+
message = `${message} ${formatTiming(entry.timing)}`;
|
|
68
|
+
}
|
|
64
69
|
if (entry.node) {
|
|
65
70
|
const start = entry.node?.loc?.start ?? { line: 0, column: 0 };
|
|
66
71
|
// strip "file://" protocol, but not href
|
|
@@ -139,6 +144,7 @@ export default class Logger {
|
|
|
139
144
|
return;
|
|
140
145
|
}
|
|
141
146
|
const message = formatMessage(entry, 'warn');
|
|
147
|
+
|
|
142
148
|
// biome-ignore lint/suspicious/noConsole: this is a logger
|
|
143
149
|
console.warn(message);
|
|
144
150
|
}
|
|
@@ -168,13 +174,7 @@ export default class Logger {
|
|
|
168
174
|
|
|
169
175
|
message = `${pc.dim(timeFormatter.format(performance.now()))} ${message}`;
|
|
170
176
|
if (typeof entry.timing === 'number') {
|
|
171
|
-
|
|
172
|
-
if (entry.timing < 1_000) {
|
|
173
|
-
timing = `${Math.round(entry.timing * 100) / 100}ms`;
|
|
174
|
-
} else if (entry.timing < 60_000) {
|
|
175
|
-
timing = `${Math.round(entry.timing * 100) / 100_000}s`;
|
|
176
|
-
}
|
|
177
|
-
message = `${message}${timing ? pc.dim(` [${timing}]`) : ''}`;
|
|
177
|
+
message = `${message} ${formatTiming(entry.timing)}`;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// biome-ignore lint/suspicious/noConsole: this is a logger
|
|
@@ -193,6 +193,18 @@ export default class Logger {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
function formatTiming(timing: number): string {
|
|
197
|
+
let output = '';
|
|
198
|
+
if (timing < 1_000) {
|
|
199
|
+
output = `${Math.round(timing * 100) / 100}ms`;
|
|
200
|
+
} else if (timing < 60_000) {
|
|
201
|
+
output = `${Math.round(timing) / 1_000}s`;
|
|
202
|
+
} else {
|
|
203
|
+
output = `${Math.round(timing / 1_000) / 60}m`;
|
|
204
|
+
}
|
|
205
|
+
return pc.dim(`[${output}]`);
|
|
206
|
+
}
|
|
207
|
+
|
|
196
208
|
export class TokensJSONError extends Error {
|
|
197
209
|
constructor(message: string) {
|
|
198
210
|
super(message);
|
package/src/parse/index.ts
CHANGED
|
@@ -73,6 +73,15 @@ export default async function parse(
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const resolverTiming = performance.now();
|
|
77
|
+
const finalResolver = resolver || (await createSyntheticResolver(tokens, { config, logger, req, sources }));
|
|
78
|
+
logger.debug({
|
|
79
|
+
message: 'Resolver finalized',
|
|
80
|
+
group: 'parser',
|
|
81
|
+
label: 'core',
|
|
82
|
+
timing: performance.now() - resolverTiming,
|
|
83
|
+
});
|
|
84
|
+
|
|
76
85
|
logger.debug({
|
|
77
86
|
message: 'Finish all parser tasks',
|
|
78
87
|
group: 'parser',
|
|
@@ -93,7 +102,7 @@ export default async function parse(
|
|
|
93
102
|
return {
|
|
94
103
|
tokens,
|
|
95
104
|
sources,
|
|
96
|
-
resolver:
|
|
105
|
+
resolver: finalResolver,
|
|
97
106
|
};
|
|
98
107
|
}
|
|
99
108
|
|
package/src/parse/process.ts
CHANGED
|
@@ -44,12 +44,11 @@ export function processTokens(
|
|
|
44
44
|
if (node.type !== 'Object') {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
groupFromNode(node, { path, groups });
|
|
47
|
+
groupFromNode(node, { path: isResolver ? filterResolverPaths(rawPath) : rawPath, groups });
|
|
49
48
|
const token = tokenFromNode(node, {
|
|
50
49
|
groups,
|
|
51
50
|
ignore: config.ignore,
|
|
52
|
-
path,
|
|
51
|
+
path: isResolver ? filterResolverPaths(rawPath) : rawPath,
|
|
53
52
|
source: rootSource,
|
|
54
53
|
});
|
|
55
54
|
if (token) {
|
package/src/parse/token.ts
CHANGED
|
@@ -35,7 +35,7 @@ export function tokenFromNode(
|
|
|
35
35
|
node: momoa.AnyNode,
|
|
36
36
|
{ groups, path, source, ignore }: TokenFromNodeOptions,
|
|
37
37
|
): TokenNormalized | undefined {
|
|
38
|
-
const isToken = node.type === 'Object' && getObjMember(node, '$value') && !path.includes('$extensions');
|
|
38
|
+
const isToken = node.type === 'Object' && !!getObjMember(node, '$value') && !path.includes('$extensions');
|
|
39
39
|
if (!isToken) {
|
|
40
40
|
return undefined;
|
|
41
41
|
}
|
|
@@ -59,6 +59,7 @@ export function tokenFromNode(
|
|
|
59
59
|
$deprecated: originalToken.$deprecated ?? group.$deprecated ?? undefined, // ⚠️ MUST use ?? here to inherit false correctly
|
|
60
60
|
$value: originalToken.$value,
|
|
61
61
|
$extensions: originalToken.$extensions || undefined,
|
|
62
|
+
$extends: originalToken.$extends || undefined,
|
|
62
63
|
aliasChain: undefined,
|
|
63
64
|
aliasedBy: undefined,
|
|
64
65
|
aliasOf: undefined,
|
|
@@ -94,7 +95,7 @@ export function tokenFromNode(
|
|
|
94
95
|
const $extensions = getObjMember(node, '$extensions');
|
|
95
96
|
if ($extensions) {
|
|
96
97
|
const modeNode = getObjMember($extensions as momoa.ObjectNode, 'mode') as momoa.ObjectNode;
|
|
97
|
-
for (const mode of Object.keys((token.$extensions as any).mode)) {
|
|
98
|
+
for (const mode of Object.keys((token.$extensions as any).mode ?? {})) {
|
|
98
99
|
const modeValue = (token.$extensions as any).mode[mode];
|
|
99
100
|
token.mode[mode] = {
|
|
100
101
|
$value: modeValue,
|
package/src/resolver/load.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as momoa from '@humanwhocodes/momoa';
|
|
1
|
+
import type * as momoa from '@humanwhocodes/momoa';
|
|
2
2
|
import { type InputSource, type InputSourceWithDocument, maybeRawJSON } from '@terrazzo/json-schema-tools';
|
|
3
3
|
import type { TokenNormalizedSet } from '@terrazzo/token-tools';
|
|
4
4
|
import { merge } from 'merge-anything';
|
|
@@ -160,7 +160,7 @@ export function createResolver(
|
|
|
160
160
|
const tokens = processTokens(rootSource, {
|
|
161
161
|
config,
|
|
162
162
|
logger,
|
|
163
|
-
sourceByFilename: {},
|
|
163
|
+
sourceByFilename: { [resolverSource._source.filename!.href]: rootSource },
|
|
164
164
|
refMap: {},
|
|
165
165
|
sources,
|
|
166
166
|
});
|
package/src/resolver/validate.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as momoa from '@humanwhocodes/momoa';
|
|
2
2
|
import { getObjMember, getObjMembers } from '@terrazzo/json-schema-tools';
|
|
3
3
|
import type { LogEntry, default as Logger } from '../logger.js';
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ export function isLikelyResolver(doc: momoa.DocumentNode): boolean {
|
|
|
24
24
|
case 'description':
|
|
25
25
|
case 'version': {
|
|
26
26
|
// 1. name, description, or version are a string
|
|
27
|
-
if (member.
|
|
27
|
+
if (member.value.type === 'String') {
|
|
28
28
|
return true;
|
|
29
29
|
}
|
|
30
30
|
break;
|
|
@@ -176,6 +176,7 @@ export function validateResolver(node: momoa.DocumentNode, { logger, src }: Vali
|
|
|
176
176
|
errors.push({ ...entry, message: `Expected object`, node: member.value });
|
|
177
177
|
}
|
|
178
178
|
break;
|
|
179
|
+
case '$schema':
|
|
179
180
|
case '$ref': {
|
|
180
181
|
if (member.value.type !== 'String') {
|
|
181
182
|
errors.push({ ...entry, message: `Expected string`, node: member.value });
|
|
@@ -329,6 +330,17 @@ export function validateModifier(
|
|
|
329
330
|
}
|
|
330
331
|
break;
|
|
331
332
|
}
|
|
333
|
+
case 'default': {
|
|
334
|
+
if (member.value.type !== 'String') {
|
|
335
|
+
errors.push({ ...entry, message: `Expected string`, node: member.value });
|
|
336
|
+
} else {
|
|
337
|
+
const contexts = getObjMember(node, 'contexts') as momoa.ObjectNode | undefined;
|
|
338
|
+
if (!contexts || !getObjMember(contexts, member.value.value)) {
|
|
339
|
+
errors.push({ ...entry, message: 'Invalid default context', node: member.value });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
332
344
|
case '$defs':
|
|
333
345
|
case '$extensions':
|
|
334
346
|
if (member.value.type !== 'Object') {
|
package/src/types.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface BuildHookOptions {
|
|
|
32
32
|
getTransforms(params: TransformParams): TokenTransformed[];
|
|
33
33
|
/** Momoa documents */
|
|
34
34
|
sources: InputSourceWithDocument[];
|
|
35
|
+
/** Resolver */
|
|
36
|
+
resolver: Resolver;
|
|
35
37
|
outputFile: (
|
|
36
38
|
/** Filename to output (relative to outDir) */
|
|
37
39
|
filename: string,
|
|
@@ -311,7 +313,7 @@ export interface Plugin {
|
|
|
311
313
|
name: string;
|
|
312
314
|
/** Read config, and optionally modify */
|
|
313
315
|
// biome-ignore lint/suspicious/noConfusingVoidType format: this helps plugins be a little looser on their typing
|
|
314
|
-
config?(config: ConfigInit): void | ConfigInit | undefined;
|
|
316
|
+
config?(config: ConfigInit, context: PluginHookContext): void | ConfigInit | undefined;
|
|
315
317
|
/**
|
|
316
318
|
* Declare:
|
|
317
319
|
* - `"pre"`: run this plugin BEFORE all others
|
|
@@ -452,6 +454,8 @@ export interface TransformHookOptions {
|
|
|
452
454
|
meta?: TokenTransformedBase['meta'];
|
|
453
455
|
},
|
|
454
456
|
): void;
|
|
457
|
+
/** Resolver */
|
|
458
|
+
resolver: Resolver;
|
|
455
459
|
/** Momoa documents */
|
|
456
460
|
sources: InputSourceWithDocument[];
|
|
457
461
|
}
|