@astryxdesign/cli 0.7.12 → 0.7.13-canary.9993f4c
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/api/component/__fixtures__/resolutions/modules.txt +1 -1
- package/api/doctor/_adapter.d.mts +17 -0
- package/api/doctor/_adapter.mjs +40 -0
- package/api/doctor/doctor.d.mts +34 -4
- package/api/doctor/doctor.mjs +108 -21
- package/api/icon/_adapter.d.mts +6 -3
- package/api/icon/_adapter.mjs +11 -3
- package/api/icon/icon.type.d.mts +22 -1
- package/api/icon/icon.type.mjs +12 -4
- package/api/icon/iconUse.doc.mjs +27 -7
- package/api/icon/list/list.mjs +29 -3
- package/api/icon/use/use.d.mts +7 -3
- package/api/icon/use/use.mjs +68 -18
- package/api/integration/validate-integration.d.mts +4 -0
- package/api/integration/validate-integration.mjs +32 -2
- package/api/integration/validate-integration.type.d.mts +15 -0
- package/api/integration/validate-integration.type.mjs +6 -1
- package/api/template/copy/copy.mjs +4 -2
- package/api/template/template.type.d.mts +42 -4
- package/api/template/template.type.mjs +9 -6
- package/assets/discover/official-packages.json +4 -0
- package/assets/docs/tree/icon-sets.doc.mjs +91 -7
- package/assets/icons/definitions/alignBottom.doc.mjs +9 -0
- package/assets/icons/definitions/alignJustify.doc.mjs +9 -0
- package/assets/icons/definitions/alignMiddle.doc.mjs +9 -0
- package/assets/icons/definitions/alignTop.doc.mjs +9 -0
- package/assets/icons/definitions/angle.doc.mjs +9 -0
- package/assets/icons/definitions/cloud.doc.mjs +9 -0
- package/assets/icons/definitions/collection.doc.mjs +9 -0
- package/assets/icons/definitions/contrast.doc.mjs +9 -0
- package/assets/icons/definitions/cornerRadius.doc.mjs +9 -0
- package/assets/icons/definitions/cut.doc.mjs +9 -0
- package/assets/icons/definitions/fingerprint.doc.mjs +9 -0
- package/assets/icons/definitions/fire.doc.mjs +9 -0
- package/assets/icons/definitions/frame.doc.mjs +9 -0
- package/assets/icons/definitions/gift.doc.mjs +9 -0
- package/assets/icons/definitions/group.doc.mjs +9 -0
- package/assets/icons/definitions/integration.doc.mjs +9 -0
- package/assets/icons/definitions/lowercase.doc.mjs +9 -0
- package/assets/icons/definitions/paddingHorizontal.doc.mjs +9 -0
- package/assets/icons/definitions/paddingPerSide.doc.mjs +9 -0
- package/assets/icons/definitions/paddingVertical.doc.mjs +9 -0
- package/assets/icons/definitions/panelBottom.doc.mjs +9 -0
- package/assets/icons/definitions/photoEffect.doc.mjs +9 -0
- package/assets/icons/definitions/radiusPerCorner.doc.mjs +9 -0
- package/assets/icons/definitions/rotateCounterclockwise.doc.mjs +9 -0
- package/assets/icons/definitions/tablet.doc.mjs +9 -0
- package/assets/icons/definitions/text.doc.mjs +9 -0
- package/assets/icons/definitions/textRegular.doc.mjs +9 -0
- package/assets/icons/definitions/transfer.doc.mjs +9 -0
- package/assets/icons/definitions/uppercase.doc.mjs +9 -0
- package/assets/icons/definitions/userRemove.doc.mjs +9 -0
- package/assets/templates/blocks/components/BaseTypeahead/BaseTypeaheadMenu.doc.mjs +23 -0
- package/assets/templates/blocks/components/BaseTypeahead/BaseTypeaheadMenu.tsx +199 -0
- package/assets/templates/blocks/components/ButtonGroup/ButtonGroupShowcase.doc.mjs +1 -0
- package/assets/templates/blocks/components/ChatSystemMessage/ChatSystemMessageStatusUpdates.doc.mjs +7 -2
- package/assets/templates/blocks/components/InternationalizationProvider/useDirectionHookUsage.doc.mjs +18 -0
- package/assets/templates/blocks/components/InternationalizationProvider/useDirectionHookUsage.tsx +101 -0
- package/assets/templates/pages/canvas-editor/template.doc.mjs +20 -0
- package/assets/templates/pages/editor/template.doc.mjs +1 -0
- package/assets/templates/pages/form-wizard-vertical/template.doc.mjs +1 -0
- package/assets/templates/pages/settings-dialog/template.doc.mjs +4 -0
- package/assets/templates/pages/shell-top-nav/template.doc.mjs +3 -0
- package/assets/templates/pages/table-collapsible/template.doc.mjs +1 -0
- package/assets/templates/pages/table-comparison/template.doc.mjs +1 -0
- package/authoring/config/config.doc.mjs +4 -4
- package/authoring/config/parse.d.mts +1 -1
- package/authoring/config/parse.mjs +3 -1
- package/authoring/config/type.ts +7 -5
- package/authoring/doctypes/template/template.doc.mjs +1 -1
- package/authoring/doctypes/template/type.ts +5 -3
- package/clients/cli/commands/experimental/icon.mjs +38 -9
- package/clients/cli/commands/experimental-icon-use.doc.mjs +9 -4
- package/clients/cli/commands/template.mjs +12 -3
- package/foundation/config/config-writer.d.mts +14 -0
- package/foundation/config/config-writer.mjs +73 -17
- package/foundation/discovery/icon-set-discovery.d.mts +77 -7
- package/foundation/discovery/icon-set-discovery.mjs +128 -42
- package/foundation/discovery/template-icon-notes.d.mts +46 -0
- package/foundation/discovery/template-icon-notes.mjs +94 -16
- package/foundation/discovery/template-icons.d.mts +247 -0
- package/foundation/discovery/template-icons.mjs +591 -37
- package/foundation/integrations/cli-requirement.d.mts +13 -4
- package/foundation/integrations/cli-requirement.mjs +18 -6
- package/package.json +14 -14
|
@@ -18,27 +18,47 @@ export const doc = {
|
|
|
18
18
|
isReady: true,
|
|
19
19
|
category: 'Tools - Canvas Editor',
|
|
20
20
|
icons: {
|
|
21
|
+
Aperture: 'photoEffect',
|
|
22
|
+
ArrowDownToLine: 'alignBottom',
|
|
23
|
+
ArrowUpToLine: 'alignTop',
|
|
24
|
+
Baseline: 'textRegular',
|
|
25
|
+
CaseLower: 'lowercase',
|
|
21
26
|
CaseSensitive: 'textCase',
|
|
27
|
+
CaseUpper: 'uppercase',
|
|
28
|
+
Contrast: 'contrast',
|
|
22
29
|
Download: 'download',
|
|
23
30
|
File: 'file',
|
|
24
31
|
FlipHorizontal2: 'flipHorizontal',
|
|
25
32
|
FlipVertical2: 'flipVertical',
|
|
33
|
+
FoldVertical: 'alignMiddle',
|
|
34
|
+
Frame: 'frame',
|
|
26
35
|
Grip: 'drag',
|
|
36
|
+
Group: 'group',
|
|
27
37
|
Image: 'image',
|
|
28
38
|
Italic: 'italic',
|
|
29
39
|
Lock: 'lock',
|
|
30
40
|
LockOpen: 'unlock',
|
|
31
41
|
Menu: 'menu',
|
|
32
42
|
Minus: 'remove',
|
|
43
|
+
PanelBottom: 'panelBottom',
|
|
33
44
|
PanelLeft: 'panelLeft',
|
|
45
|
+
PanelLeftRightDashed: 'paddingHorizontal',
|
|
34
46
|
PanelRight: 'panelRight',
|
|
47
|
+
PanelTopBottomDashed: 'paddingVertical',
|
|
35
48
|
Plus: 'add',
|
|
36
49
|
Redo2: 'redo',
|
|
50
|
+
RotateCcw: 'rotateCounterclockwise',
|
|
37
51
|
RotateCw: 'rotate',
|
|
52
|
+
Scan: 'cornerRadius',
|
|
53
|
+
SquareRoundCorner: 'radiusPerCorner',
|
|
54
|
+
SquareSquare: 'paddingPerSide',
|
|
38
55
|
Strikethrough: 'strikethrough',
|
|
39
56
|
TextAlignCenter: 'alignCenter',
|
|
40
57
|
TextAlignEnd: 'alignEnd',
|
|
58
|
+
TextAlignJustify: 'alignJustify',
|
|
41
59
|
TextAlignStart: 'alignStart',
|
|
60
|
+
TriangleRight: 'angle',
|
|
61
|
+
Type: 'text',
|
|
42
62
|
Underline: 'underline',
|
|
43
63
|
Undo2: 'undo',
|
|
44
64
|
X: 'close',
|
|
@@ -21,6 +21,7 @@ export const doc = {
|
|
|
21
21
|
ArrowDownTrayIcon: 'download',
|
|
22
22
|
ArrowUturnLeftIcon: 'undo',
|
|
23
23
|
AtSymbolIcon: 'mention',
|
|
24
|
+
Bars3BottomLeftIcon: 'panelLeft',
|
|
24
25
|
Bars3Icon: 'menu',
|
|
25
26
|
BeakerIcon: 'experiment',
|
|
26
27
|
BellIcon: 'notification',
|
|
@@ -40,6 +41,7 @@ export const doc = {
|
|
|
40
41
|
DocumentTextIcon: 'document',
|
|
41
42
|
EnvelopeIcon: 'mail',
|
|
42
43
|
EyeIcon: 'eye',
|
|
44
|
+
FingerPrintIcon: 'fingerprint',
|
|
43
45
|
FolderIcon: 'folder',
|
|
44
46
|
GlobeAltIcon: 'globe',
|
|
45
47
|
HandRaisedIcon: 'raiseHand',
|
|
@@ -50,7 +52,9 @@ export const doc = {
|
|
|
50
52
|
MagnifyingGlassIcon: 'search',
|
|
51
53
|
MapPinIcon: 'location',
|
|
52
54
|
MoonIcon: 'moon',
|
|
55
|
+
PaintBrushIcon: 'swatch',
|
|
53
56
|
PencilSquareIcon: 'compose',
|
|
57
|
+
PuzzlePieceIcon: 'integration',
|
|
54
58
|
QuestionMarkCircleIcon: 'info',
|
|
55
59
|
ReceiptPercentIcon: 'discount',
|
|
56
60
|
ShieldCheckIcon: 'shield',
|
|
@@ -20,7 +20,10 @@ export const doc = {
|
|
|
20
20
|
category: 'Shell - Top Nav',
|
|
21
21
|
icons: {
|
|
22
22
|
BoltIcon: 'bolt',
|
|
23
|
+
CloudIcon: 'cloud',
|
|
23
24
|
FaceSmileIcon: 'emoji',
|
|
25
|
+
FireIcon: 'fire',
|
|
26
|
+
GiftIcon: 'gift',
|
|
24
27
|
GlobeAltIcon: 'globe',
|
|
25
28
|
HomeModernIcon: 'home',
|
|
26
29
|
MagnifyingGlassIcon: 'search',
|
|
@@ -68,7 +68,7 @@ export const doc = {
|
|
|
68
68
|
description:
|
|
69
69
|
'Tell `{@command discover}` which integrations this project could add: an async function that returns a catalog. An integration can provide one too, as a `discover` named export from its manifest. Discover calls every source, yours first, and one that fails never hides the others. Discover only reads; your package manager installs.',
|
|
70
70
|
example:
|
|
71
|
-
|
|
71
|
+
'async ({signal, package: name, version}) => fetchCatalog({signal, name, version})',
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
name: 'experimental',
|
|
@@ -77,11 +77,11 @@ export const doc = {
|
|
|
77
77
|
fields: [
|
|
78
78
|
{
|
|
79
79
|
name: 'experimental.iconSet',
|
|
80
|
-
type: 'string',
|
|
80
|
+
type: 'string | string[]',
|
|
81
81
|
stability: 'experimental',
|
|
82
82
|
description:
|
|
83
|
-
"Experimental: may change or be removed in a patch release. The project's icon set, by set name or as `<package>/<set
|
|
84
|
-
example: "'@acme/outline-icons/outline'",
|
|
83
|
+
"Experimental: may change or be removed in a patch release. The project's main icon set, by set name or as `<package>/<set>`; or a list of them, the main set first and then backup sets in order. A copied template takes each icon from the first set in the list that has its meaning, and draws a placeholder the copied file declares when none has it. It is the opt-in: copied templates use these icons, and `astryx discover` and `astryx doctor` report icon sets, only when it is set. `astryx experimental icon use <main> --backup <set>` writes it. `astryx experimental icon` ranks the main set's icons first; without it, the only installed set is the project's there. `astryx search` never returns icons.",
|
|
84
|
+
example: "['@acme/outline-icons/outline', '@acme/brand-icons/brand']",
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
name: 'experimental.xle.components',
|
|
@@ -58,7 +58,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
58
58
|
default: z.ZodOptional<z.ZodBoolean>;
|
|
59
59
|
}, z.core.$strict>>>;
|
|
60
60
|
}, z.core.$strict>>;
|
|
61
|
-
iconSet: z.ZodOptional<z.ZodString
|
|
61
|
+
iconSet: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
62
62
|
}, z.core.$strict>>;
|
|
63
63
|
}, z.core.$strict>;
|
|
64
64
|
export {};
|
package/authoring/config/type.ts
CHANGED
|
@@ -123,12 +123,14 @@ export interface AstryxConfig {
|
|
|
123
123
|
components?: Record<string, XleComponent>;
|
|
124
124
|
};
|
|
125
125
|
/**
|
|
126
|
-
* Experimental icon sets (spec:AST-084): the project's icon set, a
|
|
127
|
-
* name (`"outline"`) or `"<package>/<set>"`, and the opt-in.
|
|
128
|
-
*
|
|
129
|
-
*
|
|
126
|
+
* Experimental icon sets (spec:AST-084): the project's main icon set, a
|
|
127
|
+
* set name (`"outline"`) or `"<package>/<set>"`, and the opt-in. A list
|
|
128
|
+
* (`["outline", "@acme/icons/brand"]`) names the main set first, then
|
|
129
|
+
* backup sets in order: a copied template takes each icon from the first
|
|
130
|
+
* set that has its meaning. Copied templates use these icons, and
|
|
131
|
+
* discover and Doctor report icon sets, only when it is set.
|
|
130
132
|
* @experimental May change or be removed in a patch release.
|
|
131
133
|
*/
|
|
132
|
-
iconSet?: string;
|
|
134
|
+
iconSet?: string | string[];
|
|
133
135
|
};
|
|
134
136
|
}
|
|
@@ -69,7 +69,7 @@ export const doc = {
|
|
|
69
69
|
type: 'Record<string, string>',
|
|
70
70
|
stability: 'experimental',
|
|
71
71
|
description:
|
|
72
|
-
"The icon definition each icon the source imports draws, such as `{TrashIcon: 'delete'}`: a named import is keyed by the name it imports (`{Trash as TrashIcon}` is `Trash`), a default import by its local name (`import DeleteIcon from …` is `DeleteIcon`). When the project sets `experimental.iconSet`, `astryx template` (shown or copied) replaces each declared icon with the
|
|
72
|
+
"The icon definition each icon the source imports draws, such as `{TrashIcon: 'delete'}`: a named import is keyed by the name it imports (`{Trash as TrashIcon}` is `Trash`), a default import by its local name (`import DeleteIcon from …` is `DeleteIcon`). When the project sets `experimental.iconSet`, `astryx template` (shown or copied) replaces each declared icon with the icon for that definition (`astryx docs cli/experimental/icon-definitions`) from the first set in the project's list that maps it, or with a placeholder the copied file declares when no set does, and reports every key as mapped, a placeholder, or kept; an undeclared import is left alone, and `astryx integration check` notes it (`template_icon_undeclared`, failing `--strict --next`) in a package whose templates declare `icons`. A name imported from two modules (`TrashIcon` from an outline and a solid module) needs one key, and each import keeps its variant when its module belongs to an installed set.",
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
name: 'isReady',
|
|
@@ -55,9 +55,11 @@ export interface BaseTemplateDoc extends AuthoredDocGraphFields {
|
|
|
55
55
|
* (`{TrashIcon: 'delete'}`), keyed by the name a named import reads
|
|
56
56
|
* (`{Trash as TrashIcon}` is `Trash`) or a default import's local name
|
|
57
57
|
* (`import DeleteIcon from …` is `DeleteIcon`). When the project has an
|
|
58
|
-
* icon set, each declared icon is replaced by the
|
|
59
|
-
* definition (`astryx docs cli/experimental/icon-definitions`)
|
|
60
|
-
*
|
|
58
|
+
* icon set, each declared icon is replaced by the icon for that
|
|
59
|
+
* definition (`astryx docs cli/experimental/icon-definitions`) from the
|
|
60
|
+
* first set in the project's list that maps it, or by a placeholder the
|
|
61
|
+
* copied file declares when no set does, and every key is reported as mapped, a
|
|
62
|
+
* placeholder, or kept. A name imported from two modules
|
|
61
63
|
* (`TrashIcon` from an outline and a solid module) needs one key.
|
|
62
64
|
* @experimental May change or be removed in a patch release. */
|
|
63
65
|
icons?: Record<string, string>;
|
|
@@ -256,10 +256,17 @@ export function registerExperimentalIcon(group, program) {
|
|
|
256
256
|
return answered;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
const {status, projectSet, reason, sets, withdrawn} =
|
|
259
|
+
const {status, projectSet, reason, sets, withdrawn, order, next} =
|
|
260
|
+
result.data;
|
|
260
261
|
emit(
|
|
261
262
|
section('Icon sets'),
|
|
262
|
-
record({
|
|
263
|
+
record({
|
|
264
|
+
status,
|
|
265
|
+
projectSet,
|
|
266
|
+
...(order.length > 1 ? {order: order.join(' > ')} : {}),
|
|
267
|
+
reason,
|
|
268
|
+
}),
|
|
269
|
+
...(next ? [text(`[warn] ${next}`)] : []),
|
|
263
270
|
...(sets.length > 0
|
|
264
271
|
? [
|
|
265
272
|
records(
|
|
@@ -283,6 +290,7 @@ export function registerExperimentalIcon(group, program) {
|
|
|
283
290
|
'definitions',
|
|
284
291
|
'own',
|
|
285
292
|
'missing',
|
|
293
|
+
'role',
|
|
286
294
|
'project',
|
|
287
295
|
'description',
|
|
288
296
|
],
|
|
@@ -346,21 +354,42 @@ export function registerExperimentalIcon(group, program) {
|
|
|
346
354
|
|
|
347
355
|
defineCommand(iconCli, iconUseCommand, {
|
|
348
356
|
fn: iconUseFn,
|
|
349
|
-
action: (
|
|
357
|
+
action: (
|
|
358
|
+
/** @type {string} */ set,
|
|
359
|
+
/** @type {{backup?: string[]}} */ options,
|
|
360
|
+
) =>
|
|
350
361
|
run(
|
|
351
|
-
() => iconUse(set, {cwd: process.cwd()}),
|
|
352
|
-
({data}) =>
|
|
362
|
+
() => iconUse(set, {cwd: process.cwd(), backups: options.backup ?? []}),
|
|
363
|
+
({data}) => {
|
|
364
|
+
/** @type {Array<{iconSet: string, set: string}>} */
|
|
365
|
+
const backups = data.backups;
|
|
366
|
+
const shown = (/** @type {string | string[]} */ value) =>
|
|
367
|
+
Array.isArray(value) ? JSON.stringify(value) : `"${value}"`;
|
|
368
|
+
const main = `"${data.iconSet}" (${data.set})`;
|
|
369
|
+
const order =
|
|
370
|
+
backups.length > 0
|
|
371
|
+
? `main set ${main}, then ${backups.map(each => `"${each.iconSet}" (${each.set})`).join(', then ')}`
|
|
372
|
+
: main;
|
|
353
373
|
emit(
|
|
354
374
|
text(
|
|
355
375
|
data.changed
|
|
356
|
-
? `[ok] experimental.iconSet is now
|
|
357
|
-
: `[ok] experimental.iconSet already names
|
|
376
|
+
? `[ok] experimental.iconSet is now ${order} in ${data.config}${data.created ? ', created' : ''}.`
|
|
377
|
+
: `[ok] experimental.iconSet already names ${order} in ${data.config}.`,
|
|
358
378
|
),
|
|
379
|
+
...(backups.length > 0
|
|
380
|
+
? [
|
|
381
|
+
text(
|
|
382
|
+
'A copied template takes each icon from the first set in this order that has its meaning.',
|
|
383
|
+
),
|
|
384
|
+
]
|
|
385
|
+
: []),
|
|
359
386
|
...(data.previous && data.changed
|
|
360
|
-
? [text(`It was
|
|
387
|
+
? [text(`It was ${shown(data.previous)}.`)]
|
|
361
388
|
: []),
|
|
389
|
+
...(data.next ? [text(`[warn] ${data.next}`)] : []),
|
|
362
390
|
...(data.warning ? [text(`[warn] ${data.warning}`)] : []),
|
|
363
|
-
)
|
|
391
|
+
);
|
|
392
|
+
},
|
|
364
393
|
),
|
|
365
394
|
});
|
|
366
395
|
|
|
@@ -9,13 +9,18 @@ export const doc = {
|
|
|
9
9
|
displayName: 'astryx experimental icon use',
|
|
10
10
|
namespace: 'cli/commands',
|
|
11
11
|
stability: 'experimental',
|
|
12
|
-
summary: "Choose the project's icon set",
|
|
12
|
+
summary: "Choose the project's main icon set and backups",
|
|
13
13
|
description:
|
|
14
|
-
"Writes `experimental.iconSet` in astryx.config, so copied templates and blocks use that set's icons.
|
|
14
|
+
"Writes `experimental.iconSet` in astryx.config, so copied templates and blocks use that set's icons. With `--backup`, it writes a list: the main set first, then each backup in the order given. A copied template takes each icon from the first set in the list that has its meaning. Every set must be installed. Experimental: it may change or be removed in a patch release.",
|
|
15
15
|
fn: 'iconUse',
|
|
16
16
|
args: [{name: 'set', param: 'set', required: true}],
|
|
17
|
+
options: [{flag: '--backup <set...>', param: 'options.backups'}],
|
|
17
18
|
examples: [
|
|
18
19
|
{label: 'Use a set', cli: 'astryx experimental icon use outline'},
|
|
20
|
+
{
|
|
21
|
+
label: 'Use a main set with a backup',
|
|
22
|
+
cli: 'astryx experimental icon use outline --backup @acme/brand-icons/brand',
|
|
23
|
+
},
|
|
19
24
|
{
|
|
20
25
|
label: 'Name its package',
|
|
21
26
|
cli: 'astryx experimental icon use @acme/icons/outline',
|
|
@@ -25,11 +30,11 @@ export const doc = {
|
|
|
25
30
|
exitCodes: [
|
|
26
31
|
{
|
|
27
32
|
code: 0,
|
|
28
|
-
when: 'experimental.iconSet is written, or already names the
|
|
33
|
+
when: 'experimental.iconSet is written, or already names the sets',
|
|
29
34
|
},
|
|
30
35
|
{
|
|
31
36
|
code: 1,
|
|
32
|
-
when: '
|
|
37
|
+
when: 'a set is not installed, two packages ship it, a set is named twice, the config cannot be updated (not a static object literal, or it spreads another object, which you then edit by hand), or the config is protected (a symbolic link, or marked generated, vendored, or ignored: `ERR_CODEMOD_PROTECTED`, naming the declaration)',
|
|
33
38
|
},
|
|
34
39
|
],
|
|
35
40
|
related: ['experimental icon', 'template'],
|
|
@@ -57,9 +57,10 @@ function demoContentNote(count, file) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* The icon report as text lines (AST-084 FR10): one line per mapped
|
|
61
|
-
* icon, or one line saying why nothing was mapped.
|
|
62
|
-
* has no icon set at all (no report: icon sets are
|
|
60
|
+
* The icon report as text lines (AST-084 FR10): one line per mapped,
|
|
61
|
+
* placeholder, or kept icon, or one line saying why nothing was mapped.
|
|
62
|
+
* Nothing when the project has no icon set at all (no report: icon sets are
|
|
63
|
+
* experimental). A placeholder line names its meaning and both fixes.
|
|
63
64
|
* @param {import('../../../api/template/template.type.mjs').TemplateIconsReport | undefined} icons
|
|
64
65
|
* @returns {string[]}
|
|
65
66
|
*/
|
|
@@ -74,10 +75,18 @@ function iconLines(icons) {
|
|
|
74
75
|
...icons.mapped.map(
|
|
75
76
|
each =>
|
|
76
77
|
`icons: mapped ${each.source.export} (${each.source.module}) -> ${each.target.export} (${each.target.module}) by ${each.by}` +
|
|
78
|
+
(icons.order ? ` from ${each.target.package}/${each.target.set}` : '') +
|
|
79
|
+
(each.variant
|
|
80
|
+
? `; ${each.variant.asked} look not in the set, drew ${each.variant.used ?? 'its default look'}`
|
|
81
|
+
: '') +
|
|
77
82
|
(each.dropped && each.dropped.length > 0
|
|
78
83
|
? `; dropped ${each.dropped.join(', ')} (size comes from CSS)`
|
|
79
84
|
: ''),
|
|
80
85
|
),
|
|
86
|
+
...(icons.placeholder ?? []).map(
|
|
87
|
+
each =>
|
|
88
|
+
`icons: placeholder ${each.source.export} (${each.source.module}) -> ${each.target.local}: no set in your list has "${each.definitions.join('", "')}", so the file draws a placeholder it declares. Fix: add a backup set that has it, \`${each.fixes[1]}\`; or, if you author your main set, map it there, \`${each.fixes[0]}\`.`,
|
|
89
|
+
),
|
|
81
90
|
...icons.kept.map(
|
|
82
91
|
each =>
|
|
83
92
|
`icons: kept ${each.export}${each.module == null ? '' : ` (${each.module})`}: ${each.reason}` +
|
|
@@ -14,6 +14,20 @@ export function setConfigString(projectDir: string, key: string, value: string):
|
|
|
14
14
|
previous: string | null;
|
|
15
15
|
changed: boolean;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Set `key` to `value`, a string or a list of strings, in the project's
|
|
19
|
+
* astryx.config.
|
|
20
|
+
* @param {string} projectDir
|
|
21
|
+
* @param {string} key a top-level key, or a dotted path (`experimental.iconSet`)
|
|
22
|
+
* @param {string | readonly string[]} value
|
|
23
|
+
* @returns {{file: string, created: boolean, previous: string | string[] | null, changed: boolean}}
|
|
24
|
+
*/
|
|
25
|
+
export function setConfigValue(projectDir: string, key: string, value: string | readonly string[]): {
|
|
26
|
+
file: string;
|
|
27
|
+
created: boolean;
|
|
28
|
+
previous: string | string[] | null;
|
|
29
|
+
changed: boolean;
|
|
30
|
+
};
|
|
17
31
|
/** A protected astryx.config the writer refused to touch. */
|
|
18
32
|
export class ConfigProtectedError extends Error {
|
|
19
33
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @file Write one string field of a project's
|
|
4
|
+
* @file Write one string field, or one list of strings, in a project's
|
|
5
|
+
* astryx.config.
|
|
5
6
|
*
|
|
6
7
|
* The field is a top-level key or a dotted path into nested object literals
|
|
7
8
|
* (`experimental.iconSet`); a missing object on the path is created.
|
|
@@ -18,7 +19,8 @@
|
|
|
18
19
|
* `ERR_CODEMOD_PROTECTED` naming the protecting declaration, and an unreadable
|
|
19
20
|
* protection source fails with `ERR_CODEMOD_PROTECTION_SOURCE`.
|
|
20
21
|
*
|
|
21
|
-
* @input A project directory, a field name, and its string
|
|
22
|
+
* @input A project directory, a field name, and its value: a string or a
|
|
23
|
+
* list of strings
|
|
22
24
|
* @output The config file written and the value it replaced
|
|
23
25
|
* @position packages/cli/foundation/config — read by `astryx experimental icon use`
|
|
24
26
|
*/
|
|
@@ -87,6 +89,47 @@ function propertyNamed(property, name) {
|
|
|
87
89
|
);
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
/** @param {string} text */
|
|
93
|
+
const quote = text => `'${text.replace(/\\/gu, '\\\\').replace(/'/gu, "\\'")}'`;
|
|
94
|
+
|
|
95
|
+
/** @param {string | readonly string[]} value */
|
|
96
|
+
const written = value =>
|
|
97
|
+
typeof value === 'string'
|
|
98
|
+
? quote(value)
|
|
99
|
+
: `[${value.map(each => quote(each)).join(', ')}]`;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The string, or list of strings, a config property holds; null for any
|
|
103
|
+
* other value.
|
|
104
|
+
* @param {any} node
|
|
105
|
+
* @returns {string | string[] | null}
|
|
106
|
+
*/
|
|
107
|
+
function literalValue(node) {
|
|
108
|
+
if (node?.type === 'StringLiteral' || node?.type === 'Literal') {
|
|
109
|
+
return typeof node.value === 'string' ? node.value : null;
|
|
110
|
+
}
|
|
111
|
+
if (node?.type === 'ArrayExpression') {
|
|
112
|
+
const items = node.elements.map((/** @type {any} */ each) =>
|
|
113
|
+
each?.type === 'StringLiteral' ||
|
|
114
|
+
(each?.type === 'Literal' && typeof each.value === 'string')
|
|
115
|
+
? String(each.value)
|
|
116
|
+
: null,
|
|
117
|
+
);
|
|
118
|
+
return items.every((/** @type {string | null} */ each) => each != null)
|
|
119
|
+
? /** @type {string[]} */ (items)
|
|
120
|
+
: null;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {string | string[] | null} a
|
|
127
|
+
* @param {string | readonly string[]} b
|
|
128
|
+
*/
|
|
129
|
+
function sameValue(a, b) {
|
|
130
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
131
|
+
}
|
|
132
|
+
|
|
90
133
|
/**
|
|
91
134
|
* Set `key` to `value` in the project's astryx.config.
|
|
92
135
|
* @param {string} projectDir
|
|
@@ -95,6 +138,22 @@ function propertyNamed(property, name) {
|
|
|
95
138
|
* @returns {{file: string, created: boolean, previous: string | null, changed: boolean}}
|
|
96
139
|
*/
|
|
97
140
|
export function setConfigString(projectDir, key, value) {
|
|
141
|
+
const result = setConfigValue(projectDir, key, value);
|
|
142
|
+
return {
|
|
143
|
+
...result,
|
|
144
|
+
previous: typeof result.previous === 'string' ? result.previous : null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Set `key` to `value`, a string or a list of strings, in the project's
|
|
150
|
+
* astryx.config.
|
|
151
|
+
* @param {string} projectDir
|
|
152
|
+
* @param {string} key a top-level key, or a dotted path (`experimental.iconSet`)
|
|
153
|
+
* @param {string | readonly string[]} value
|
|
154
|
+
* @returns {{file: string, created: boolean, previous: string | string[] | null, changed: boolean}}
|
|
155
|
+
*/
|
|
156
|
+
export function setConfigValue(projectDir, key, value) {
|
|
98
157
|
const keys = key.split('.');
|
|
99
158
|
const leaf = /** @type {string} */ (keys[keys.length - 1]);
|
|
100
159
|
const existing = CONFIG_BASENAMES.map(name =>
|
|
@@ -103,7 +162,6 @@ export function setConfigString(projectDir, key, value) {
|
|
|
103
162
|
if (!existing) {
|
|
104
163
|
const file = path.join(projectDir, 'astryx.config.mjs');
|
|
105
164
|
assertWritable(projectDir, file);
|
|
106
|
-
const quoted = `'${value.replace(/\\/gu, '\\\\').replace(/'/gu, "\\'")}'`;
|
|
107
165
|
// Each key on the path opens one nested object: `experimental: {`.
|
|
108
166
|
const open = keys
|
|
109
167
|
.slice(0, -1)
|
|
@@ -116,7 +174,7 @@ export function setConfigString(projectDir, key, value) {
|
|
|
116
174
|
.join('');
|
|
117
175
|
writeAtomically(
|
|
118
176
|
file,
|
|
119
|
-
`export default {\n${open}${' '.repeat(keys.length)}${leaf}: ${
|
|
177
|
+
`export default {\n${open}${' '.repeat(keys.length)}${leaf}: ${written(value)},\n${close}};\n`,
|
|
120
178
|
);
|
|
121
179
|
return {file, created: true, previous: null, changed: true};
|
|
122
180
|
}
|
|
@@ -127,7 +185,7 @@ export function setConfigString(projectDir, key, value) {
|
|
|
127
185
|
let object = findManifestObject(j, ast);
|
|
128
186
|
const refuse = () =>
|
|
129
187
|
new Error(
|
|
130
|
-
`Could not update ${path.basename(existing)} safely: its default export is not a static object literal. Set "${key}:
|
|
188
|
+
`Could not update ${path.basename(existing)} safely: its default export is not a static object literal. Set "${key}: ${written(value)}" in it yourself.`,
|
|
131
189
|
);
|
|
132
190
|
if (!object) throw refuse();
|
|
133
191
|
// A spread on the path can hold the same keys: a key added beside it would
|
|
@@ -137,7 +195,7 @@ export function setConfigString(projectDir, key, value) {
|
|
|
137
195
|
const assertNoSpread = (holder, where) => {
|
|
138
196
|
if (holder.properties.some((/** @type {any} */ each) => isSpread(each))) {
|
|
139
197
|
throw new Error(
|
|
140
|
-
`Could not update ${path.basename(existing)} safely: ${where} spreads another object (\`...\`), whose keys a written "${key}" could replace. Set "${key}:
|
|
198
|
+
`Could not update ${path.basename(existing)} safely: ${where} spreads another object (\`...\`), whose keys a written "${key}" could replace. Set "${key}: ${written(value)}" in it yourself.`,
|
|
141
199
|
);
|
|
142
200
|
}
|
|
143
201
|
};
|
|
@@ -160,22 +218,20 @@ export function setConfigString(projectDir, key, value) {
|
|
|
160
218
|
const property = object.properties.find((/** @type {any} */ each) =>
|
|
161
219
|
propertyNamed(each, leaf),
|
|
162
220
|
);
|
|
163
|
-
|
|
221
|
+
const node =
|
|
222
|
+
typeof value === 'string'
|
|
223
|
+
? j.stringLiteral(value)
|
|
224
|
+
: j.arrayExpression(value.map(each => j.stringLiteral(each)));
|
|
225
|
+
/** @type {string | string[] | null} */
|
|
164
226
|
let previous = null;
|
|
165
227
|
if (property) {
|
|
166
|
-
|
|
167
|
-
previous
|
|
168
|
-
current?.type === 'StringLiteral' || current?.type === 'Literal'
|
|
169
|
-
? String(current.value)
|
|
170
|
-
: null;
|
|
171
|
-
if (previous === value) {
|
|
228
|
+
previous = literalValue(property.value);
|
|
229
|
+
if (sameValue(previous, value)) {
|
|
172
230
|
return {file: existing, created: false, previous, changed: false};
|
|
173
231
|
}
|
|
174
|
-
property.value =
|
|
232
|
+
property.value = node;
|
|
175
233
|
} else {
|
|
176
|
-
object.properties.push(
|
|
177
|
-
j.objectProperty(j.identifier(leaf), j.stringLiteral(value)),
|
|
178
|
-
);
|
|
234
|
+
object.properties.push(j.objectProperty(j.identifier(leaf), node));
|
|
179
235
|
}
|
|
180
236
|
let next = ast.toSource({quote: 'single', reuseWhitespace: true});
|
|
181
237
|
if (source.endsWith('\n') && !next.endsWith('\n')) next += '\n';
|