@sia-ui/cli 0.1.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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/diff.mjs +91 -0
- package/dist/index.js +325 -0
- package/dist/lifecycle.mjs +242 -0
- package/dist/lock.mjs +92 -0
- package/dist/registry/package/components/Accordion/index.tsx +172 -0
- package/dist/registry/package/components/Accordion/styles.css +97 -0
- package/dist/registry/package/components/Alert/index.tsx +31 -0
- package/dist/registry/package/components/Alert/styles.css +12 -0
- package/dist/registry/package/components/AmountDisplay/index.tsx +11 -0
- package/dist/registry/package/components/AmountDisplay/styles.css +9 -0
- package/dist/registry/package/components/AppShell/index.tsx +6 -0
- package/dist/registry/package/components/AppShell/styles.css +7 -0
- package/dist/registry/package/components/AspectRatio/index.tsx +29 -0
- package/dist/registry/package/components/AspectRatio/styles.css +15 -0
- package/dist/registry/package/components/AsyncState/index.tsx +22 -0
- package/dist/registry/package/components/AsyncState/styles.css +1 -0
- package/dist/registry/package/components/AuditMeta/index.tsx +44 -0
- package/dist/registry/package/components/AuditMeta/styles.css +12 -0
- package/dist/registry/package/components/AuthLayout/index.tsx +39 -0
- package/dist/registry/package/components/AuthLayout/styles.css +6 -0
- package/dist/registry/package/components/Autocomplete/index.tsx +8 -0
- package/dist/registry/package/components/Avatar/index.tsx +23 -0
- package/dist/registry/package/components/Avatar/styles.css +5 -0
- package/dist/registry/package/components/Badge/index.tsx +62 -0
- package/dist/registry/package/components/Badge/styles.css +26 -0
- package/dist/registry/package/components/Breadcrumb/index.tsx +131 -0
- package/dist/registry/package/components/Breadcrumb/styles.css +76 -0
- package/dist/registry/package/components/Button/index.tsx +91 -0
- package/dist/registry/package/components/Button/styles.css +111 -0
- package/dist/registry/package/components/Calendar/index.tsx +192 -0
- package/dist/registry/package/components/Card/index.tsx +8 -0
- package/dist/registry/package/components/Card/styles.css +4 -0
- package/dist/registry/package/components/Chart/index.tsx +437 -0
- package/dist/registry/package/components/Chart/styles.css +182 -0
- package/dist/registry/package/components/Checkbox/index.tsx +193 -0
- package/dist/registry/package/components/Checkbox/styles.css +39 -0
- package/dist/registry/package/components/ColorPicker/index.tsx +6 -0
- package/dist/registry/package/components/ColorPicker/styles.css +2 -0
- package/dist/registry/package/components/CommandPalette/index.tsx +7 -0
- package/dist/registry/package/components/CommandPalette/styles.css +8 -0
- package/dist/registry/package/components/ConfirmDialog/index.tsx +24 -0
- package/dist/registry/package/components/Container/index.tsx +11 -0
- package/dist/registry/package/components/Container/styles.css +1 -0
- package/dist/registry/package/components/Countdown/index.tsx +3 -0
- package/dist/registry/package/components/CrudPage/index.tsx +9 -0
- package/dist/registry/package/components/CrudPage/styles.css +1 -0
- package/dist/registry/package/components/CurrencyInput/index.tsx +15 -0
- package/dist/registry/package/components/CurrencyInput/styles.css +1 -0
- package/dist/registry/package/components/DataTable/index.tsx +240 -0
- package/dist/registry/package/components/DataTable/styles.css +98 -0
- package/dist/registry/package/components/DatePicker/index.tsx +102 -0
- package/dist/registry/package/components/DateRangeFilter/index.tsx +8 -0
- package/dist/registry/package/components/DateRangeFilter/styles.css +4 -0
- package/dist/registry/package/components/DateRangePicker/index.tsx +36 -0
- package/dist/registry/package/components/DateTimePicker/index.tsx +32 -0
- package/dist/registry/package/components/Descriptions/index.tsx +7 -0
- package/dist/registry/package/components/Descriptions/styles.css +8 -0
- package/dist/registry/package/components/Divider/index.tsx +12 -0
- package/dist/registry/package/components/Divider/styles.css +3 -0
- package/dist/registry/package/components/Drawer/context.ts +14 -0
- package/dist/registry/package/components/Drawer/index.tsx +209 -0
- package/dist/registry/package/components/Drawer/styles.css +151 -0
- package/dist/registry/package/components/DropdownMenu/index.tsx +350 -0
- package/dist/registry/package/components/DropdownMenu/styles.css +100 -0
- package/dist/registry/package/components/DurationDisplay/index.tsx +3 -0
- package/dist/registry/package/components/EmptyState/index.tsx +23 -0
- package/dist/registry/package/components/EmptyState/styles.css +6 -0
- package/dist/registry/package/components/EntityMeta/index.tsx +65 -0
- package/dist/registry/package/components/EntityMeta/styles.css +9 -0
- package/dist/registry/package/components/ErrorState/index.tsx +30 -0
- package/dist/registry/package/components/ErrorState/styles.css +6 -0
- package/dist/registry/package/components/EventCalendar/index.tsx +8 -0
- package/dist/registry/package/components/EventCalendar/styles.css +8 -0
- package/dist/registry/package/components/Field/index.tsx +664 -0
- package/dist/registry/package/components/Field/styles.css +14 -0
- package/dist/registry/package/components/FileUpload/index.tsx +68 -0
- package/dist/registry/package/components/FileUpload/styles.css +4 -0
- package/dist/registry/package/components/FiltersBar/index.tsx +20 -0
- package/dist/registry/package/components/FiltersBar/styles.css +6 -0
- package/dist/registry/package/components/Form/index.tsx +211 -0
- package/dist/registry/package/components/Form/styles.css +80 -0
- package/dist/registry/package/components/FormField/index.tsx +2 -0
- package/dist/registry/package/components/HoverCard/index.tsx +121 -0
- package/dist/registry/package/components/HoverCard/styles.css +26 -0
- package/dist/registry/package/components/IconButton/index.tsx +27 -0
- package/dist/registry/package/components/IconButton/styles.css +7 -0
- package/dist/registry/package/components/Icons/index.tsx +185 -0
- package/dist/registry/package/components/ImageUpload/index.tsx +6 -0
- package/dist/registry/package/components/ImageUpload/styles.css +4 -0
- package/dist/registry/package/components/Input/index.tsx +103 -0
- package/dist/registry/package/components/Input/styles.css +8 -0
- package/dist/registry/package/components/InputNumber/index.tsx +196 -0
- package/dist/registry/package/components/InputNumber/styles.css +54 -0
- package/dist/registry/package/components/JsonEditor/index.tsx +6 -0
- package/dist/registry/package/components/JsonEditor/styles.css +2 -0
- package/dist/registry/package/components/KpiGrid/index.tsx +11 -0
- package/dist/registry/package/components/KpiGrid/styles.css +5 -0
- package/dist/registry/package/components/Label/index.tsx +43 -0
- package/dist/registry/package/components/Label/styles.css +25 -0
- package/dist/registry/package/components/MarkdownEditor/index.tsx +5 -0
- package/dist/registry/package/components/MiniCalendar/index.tsx +3 -0
- package/dist/registry/package/components/Modal/index.tsx +104 -0
- package/dist/registry/package/components/Modal/styles.css +20 -0
- package/dist/registry/package/components/MonthPicker/index.tsx +5 -0
- package/dist/registry/package/components/MultiSelect/index.tsx +133 -0
- package/dist/registry/package/components/MultiSelect/styles.css +3 -0
- package/dist/registry/package/components/OtpInput/index.tsx +5 -0
- package/dist/registry/package/components/OtpInput/styles.css +2 -0
- package/dist/registry/package/components/Overlay/index.tsx +140 -0
- package/dist/registry/package/components/Overlay/styles.css +1 -0
- package/dist/registry/package/components/PageHeader/index.tsx +24 -0
- package/dist/registry/package/components/PageHeader/styles.css +5 -0
- package/dist/registry/package/components/Pagination/index.tsx +155 -0
- package/dist/registry/package/components/Pagination/styles.css +103 -0
- package/dist/registry/package/components/PasswordInput/index.tsx +125 -0
- package/dist/registry/package/components/PasswordInput/styles.css +58 -0
- package/dist/registry/package/components/PermissionGate/index.tsx +3 -0
- package/dist/registry/package/components/PhoneInput/index.tsx +189 -0
- package/dist/registry/package/components/PhoneInput/styles.css +35 -0
- package/dist/registry/package/components/Popover/index.tsx +12 -0
- package/dist/registry/package/components/Popover/styles.css +12 -0
- package/dist/registry/package/components/Progress/index.tsx +149 -0
- package/dist/registry/package/components/Progress/styles.css +101 -0
- package/dist/registry/package/components/QrCode/index.tsx +6 -0
- package/dist/registry/package/components/QrCode/styles.css +5 -0
- package/dist/registry/package/components/RadioGroup/index.tsx +29 -0
- package/dist/registry/package/components/RadioGroup/styles.css +20 -0
- package/dist/registry/package/components/Rating/index.tsx +82 -0
- package/dist/registry/package/components/Rating/styles.css +41 -0
- package/dist/registry/package/components/ReferenceSelect/index.tsx +7 -0
- package/dist/registry/package/components/ReferenceSelect/styles.css +2 -0
- package/dist/registry/package/components/RelativeTime/index.tsx +22 -0
- package/dist/registry/package/components/RelativeTime/styles.css +1 -0
- package/dist/registry/package/components/RequireSession/index.tsx +127 -0
- package/dist/registry/package/components/RequireSession/styles.css +13 -0
- package/dist/registry/package/components/ResizablePanel/index.tsx +6 -0
- package/dist/registry/package/components/ResizablePanel/styles.css +5 -0
- package/dist/registry/package/components/RichTextEditor/index.tsx +5 -0
- package/dist/registry/package/components/RichTextEditor/styles.css +2 -0
- package/dist/registry/package/components/ScrollArea/index.tsx +114 -0
- package/dist/registry/package/components/ScrollArea/styles.css +74 -0
- package/dist/registry/package/components/SearchInput/index.tsx +121 -0
- package/dist/registry/package/components/SearchInput/styles.css +6 -0
- package/dist/registry/package/components/Select/index.tsx +324 -0
- package/dist/registry/package/components/Select/styles.css +58 -0
- package/dist/registry/package/components/Sidebar/index.tsx +232 -0
- package/dist/registry/package/components/Sidebar/styles.css +140 -0
- package/dist/registry/package/components/Skeleton/index.tsx +21 -0
- package/dist/registry/package/components/Skeleton/styles.css +2 -0
- package/dist/registry/package/components/Slider/index.tsx +193 -0
- package/dist/registry/package/components/Slider/styles.css +53 -0
- package/dist/registry/package/components/Spinner/index.tsx +28 -0
- package/dist/registry/package/components/Spinner/styles.css +8 -0
- package/dist/registry/package/components/StatCard/index.tsx +15 -0
- package/dist/registry/package/components/StatCard/styles.css +3 -0
- package/dist/registry/package/components/Statistic/index.tsx +23 -0
- package/dist/registry/package/components/Statistic/styles.css +6 -0
- package/dist/registry/package/components/Steps/index.tsx +108 -0
- package/dist/registry/package/components/Steps/styles.css +138 -0
- package/dist/registry/package/components/Switch/index.tsx +46 -0
- package/dist/registry/package/components/Switch/styles.css +26 -0
- package/dist/registry/package/components/Tabs/index.tsx +44 -0
- package/dist/registry/package/components/Tabs/styles.css +41 -0
- package/dist/registry/package/components/TagsInput/index.tsx +6 -0
- package/dist/registry/package/components/TagsInput/styles.css +2 -0
- package/dist/registry/package/components/Textarea/index.tsx +21 -0
- package/dist/registry/package/components/Textarea/styles.css +7 -0
- package/dist/registry/package/components/TimePicker/index.tsx +76 -0
- package/dist/registry/package/components/TimePicker/styles.css +13 -0
- package/dist/registry/package/components/TimeRangePicker/index.tsx +32 -0
- package/dist/registry/package/components/TimeRangePicker/styles.css +1 -0
- package/dist/registry/package/components/Timeline/index.tsx +8 -0
- package/dist/registry/package/components/Timeline/styles.css +12 -0
- package/dist/registry/package/components/Timer/index.tsx +9 -0
- package/dist/registry/package/components/Toast/index.tsx +426 -0
- package/dist/registry/package/components/Toast/styles.css +175 -0
- package/dist/registry/package/components/Toggle/index.tsx +181 -0
- package/dist/registry/package/components/Toggle/styles.css +124 -0
- package/dist/registry/package/components/Tooltip/index.tsx +163 -0
- package/dist/registry/package/components/Tooltip/styles.css +57 -0
- package/dist/registry/package/components/Tour/index.tsx +8 -0
- package/dist/registry/package/components/Tour/styles.css +6 -0
- package/dist/registry/package/components/Transfer/index.tsx +7 -0
- package/dist/registry/package/components/Transfer/styles.css +6 -0
- package/dist/registry/package/components/Tree/index.tsx +8 -0
- package/dist/registry/package/components/Tree/styles.css +6 -0
- package/dist/registry/package/components/TreeSelect/index.tsx +10 -0
- package/dist/registry/package/components/TreeSelect/styles.css +3 -0
- package/dist/registry/package/components/Typography/index.tsx +27 -0
- package/dist/registry/package/components/Typography/styles.css +16 -0
- package/dist/registry/package/components/UnsavedChangesGuard/index.tsx +4 -0
- package/dist/registry/package/components/WeekPicker/index.tsx +5 -0
- package/dist/registry/package/components/YearPicker/index.tsx +3 -0
- package/dist/registry/package/lib/form-adapter-rhf.tsx +141 -0
- package/dist/registry/registry.json +1780 -0
- package/dist/registry/styles/sia-ui.css +361 -0
- package/dist/source.mjs +93 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan Mbella
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @sia-ui/cli
|
|
2
|
+
|
|
3
|
+
Installer les composants SIA UI dans une application, comme shadcn/ui.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx sia-ui init
|
|
7
|
+
npx sia-ui add data-table crud-page
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
init écrit sia-ui.json et la feuille de style
|
|
12
|
+
list liste les composants, [pkg] = servi par le package
|
|
13
|
+
add <nom...> installe, avec ses dépendances de registre
|
|
14
|
+
add <nom> --overwrite écrase une copie existante
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Les chemins d'installation viennent de `sia-ui.json` :
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{ "components": "src/components", "lib": "src/lib", "styles": "src/styles/sia-ui.css" }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
La CLI copie le fichier source tel quel et **annonce les paquets npm à
|
|
24
|
+
installer** plutôt que de les installer d'office :
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
add src/components/DataTable/index.tsx
|
|
28
|
+
add src/components/DataTable/styles.css
|
|
29
|
+
|
|
30
|
+
À installer :
|
|
31
|
+
pnpm add @sia-ui/headless @sia-ui/react @sia-ui/tokens @sia-ui/utils
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Place dans SIA UI
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
@sia-ui/tokens valeurs : couleurs, espacements, mouvement, vocabulaire
|
|
38
|
+
@sia-ui/utils fonctions sans dépendance
|
|
39
|
+
@sia-ui/api client HTTP — indépendant de l'interface
|
|
40
|
+
@sia-ui/headless comportement, avec React, sans plateforme
|
|
41
|
+
@sia-ui/react primitives de runtime DOM
|
|
42
|
+
@sia-ui/react-web composants DOM
|
|
43
|
+
@sia-ui/react-native composants natifs, à venir
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Une chose s'écrit au niveau le plus bas où elle a du sens, et une seule fois.
|
|
47
|
+
Voir [la documentation](https://github.com/BeatJo/sia-ui/tree/main/docs).
|
|
48
|
+
|
|
49
|
+
## Licence
|
|
50
|
+
|
|
51
|
+
MIT
|
package/dist/diff.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Un diff unifié, sans dépendance.
|
|
3
|
+
*
|
|
4
|
+
* La règle du dépôt vaut aussi pour l'outillage : SIA UI se branche sur les
|
|
5
|
+
* librairies d'une application, il n'en impose aucune. Trente lignes de plus
|
|
6
|
+
* grande sous-séquence commune valent mieux qu'un paquet de plus dans le
|
|
7
|
+
* `node_modules` de qui installe la CLI.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Les indices appariés entre deux suites, par programmation dynamique. */
|
|
11
|
+
function sousSequenceCommune(a, b) {
|
|
12
|
+
const n = a.length;
|
|
13
|
+
const m = b.length;
|
|
14
|
+
const table = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
15
|
+
|
|
16
|
+
for (let i = n - 1; i >= 0; i -= 1) {
|
|
17
|
+
for (let j = m - 1; j >= 0; j -= 1) {
|
|
18
|
+
table[i][j] =
|
|
19
|
+
a[i] === b[j]
|
|
20
|
+
? table[i + 1][j + 1] + 1
|
|
21
|
+
: Math.max(table[i + 1][j], table[i][j + 1]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const paires = [];
|
|
26
|
+
let i = 0;
|
|
27
|
+
let j = 0;
|
|
28
|
+
while (i < n && j < m) {
|
|
29
|
+
if (a[i] === b[j]) {
|
|
30
|
+
paires.push([i, j]);
|
|
31
|
+
i += 1;
|
|
32
|
+
j += 1;
|
|
33
|
+
} else if (table[i + 1][j] >= table[i][j + 1]) {
|
|
34
|
+
i += 1;
|
|
35
|
+
} else {
|
|
36
|
+
j += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return paires;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Les lignes changées entre deux textes, avec leur contexte.
|
|
44
|
+
*
|
|
45
|
+
* Rend une liste de `{ signe, ligne }` où le signe vaut ` `, `-` ou `+`.
|
|
46
|
+
*/
|
|
47
|
+
export function lignesDiff(avant, apres, contexte = 2) {
|
|
48
|
+
const a = avant.split(/\r?\n/);
|
|
49
|
+
const b = apres.split(/\r?\n/);
|
|
50
|
+
const paires = sousSequenceCommune(a, b);
|
|
51
|
+
|
|
52
|
+
const marques = [];
|
|
53
|
+
let i = 0;
|
|
54
|
+
let j = 0;
|
|
55
|
+
|
|
56
|
+
for (const [ia, ib] of [...paires, [a.length, b.length]]) {
|
|
57
|
+
while (i < ia) marques.push({ signe: "-", ligne: a[i++] });
|
|
58
|
+
while (j < ib) marques.push({ signe: "+", ligne: b[j++] });
|
|
59
|
+
if (ia < a.length) {
|
|
60
|
+
marques.push({ signe: " ", ligne: a[ia] });
|
|
61
|
+
i = ia + 1;
|
|
62
|
+
j = ib + 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// On ne garde que ce qui entoure un changement : un fichier de trois cents
|
|
67
|
+
// lignes dont une bouge ne doit pas en afficher trois cents.
|
|
68
|
+
const garder = new Set();
|
|
69
|
+
marques.forEach((m, index) => {
|
|
70
|
+
if (m.signe === " ") return;
|
|
71
|
+
for (let k = index - contexte; k <= index + contexte; k += 1) {
|
|
72
|
+
if (k >= 0 && k < marques.length) garder.add(k);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const sortie = [];
|
|
77
|
+
let trou = false;
|
|
78
|
+
marques.forEach((m, index) => {
|
|
79
|
+
if (garder.has(index)) {
|
|
80
|
+
if (trou) {
|
|
81
|
+
sortie.push({ signe: "~", ligne: "…" });
|
|
82
|
+
trou = false;
|
|
83
|
+
}
|
|
84
|
+
sortie.push(m);
|
|
85
|
+
} else if (sortie.length > 0) {
|
|
86
|
+
trou = true;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return sortie;
|
|
91
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import {
|
|
5
|
+
LOCK_FILE,
|
|
6
|
+
ecrireLock,
|
|
7
|
+
empreinte,
|
|
8
|
+
lireLock,
|
|
9
|
+
noter,
|
|
10
|
+
} from "./lock.mjs";
|
|
11
|
+
import { diff, doctor, outdated, remove } from "./lifecycle.mjs";
|
|
12
|
+
import { choisirSource } from "./source.mjs";
|
|
13
|
+
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const registryRoot = resolve(here, "registry");
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CONFIG = {
|
|
18
|
+
components: "src/components",
|
|
19
|
+
lib: "src/lib",
|
|
20
|
+
styles: "src/styles/sia-ui.css",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Les arguments, avec les options à valeur.
|
|
25
|
+
*
|
|
26
|
+
* `--registry <url>` en prend une : la traiter comme un drapeau nu ferait
|
|
27
|
+
* passer l'URL pour un nom de composant, et la CLI répondrait « Composant
|
|
28
|
+
* inconnu : https://… ».
|
|
29
|
+
*/
|
|
30
|
+
function analyser(argv) {
|
|
31
|
+
const flags = new Set();
|
|
32
|
+
const valeurs = {};
|
|
33
|
+
const args = [];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
36
|
+
const brut = argv[i];
|
|
37
|
+
if (!brut.startsWith("--")) {
|
|
38
|
+
args.push(brut);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [nom, valeurCollee] = brut.split(/=(.*)/s);
|
|
43
|
+
if (nom === "--registry") {
|
|
44
|
+
valeurs.registry = valeurCollee ?? argv[++i];
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
flags.add(nom);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { flags, valeurs, args };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [, , command = "help", ...rest] = process.argv;
|
|
54
|
+
const { flags, valeurs, args } = analyser(rest);
|
|
55
|
+
|
|
56
|
+
async function exists(path) {
|
|
57
|
+
try {
|
|
58
|
+
await access(path);
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* La configuration du projet, si elle existe.
|
|
67
|
+
*
|
|
68
|
+
* `init` l'écrit, `add` la lit : sans ça la CLI installait toujours dans
|
|
69
|
+
* `src/`, quelle que soit l'arborescence du projet.
|
|
70
|
+
*/
|
|
71
|
+
async function readConfig(cwd) {
|
|
72
|
+
const path = join(cwd, "sia-ui.json");
|
|
73
|
+
if (!(await exists(path))) return DEFAULT_CONFIG;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(await readFile(path, "utf8")) };
|
|
77
|
+
} catch {
|
|
78
|
+
console.warn("sia-ui.json est illisible — valeurs par défaut utilisées.");
|
|
79
|
+
return DEFAULT_CONFIG;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Où lire le catalogue.
|
|
85
|
+
*
|
|
86
|
+
* Du plus explicite au plus implicite : l'option de la ligne de commande, puis
|
|
87
|
+
* le champ `registry` de `sia-ui.json`, puis la copie embarquée dans la CLI.
|
|
88
|
+
*/
|
|
89
|
+
async function ouvrirSource(config) {
|
|
90
|
+
return choisirSource({
|
|
91
|
+
racineLocale: registryRoot,
|
|
92
|
+
url: valeurs.registry ?? config.registry,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveItems(registre, names) {
|
|
97
|
+
const resolved = new Map();
|
|
98
|
+
|
|
99
|
+
const visit = (name) => {
|
|
100
|
+
if (resolved.has(name)) return;
|
|
101
|
+
|
|
102
|
+
const item = registre.items.find((candidate) => candidate.name === name);
|
|
103
|
+
if (!item) throw new Error(`Composant inconnu : ${name}`);
|
|
104
|
+
|
|
105
|
+
if (item.status === "removed") {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`${name} a été retiré du registre${item.replacedBy ? ` — utiliser ${item.replacedBy}` : ""}.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const dependency of item.registryDependencies ?? []) visit(dependency);
|
|
112
|
+
resolved.set(name, item);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
for (const name of names) visit(name);
|
|
116
|
+
return [...resolved.values()];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Où va un fichier de l'entrée, selon la configuration du projet.
|
|
121
|
+
*
|
|
122
|
+
* Les chemins du registre sont exprimés depuis la racine des sources —
|
|
123
|
+
* `components/ui/badge.tsx`, `lib/sia-ui.ts` — et sont réancrés ici.
|
|
124
|
+
*/
|
|
125
|
+
function targetFor(file, config) {
|
|
126
|
+
// Les entrées historiques rangent tout sous `components/ui/`, les entrées du
|
|
127
|
+
// package sous `components/<Nom>/`. Les deux préfixes désignent le même
|
|
128
|
+
// dossier chez l'utilisateur.
|
|
129
|
+
if (file.startsWith("components/ui/")) {
|
|
130
|
+
return join(config.components, file.slice("components/ui/".length));
|
|
131
|
+
}
|
|
132
|
+
if (file.startsWith("components/")) {
|
|
133
|
+
return join(config.components, file.slice("components/".length));
|
|
134
|
+
}
|
|
135
|
+
if (file.startsWith("lib/")) {
|
|
136
|
+
return join(config.lib, file.slice("lib/".length));
|
|
137
|
+
}
|
|
138
|
+
return join("src", file);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Un chemin de projet, écrit pareil partout.
|
|
143
|
+
*
|
|
144
|
+
* `sia-ui.lock.json` est versionné dans le dépôt de l'utilisateur : un
|
|
145
|
+
* `src\components\Button` écrit sous Windows et un `src/components/Button`
|
|
146
|
+
* écrit sous Linux décriraient le même fichier avec deux clés différentes, et
|
|
147
|
+
* `doctor` verrait un composant manquant à chaque changement de machine.
|
|
148
|
+
*/
|
|
149
|
+
function cheminUnix(chemin) {
|
|
150
|
+
return chemin.split(sep).join("/");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function init(cwd) {
|
|
154
|
+
const configPath = join(cwd, "sia-ui.json");
|
|
155
|
+
if (!(await exists(configPath))) {
|
|
156
|
+
await writeFile(
|
|
157
|
+
configPath,
|
|
158
|
+
`${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`,
|
|
159
|
+
);
|
|
160
|
+
console.log("sia-ui.json créé.");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const config = await readConfig(cwd);
|
|
164
|
+
const styleTarget = join(cwd, config.styles);
|
|
165
|
+
await mkdir(dirname(styleTarget), { recursive: true });
|
|
166
|
+
|
|
167
|
+
if (!(await exists(styleTarget))) {
|
|
168
|
+
await cp(join(registryRoot, "styles/sia-ui.css"), styleTarget);
|
|
169
|
+
console.log(`add ${config.styles}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log("SIA UI initialisé.");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function add(cwd, names) {
|
|
176
|
+
if (names.length === 0) {
|
|
177
|
+
throw new Error("Indiquer au moins un nom de composant.");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const overwrite = flags.has("--overwrite");
|
|
181
|
+
const config = await readConfig(cwd);
|
|
182
|
+
const source = await ouvrirSource(config);
|
|
183
|
+
const items = resolveItems(source.registre, names);
|
|
184
|
+
const npm = new Set();
|
|
185
|
+
const warnings = [];
|
|
186
|
+
const lock = await lireLock(cwd);
|
|
187
|
+
|
|
188
|
+
if (source.origine !== "embarqué") {
|
|
189
|
+
console.log(`registre ${source.origine}\n`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
for (const item of items) {
|
|
193
|
+
if (item.status === "deprecated") {
|
|
194
|
+
warnings.push(
|
|
195
|
+
`${item.name} est déprécié${item.replacedBy ? ` — préférer ${item.replacedBy}` : ""}${item.deprecatedReason ? ` (${item.deprecatedReason})` : ""}.`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const dependency of item.dependencies ?? []) npm.add(dependency);
|
|
200
|
+
|
|
201
|
+
const empreintes = {};
|
|
202
|
+
let copieComplete = true;
|
|
203
|
+
|
|
204
|
+
for (const file of item.files) {
|
|
205
|
+
const relative = targetFor(file, config);
|
|
206
|
+
const target = join(cwd, relative);
|
|
207
|
+
|
|
208
|
+
await mkdir(dirname(target), { recursive: true });
|
|
209
|
+
|
|
210
|
+
if ((await exists(target)) && !overwrite) {
|
|
211
|
+
console.log(`skip ${relative} (déjà présent — --overwrite pour écraser)`);
|
|
212
|
+
// Le verrou ne décrit que ce qui a réellement été écrit : y noter une
|
|
213
|
+
// empreinte qu'on n'a pas posée ferait mentir `doctor` et `diff`.
|
|
214
|
+
copieComplete = false;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Le contenu passe par la mémoire plutôt que par une copie de fichier :
|
|
219
|
+
// c'est ce qui permet à la même boucle de servir une source distante.
|
|
220
|
+
const contenu = await source.lire(file);
|
|
221
|
+
await writeFile(target, contenu);
|
|
222
|
+
empreintes[cheminUnix(relative)] = empreinte(contenu);
|
|
223
|
+
console.log(`add ${relative}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (copieComplete) noter(lock, item, empreintes);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await ecrireLock(cwd, lock);
|
|
230
|
+
console.log(`\nlock ${LOCK_FILE}`);
|
|
231
|
+
|
|
232
|
+
for (const warning of warnings) console.warn(`\n⚠ ${warning}`);
|
|
233
|
+
|
|
234
|
+
if (npm.size > 0) {
|
|
235
|
+
console.log(`\nÀ installer :\n pnpm add ${[...npm].sort().join(" ")}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function list(registre) {
|
|
240
|
+
const visibles = registre.items.filter((item) => item.name !== "utils");
|
|
241
|
+
const largeurNom = Math.max(...visibles.map((item) => item.name.length), 16);
|
|
242
|
+
const largeurVersion = Math.max(
|
|
243
|
+
...visibles.map((item) => (item.version ?? "").length),
|
|
244
|
+
7,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
for (const item of visibles) {
|
|
248
|
+
// Le statut ne s'affiche que lorsqu'il demande une décision : « stable »
|
|
249
|
+
// sur cent quatre lignes n'apprend rien à personne.
|
|
250
|
+
const statut =
|
|
251
|
+
item.status && item.status !== "stable" ? ` [${item.status}]` : "";
|
|
252
|
+
|
|
253
|
+
console.log(
|
|
254
|
+
`${item.name.padEnd(largeurNom)} ${(item.version ?? "").padEnd(largeurVersion)} ${item.description}${statut}`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function help() {
|
|
260
|
+
console.log(
|
|
261
|
+
[
|
|
262
|
+
"SIA UI",
|
|
263
|
+
"",
|
|
264
|
+
"Commandes :",
|
|
265
|
+
" init initialiser une application",
|
|
266
|
+
" list lister les composants disponibles",
|
|
267
|
+
" list --outdated lister ce qui a une version plus récente",
|
|
268
|
+
" add <nom...> installer des composants et leurs dépendances",
|
|
269
|
+
" diff [nom...] comparer les fichiers installés au registre",
|
|
270
|
+
" remove <nom...> retirer des composants installés",
|
|
271
|
+
" doctor vérifier l'état de ce qui est installé",
|
|
272
|
+
"",
|
|
273
|
+
"Options :",
|
|
274
|
+
" --overwrite écraser les fichiers déjà présents",
|
|
275
|
+
" --force passer outre les avertissements de remove",
|
|
276
|
+
" --registry <url> lire un registre servi en HTTP",
|
|
277
|
+
"",
|
|
278
|
+
"Les chemins d'installation viennent de sia-ui.json,",
|
|
279
|
+
`ce qui est installé de ${LOCK_FILE}.`,
|
|
280
|
+
"",
|
|
281
|
+
].join("\n"),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Ce dont les commandes de cycle de vie ont besoin, passé explicitement.
|
|
287
|
+
*
|
|
288
|
+
* Elles vivent dans un autre module et n'importent rien d'ici : une
|
|
289
|
+
* dépendance circulaire entre le point d'entrée et ses commandes est un piège
|
|
290
|
+
* qui ne se voit qu'à l'exécution.
|
|
291
|
+
*/
|
|
292
|
+
async function contexteProjet(cwd) {
|
|
293
|
+
const config = await readConfig(cwd);
|
|
294
|
+
const source = await ouvrirSource(config);
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
cwd,
|
|
298
|
+
config,
|
|
299
|
+
registry: source.registre,
|
|
300
|
+
lireSource: source.lire,
|
|
301
|
+
// Le verrou écrit ses chemins en barres obliques : la comparaison doit se
|
|
302
|
+
// faire dans la même écriture, sinon aucun fichier ne se retrouve sous
|
|
303
|
+
// Windows.
|
|
304
|
+
cibleDe: (fichier, conf) => cheminUnix(targetFor(fichier, conf)),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const cwd = process.cwd();
|
|
310
|
+
if (command === "init") await init(cwd);
|
|
311
|
+
else if (command === "list" && flags.has("--outdated")) {
|
|
312
|
+
await outdated(await contexteProjet(cwd));
|
|
313
|
+
} else if (command === "list") {
|
|
314
|
+
const source = await ouvrirSource(await readConfig(cwd));
|
|
315
|
+
list(source.registre);
|
|
316
|
+
} else if (command === "add") await add(cwd, args);
|
|
317
|
+
else if (command === "diff") await diff(await contexteProjet(cwd), args);
|
|
318
|
+
else if (command === "remove") {
|
|
319
|
+
await remove(await contexteProjet(cwd), args, flags);
|
|
320
|
+
} else if (command === "doctor") await doctor(await contexteProjet(cwd));
|
|
321
|
+
else help();
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error(error instanceof Error ? error.message : error);
|
|
324
|
+
process.exitCode = 1;
|
|
325
|
+
}
|