@astryxdesign/cli 0.4.0 → 0.4.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.
- package/CHANGELOG.md +14 -0
- package/api/template/template.doc.mjs +2 -2
- package/api/theme/build/build.mjs +13 -7
- package/api/theme/build/build.test.mjs +51 -0
- package/assets/docs/internationalization.doc.mjs +3 -3
- package/assets/docs/tokens.doc.mjs +35 -1
- package/authoring/integration/integration.doc.mjs +1 -1
- package/clients/cli/commands/build-theme.registry.test.mjs +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @xds/cli
|
|
2
2
|
|
|
3
|
+
# 0.4.1
|
|
4
|
+
|
|
5
|
+
#### Fixes
|
|
6
|
+
|
|
7
|
+
- `astryx theme build` no longer warns `Unknown prop` for documented state override keys. Component docs declare state-driven selectors under `theming.targets[].states` (`radio` → `checked`/`disabled`, `calendar-day` → `today`/`selected`, …), but override validation only loaded `visualProps`, so the state syntax the Theming Infrastructure wiki documents — `components: {radio: {checked: {...}}}` — warned on every build. The CSS was always generated correctly; only the warning was wrong. 30 targets across core were affected (#4778).
|
|
8
|
+
|
|
9
|
+
#### Contributors
|
|
10
|
+
|
|
11
|
+
Thanks to everyone who contributed to this release:
|
|
12
|
+
|
|
13
|
+
- @cixzhang
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
3
17
|
# 0.4.0
|
|
4
18
|
|
|
5
19
|
#### Breaking Changes
|
|
@@ -17,7 +17,7 @@ export const doc = {
|
|
|
17
17
|
'One entry point for the template family: with no name it lists the discovered ' +
|
|
18
18
|
"templates; with a name it returns that template's source, a layout skeleton, or " +
|
|
19
19
|
'scaffolds it into the project. Templates are discovered across core, external ' +
|
|
20
|
-
'packages, and integrations, so the same id can appear in more than one place
|
|
20
|
+
'packages, and integrations, so the same id can appear in more than one place; ' +
|
|
21
21
|
'narrow an ambiguous name with type and/or package.',
|
|
22
22
|
importPath: '@astryxdesign/cli/api',
|
|
23
23
|
signature:
|
|
@@ -110,7 +110,7 @@ export const doc = {
|
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
code: 'ERR_AMBIGUOUS_TEMPLATE',
|
|
113
|
-
when: 'the name matches more than one template across kinds/packages
|
|
113
|
+
when: 'the name matches more than one template across kinds/packages; narrow it with type and/or package',
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
code: 'ERR_NO_SOURCE',
|
|
@@ -815,7 +815,11 @@ ${iconType}export declare const ${toIdentifier(themeDef.name)}Theme: DefinedThem
|
|
|
815
815
|
// =============================================================================
|
|
816
816
|
|
|
817
817
|
/**
|
|
818
|
-
* Load known theme target keys
|
|
818
|
+
* Load known theme target keys from core component docs: the visual props AND
|
|
819
|
+
* the runtime states each target reflects. Both are legal override keys — the
|
|
820
|
+
* Theming Infrastructure wiki documents `radio: {checked}` and
|
|
821
|
+
* `'calendar-day': {today, selected}` alongside `button: {'variant:secondary'}`
|
|
822
|
+
* — so validation has to know both or documented syntax warns as unknown.
|
|
819
823
|
* Returns null when docs are unavailable so validation can skip unknown-key
|
|
820
824
|
* warnings rather than guessing from a second registry.
|
|
821
825
|
*
|
|
@@ -854,9 +858,10 @@ async function loadKnownComponents() {
|
|
|
854
858
|
if (typeof className !== 'string') continue;
|
|
855
859
|
const key = className.replace(/^astryx-/, '');
|
|
856
860
|
if (!key) continue;
|
|
857
|
-
const props =
|
|
858
|
-
|
|
859
|
-
|
|
861
|
+
const props = [target.visualProps, target.states]
|
|
862
|
+
.filter(list => Array.isArray(list))
|
|
863
|
+
.flat()
|
|
864
|
+
.filter((/** @type {unknown} */ p) => typeof p === 'string');
|
|
860
865
|
targets[key] = [...new Set([...(targets[key] || []), ...props])];
|
|
861
866
|
}
|
|
862
867
|
}
|
|
@@ -922,7 +927,8 @@ async function validateComponentOverrides(themeDef) {
|
|
|
922
927
|
continue;
|
|
923
928
|
}
|
|
924
929
|
|
|
925
|
-
// Check prop names in
|
|
930
|
+
// Check prop/state names in the override keys. A key is either `base`, a
|
|
931
|
+
// `prop:value` pair (possibly `+`-joined), or a bare state name.
|
|
926
932
|
const knownProps = knownComponents[component];
|
|
927
933
|
for (const key of Object.keys(rules)) {
|
|
928
934
|
if (key === 'base') continue;
|
|
@@ -934,8 +940,8 @@ async function validateComponentOverrides(themeDef) {
|
|
|
934
940
|
if (prop && !knownProps.includes(prop)) {
|
|
935
941
|
const hint =
|
|
936
942
|
knownProps.length > 0
|
|
937
|
-
? ` Known props: ${knownProps.join(', ')}`
|
|
938
|
-
: ' This component has no variant props.';
|
|
943
|
+
? ` Known props/states: ${knownProps.join(', ')}`
|
|
944
|
+
: ' This component has no variant props or states.';
|
|
939
945
|
warnings.push(
|
|
940
946
|
`Unknown prop "${prop}" on component "${component}".${hint}`,
|
|
941
947
|
);
|
|
@@ -222,3 +222,54 @@ describe('themeBuild() — check mode', () => {
|
|
|
222
222
|
expect(result?.data.stale).toEqual([]);
|
|
223
223
|
});
|
|
224
224
|
});
|
|
225
|
+
|
|
226
|
+
describe('themeBuild() — component override validation', () => {
|
|
227
|
+
it('accepts documented state keys without an "Unknown prop" warning', async () => {
|
|
228
|
+
// The state-key syntax the Theming Infrastructure wiki documents —
|
|
229
|
+
// `radio: {checked}`, `calendar-day: {today, selected}` — is declared in
|
|
230
|
+
// each component's doc under `theming.targets[].states`, not
|
|
231
|
+
// `visualProps`. `loadKnownComponents()` read only `visualProps`, so every
|
|
232
|
+
// one of these warned "Unknown prop": documented syntax that looked broken.
|
|
233
|
+
const themeFile = path.join(tmpDir, 'states.mjs');
|
|
234
|
+
fs.writeFileSync(
|
|
235
|
+
themeFile,
|
|
236
|
+
`export default {
|
|
237
|
+
name: 'states',
|
|
238
|
+
tokens: {'--color-bg': '#0a0a0a'},
|
|
239
|
+
components: {
|
|
240
|
+
radio: {
|
|
241
|
+
checked: {borderColor: 'var(--color-accent)'},
|
|
242
|
+
'checked+disabled': {opacity: '0.5'},
|
|
243
|
+
},
|
|
244
|
+
'calendar-day': {
|
|
245
|
+
today: {fontWeight: '700'},
|
|
246
|
+
selected: {backgroundColor: 'var(--color-accent)'},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
};\n`,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const result = await themeBuild('states.mjs', {}, {cwd: tmpDir});
|
|
253
|
+
|
|
254
|
+
expect(result?.data.warnings).toEqual([]);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('still warns on a key that is neither a visual prop nor a state', async () => {
|
|
258
|
+
// Widening the known set to states must not turn the guard off.
|
|
259
|
+
const themeFile = path.join(tmpDir, 'bogus.mjs');
|
|
260
|
+
fs.writeFileSync(
|
|
261
|
+
themeFile,
|
|
262
|
+
`export default {
|
|
263
|
+
name: 'bogus',
|
|
264
|
+
tokens: {'--color-bg': '#0a0a0a'},
|
|
265
|
+
components: {radio: {notAState: {opacity: '0.5'}}},
|
|
266
|
+
};\n`,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const result = await themeBuild('bogus.mjs', {}, {cwd: tmpDir});
|
|
270
|
+
|
|
271
|
+
expect(result?.data.warnings).toEqual([
|
|
272
|
+
expect.stringContaining('Unknown prop "notAState" on component "radio"'),
|
|
273
|
+
]);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -359,7 +359,7 @@ function SaveButton() {
|
|
|
359
359
|
{
|
|
360
360
|
type: 'heading',
|
|
361
361
|
level: 4,
|
|
362
|
-
text: '2. Directional icons
|
|
362
|
+
text: '2. Directional icons: mirror with CSS, not a name-swap',
|
|
363
363
|
},
|
|
364
364
|
{
|
|
365
365
|
type: 'prose',
|
|
@@ -384,7 +384,7 @@ function NextButton() {
|
|
|
384
384
|
{
|
|
385
385
|
type: 'heading',
|
|
386
386
|
level: 4,
|
|
387
|
-
text: '3. Behavioral logic
|
|
387
|
+
text: '3. Behavioral logic: read the DOM lazily, on the event',
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
type: 'prose',
|
|
@@ -393,7 +393,7 @@ function NextButton() {
|
|
|
393
393
|
{
|
|
394
394
|
type: 'heading',
|
|
395
395
|
level: 4,
|
|
396
|
-
text: '4. useDirection() context
|
|
396
|
+
text: '4. useDirection() context: the last resort',
|
|
397
397
|
},
|
|
398
398
|
{
|
|
399
399
|
type: 'prose',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// AUTO-GENERATED — do not edit manually.
|
|
4
4
|
// Source: packages/core/src/theme/tokens.stylex.ts
|
|
5
5
|
// Run: node scripts/generate-token-docs.mjs
|
|
6
|
-
// Total:
|
|
6
|
+
// Total: 188 tokens across 13 categories.
|
|
7
7
|
|
|
8
8
|
/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
|
|
9
9
|
|
|
@@ -561,6 +561,40 @@ export const docs = {
|
|
|
561
561
|
],
|
|
562
562
|
"previewType": "border-line"
|
|
563
563
|
},
|
|
564
|
+
{
|
|
565
|
+
"title": "Focus Tokens",
|
|
566
|
+
"content": [
|
|
567
|
+
{
|
|
568
|
+
"type": "prose",
|
|
569
|
+
"text": "The keyboard focus ring, shared by every component that draws one. Override these to restyle focus across the system."
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"type": "table",
|
|
573
|
+
"headers": [
|
|
574
|
+
"Token",
|
|
575
|
+
"Value"
|
|
576
|
+
],
|
|
577
|
+
"rows": [
|
|
578
|
+
[
|
|
579
|
+
"--focus-outline-width",
|
|
580
|
+
"2px"
|
|
581
|
+
],
|
|
582
|
+
[
|
|
583
|
+
"--focus-outline-style",
|
|
584
|
+
"solid"
|
|
585
|
+
],
|
|
586
|
+
[
|
|
587
|
+
"--focus-outline-color",
|
|
588
|
+
"var(--color-accent)"
|
|
589
|
+
],
|
|
590
|
+
[
|
|
591
|
+
"--focus-outline-offset",
|
|
592
|
+
"3px"
|
|
593
|
+
]
|
|
594
|
+
]
|
|
595
|
+
}
|
|
596
|
+
]
|
|
597
|
+
},
|
|
564
598
|
{
|
|
565
599
|
"title": "Radius Tokens",
|
|
566
600
|
"content": [
|
|
@@ -60,7 +60,7 @@ export const doc = {
|
|
|
60
60
|
{
|
|
61
61
|
type: 'prose',
|
|
62
62
|
text:
|
|
63
|
-
"Identity
|
|
63
|
+
"Identity, the integration's name and version, comes from the " +
|
|
64
64
|
"package's package.json, not from this manifest. The manifest only " +
|
|
65
65
|
'declares where the CLI finds each kind of artifact.',
|
|
66
66
|
},
|
|
@@ -74,6 +74,15 @@ function renderedClassLiterals() {
|
|
|
74
74
|
classes.add(m[1]);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
// A component can also name its popup SURFACE — an element usePopover
|
|
78
|
+
// owns, so the class cannot be rendered from the component itself.
|
|
79
|
+
// `surfaceTarget: 'x'` puts `astryx-x` on that surface, which makes it
|
|
80
|
+
// just as rendered as a direct themeProps() call.
|
|
81
|
+
const surfaceRe = /surfaceTarget:\s*'([^']+)'/g;
|
|
82
|
+
let sm;
|
|
83
|
+
while ((sm = surfaceRe.exec(text)) !== null) {
|
|
84
|
+
classes.add(sm[1]);
|
|
85
|
+
}
|
|
77
86
|
// Renamed targets emit their old name too, via themeProps'
|
|
78
87
|
// `legacyNames`. Those classes are just as rendered as the primary
|
|
79
88
|
// one, so a doc entry for the old name is still backed by real output.
|
package/package.json
CHANGED