@contentful/experience-design-system-types 2.41.1-dev-build-0eca66e2.0 → 2.41.1-dev-build-8f1f54c3.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.
- package/dist/package.json +1 -1
- package/dist/src/cdf/build.d.ts +5 -0
- package/dist/src/cdf/build.js +6 -1
- package/dist/src/cdf/repair-token-kinds.d.ts +8 -0
- package/dist/src/cdf/repair-token-kinds.js +46 -0
- package/dist/src/cdf/schema.d.ts +14 -0
- package/dist/src/cdf/schema.js +4 -1
- package/dist/src/cdf/validate.js +46 -5
- package/package.json +1 -1
package/dist/package.json
CHANGED
package/dist/src/cdf/build.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface CDFDocument {
|
|
|
9
9
|
$schema: typeof CDF_SCHEMA_URL;
|
|
10
10
|
[key: string]: unknown;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Every document that is saved, previewed or pushed is built here, so this is where a token property that has no kind is
|
|
14
|
+
* given one: the API rejects it, and the preview does not notice. `onRepair` hears about each repair.
|
|
15
|
+
*/
|
|
12
16
|
export declare function buildCDF(components: Array<{
|
|
13
17
|
key: string;
|
|
14
18
|
entry: CDFComponentEntry;
|
|
@@ -17,6 +21,7 @@ export declare function buildCDF(components: Array<{
|
|
|
17
21
|
entry: CDFTokenEntry;
|
|
18
22
|
}>, opts?: {
|
|
19
23
|
deleteAll?: boolean;
|
|
24
|
+
onRepair?: (message: string) => void;
|
|
20
25
|
}): CDFDocument | undefined;
|
|
21
26
|
/**
|
|
22
27
|
* Filters a CDF document down to a selected subset of component keys and
|
package/dist/src/cdf/build.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { repairTokenKinds } from './repair-token-kinds.js';
|
|
1
2
|
import { CDF_SCHEMA_URL } from './schema.js';
|
|
2
3
|
/** Drops `$required` from slot definitions — the server never reads it and
|
|
3
4
|
* older CDF consumers reject unknown slot fields. */
|
|
@@ -34,12 +35,16 @@ function resolveTokenPathCollisions(tokens) {
|
|
|
34
35
|
return { path, entry: token.entry };
|
|
35
36
|
});
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Every document that is saved, previewed or pushed is built here, so this is where a token property that has no kind is
|
|
40
|
+
* given one: the API rejects it, and the preview does not notice. `onRepair` hears about each repair.
|
|
41
|
+
*/
|
|
37
42
|
export function buildCDF(components, tokens, opts = {}) {
|
|
38
43
|
if (components.length === 0 && tokens.length === 0 && !opts.deleteAll)
|
|
39
44
|
return undefined;
|
|
40
45
|
const doc = { $schema: CDF_SCHEMA_URL };
|
|
41
46
|
for (const { key, entry } of components) {
|
|
42
|
-
setAtPath(doc, key, stripUnsupportedSlotFields(entry));
|
|
47
|
+
setAtPath(doc, key, stripUnsupportedSlotFields(repairTokenKinds(entry, key, tokens, opts.onRepair)));
|
|
43
48
|
}
|
|
44
49
|
for (const { path, entry } of resolveTokenPathCollisions(tokens)) {
|
|
45
50
|
setAtPath(doc, path, entry);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CDFComponentEntry, CDFTokenEntry } from './types.js';
|
|
2
|
+
type Tokens = Array<{
|
|
3
|
+
path: string;
|
|
4
|
+
entry: CDFTokenEntry;
|
|
5
|
+
}>;
|
|
6
|
+
/** Gives every token property a `$token.kind`. Says what it did through `note`, so a repair is never silent. */
|
|
7
|
+
export declare function repairTokenKinds(entry: CDFComponentEntry, componentKey: string, tokens: Tokens, note?: (message: string) => void): CDFComponentEntry;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** The warning the agent writes into a description when it left the kind out. Once the kind is settled it is stale, and a description is customer-facing text. */
|
|
2
|
+
const UNRESOLVED_WARNING = /\s*WARNING: token_kind unknown[^\n]*/;
|
|
3
|
+
/**
|
|
4
|
+
* The token's own type if the property's default names a token that exists, which is the token the property is bound to.
|
|
5
|
+
* Failing that, the one type shared by every token with the property's name in its path, or undefined when there is not
|
|
6
|
+
* exactly one.
|
|
7
|
+
*/
|
|
8
|
+
function inferTokenKind(propName, prop, tokens) {
|
|
9
|
+
const bound = typeof prop.$default === 'string' ? tokens.find(({ path }) => path === prop.$default) : undefined;
|
|
10
|
+
if (bound)
|
|
11
|
+
return bound.entry.$type;
|
|
12
|
+
const name = propName.toLowerCase();
|
|
13
|
+
const kinds = new Set(tokens.filter(({ path }) => path.toLowerCase().split('.').includes(name)).map(({ entry }) => entry.$type));
|
|
14
|
+
return kinds.size === 1 ? [...kinds][0] : undefined;
|
|
15
|
+
}
|
|
16
|
+
function withoutWarning(prop) {
|
|
17
|
+
if (prop.$description === undefined)
|
|
18
|
+
return prop;
|
|
19
|
+
const description = prop.$description.replace(UNRESOLVED_WARNING, '').trim();
|
|
20
|
+
const { $description: _stale, ...rest } = prop;
|
|
21
|
+
return description ? { ...rest, $description: description } : rest;
|
|
22
|
+
}
|
|
23
|
+
/** Gives every token property a `$token.kind`. Says what it did through `note`, so a repair is never silent. */
|
|
24
|
+
export function repairTokenKinds(entry, componentKey, tokens, note) {
|
|
25
|
+
const properties = { ...entry.$properties };
|
|
26
|
+
let repaired = false;
|
|
27
|
+
for (const [name, prop] of Object.entries(entry.$properties)) {
|
|
28
|
+
if (prop.$type !== 'token' || prop['$token.kind'])
|
|
29
|
+
continue;
|
|
30
|
+
repaired = true;
|
|
31
|
+
const kind = inferTokenKind(name, prop, tokens);
|
|
32
|
+
if (kind) {
|
|
33
|
+
properties[name] = { ...withoutWarning(prop), '$token.kind': kind };
|
|
34
|
+
note?.(`${componentKey}.${name} had no token kind, so "${kind}" was taken from the token file`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Its default and allowed list are token paths, which mean nothing on a string property.
|
|
38
|
+
const plain = { ...withoutWarning(prop), $type: 'string' };
|
|
39
|
+
delete plain['$token.allowed'];
|
|
40
|
+
delete plain.$default;
|
|
41
|
+
properties[name] = plain;
|
|
42
|
+
note?.(`${componentKey}.${name} had no token kind and none could be told, so it was saved as a string property`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return repaired ? { ...entry, $properties: properties } : entry;
|
|
46
|
+
}
|
package/dist/src/cdf/schema.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export declare const cdfJsonSchema: {
|
|
|
84
84
|
};
|
|
85
85
|
readonly '$token.kind': {
|
|
86
86
|
readonly type: "string";
|
|
87
|
+
readonly minLength: 1;
|
|
87
88
|
};
|
|
88
89
|
readonly '$token.allowed': {
|
|
89
90
|
readonly type: "array";
|
|
@@ -92,6 +93,19 @@ export declare const cdfJsonSchema: {
|
|
|
92
93
|
};
|
|
93
94
|
};
|
|
94
95
|
};
|
|
96
|
+
readonly if: {
|
|
97
|
+
readonly type: "object";
|
|
98
|
+
readonly properties: {
|
|
99
|
+
readonly $type: {
|
|
100
|
+
readonly const: "token";
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
readonly required: readonly ["$type"];
|
|
104
|
+
};
|
|
105
|
+
readonly then: {
|
|
106
|
+
readonly type: "object";
|
|
107
|
+
readonly required: readonly ["$token.kind"];
|
|
108
|
+
};
|
|
95
109
|
readonly additionalProperties: false;
|
|
96
110
|
};
|
|
97
111
|
readonly slot: {
|
package/dist/src/cdf/schema.js
CHANGED
|
@@ -49,9 +49,12 @@ export const cdfJsonSchema = {
|
|
|
49
49
|
$required: { type: 'boolean' },
|
|
50
50
|
$default: {},
|
|
51
51
|
$values: { type: 'array', items: { type: 'string' } },
|
|
52
|
-
'$token.kind': { type: 'string' },
|
|
52
|
+
'$token.kind': { type: 'string', minLength: 1 },
|
|
53
53
|
'$token.allowed': { type: 'array', items: { type: 'string' } },
|
|
54
54
|
},
|
|
55
|
+
// The API rejects a token property that does not say which kind of token it takes.
|
|
56
|
+
if: { type: 'object', properties: { $type: { const: 'token' } }, required: ['$type'] },
|
|
57
|
+
then: { type: 'object', required: ['$token.kind'] },
|
|
55
58
|
additionalProperties: false,
|
|
56
59
|
},
|
|
57
60
|
slot: {
|
package/dist/src/cdf/validate.js
CHANGED
|
@@ -39,14 +39,55 @@ export function parseCDFComponents(obj, prefix = '') {
|
|
|
39
39
|
}
|
|
40
40
|
return { components, tokens };
|
|
41
41
|
}
|
|
42
|
+
function entryValidator(name) {
|
|
43
|
+
const check = ajv.getSchema(`${CDF_SCHEMA_URL}#/definitions/${name}`);
|
|
44
|
+
if (!check)
|
|
45
|
+
throw new Error(`the CDF schema has no "${name}" definition`);
|
|
46
|
+
return check;
|
|
47
|
+
}
|
|
48
|
+
const validateComponentEntry = entryValidator('component');
|
|
49
|
+
const validateTokenEntry = entryValidator('token');
|
|
50
|
+
// A failed if/then rule adds a second, vaguer error; the first one already says what is wrong.
|
|
51
|
+
function toValidationErrors(errors, prefix = '') {
|
|
52
|
+
return (errors ?? [])
|
|
53
|
+
.filter((err) => err.keyword !== 'if')
|
|
54
|
+
.map((err) => ({
|
|
55
|
+
path: `${prefix}${err.instancePath}` || '/',
|
|
56
|
+
message: err.message ?? 'Unknown validation error',
|
|
57
|
+
expected: err.params ? JSON.stringify(err.params) : undefined,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The schema's top level is a `oneOf` of component, token and group, and Ajv reports a failed `oneOf` by listing the errors
|
|
62
|
+
* of every branch, which buries the real one under errors from branches that never applied. This validates each entry
|
|
63
|
+
* against the one definition its `$type` names instead, so the errors are about the entry that is wrong.
|
|
64
|
+
*/
|
|
65
|
+
function entryErrors(node, prefix) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
for (const [key, value] of Object.entries(node)) {
|
|
68
|
+
if (key.startsWith('$') || typeof value !== 'object' || value === null)
|
|
69
|
+
continue;
|
|
70
|
+
const path = `${prefix}/${key}`;
|
|
71
|
+
const type = value['$type'];
|
|
72
|
+
const check = type === 'component'
|
|
73
|
+
? validateComponentEntry
|
|
74
|
+
: TOKEN_TYPE_SET.has(type)
|
|
75
|
+
? validateTokenEntry
|
|
76
|
+
: undefined;
|
|
77
|
+
if (!check) {
|
|
78
|
+
errors.push(...entryErrors(value, path));
|
|
79
|
+
}
|
|
80
|
+
else if (!check(value)) {
|
|
81
|
+
errors.push(...toValidationErrors(check.errors, path));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return errors;
|
|
85
|
+
}
|
|
42
86
|
export function validateCDF(input) {
|
|
43
87
|
const valid = validate(input);
|
|
44
88
|
if (!valid) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
message: err.message ?? 'Unknown validation error',
|
|
48
|
-
expected: err.params ? JSON.stringify(err.params) : undefined,
|
|
49
|
-
}));
|
|
89
|
+
const focused = typeof input === 'object' && input !== null ? entryErrors(input, '') : [];
|
|
90
|
+
const errors = focused.length > 0 ? focused : toValidationErrors(validate.errors);
|
|
50
91
|
return { valid: false, errors, components: [], tokens: [] };
|
|
51
92
|
}
|
|
52
93
|
const file = input;
|