guford-ui 0.1.0 → 0.3.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/bin.mjs +262 -38
- package/package.json +1 -1
package/bin.mjs
CHANGED
|
@@ -25,13 +25,15 @@
|
|
|
25
25
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
26
26
|
import { dirname, basename, join, resolve, relative } from 'node:path';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { execSync } from 'node:child_process';
|
|
28
29
|
|
|
29
30
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
const ROOT = resolve(__dirname, '..'); // racine du dépôt guford-ui (mode local)
|
|
31
32
|
const CONFIG_FILE = 'guford-ui.json';
|
|
32
33
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
34
|
+
// Dépôt GitHub par défaut (privé → lu via l'API Contents avec token).
|
|
35
|
+
const DEFAULT_REPO = 'bekmarc/guford-ui';
|
|
36
|
+
const DEFAULT_REF = 'main';
|
|
35
37
|
|
|
36
38
|
// --- couleurs ANSI ---------------------------------------------------------
|
|
37
39
|
const c = {
|
|
@@ -48,42 +50,72 @@ function fail(msg) {
|
|
|
48
50
|
process.exit(1);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
// --- source du registry (local FS
|
|
53
|
+
// --- source du registry (local FS | URL statique | API GitHub privée) ------
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
+
* Résout la source du registry. 3 modes :
|
|
56
|
+
* - 'local' : fichiers du dépôt (exécution depuis quitus-ui/guford-ui)
|
|
57
|
+
* - 'url' : base HTTP statique (--registry / $GUFORD_UI_REGISTRY), token facultatif
|
|
58
|
+
* - 'github' : dépôt GitHub privé via l'API Contents (--repo/--ref + token requis)
|
|
55
59
|
*/
|
|
56
60
|
function resolveSource(flags) {
|
|
61
|
+
const token = flags.token || process.env.GUFORD_UI_TOKEN || process.env.GITHUB_TOKEN;
|
|
57
62
|
const explicit = flags.registry || process.env.GUFORD_UI_REGISTRY;
|
|
58
|
-
if (explicit) return {
|
|
59
|
-
if (existsSync(join(ROOT, 'registry.json'))) return {
|
|
60
|
-
return {
|
|
63
|
+
if (explicit) return { kind: 'url', base: explicit.replace(/\/+$/, ''), token };
|
|
64
|
+
if (!flags.repo && existsSync(join(ROOT, 'registry.json'))) return { kind: 'local', base: ROOT };
|
|
65
|
+
return { kind: 'github', repo: flags.repo || DEFAULT_REPO, ref: flags.ref || DEFAULT_REF, token };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sourceUrl(source, rel) {
|
|
69
|
+
if (source.kind === 'url') return `${source.base}/${rel}`;
|
|
70
|
+
// GitHub Contents API : renvoie le contenu brut (texte + binaire) avec Accept: raw
|
|
71
|
+
return `https://api.github.com/repos/${source.repo}/contents/${rel}?ref=${source.ref}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sourceHeaders(source) {
|
|
75
|
+
const h = { 'User-Agent': 'guford-ui-cli' };
|
|
76
|
+
if (source.kind === 'github') {
|
|
77
|
+
if (!source.token)
|
|
78
|
+
fail(
|
|
79
|
+
'Dépôt privé : un token est requis.\n' +
|
|
80
|
+
' → crée un PAT GitHub (Contents: read) puis: set GUFORD_UI_TOKEN=<token>\n' +
|
|
81
|
+
' (ou passe --token <token>, ou utilise --registry <url-publique>)',
|
|
82
|
+
);
|
|
83
|
+
h.Authorization = `Bearer ${source.token}`;
|
|
84
|
+
h.Accept = 'application/vnd.github.raw';
|
|
85
|
+
} else if (source.token) {
|
|
86
|
+
h.Authorization = `Bearer ${source.token}`;
|
|
87
|
+
}
|
|
88
|
+
return h;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function fetchEntry(source, rel) {
|
|
92
|
+
const url = sourceUrl(source, rel);
|
|
93
|
+
const res = await fetch(url, { headers: sourceHeaders(source) });
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
const hint = res.status === 404 ? ' (introuvable, ou token sans accès au dépôt privé)' : '';
|
|
96
|
+
fail(`Téléchargement échoué (${res.status})${hint} : ${url}`);
|
|
97
|
+
}
|
|
98
|
+
return Buffer.from(await res.arrayBuffer());
|
|
61
99
|
}
|
|
62
100
|
|
|
63
101
|
/** Lit un fichier texte du registry (chemin relatif à la racine, ex: 'registry.json'). */
|
|
64
102
|
async function readText(source, rel) {
|
|
65
|
-
if (source.
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return await res.text();
|
|
103
|
+
if (source.kind === 'local') {
|
|
104
|
+
const path = join(source.base, rel);
|
|
105
|
+
if (!existsSync(path)) fail(`Fichier introuvable: ${path}`);
|
|
106
|
+
return readFileSync(path, 'utf8');
|
|
70
107
|
}
|
|
71
|
-
|
|
72
|
-
if (!existsSync(path)) fail(`Fichier introuvable: ${path}`);
|
|
73
|
-
return readFileSync(path, 'utf8');
|
|
108
|
+
return (await fetchEntry(source, rel)).toString('utf8');
|
|
74
109
|
}
|
|
75
110
|
|
|
76
111
|
/** Lit un fichier (texte ou binaire) du registry → Buffer. */
|
|
77
112
|
async function readBuffer(source, rel) {
|
|
78
|
-
if (source.
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return Buffer.from(await res.arrayBuffer());
|
|
113
|
+
if (source.kind === 'local') {
|
|
114
|
+
const path = join(source.base, rel);
|
|
115
|
+
if (!existsSync(path)) fail(`Fichier introuvable: ${path}`);
|
|
116
|
+
return readFileSync(path);
|
|
83
117
|
}
|
|
84
|
-
|
|
85
|
-
if (!existsSync(path)) fail(`Fichier introuvable: ${path}`);
|
|
86
|
-
return readFileSync(path);
|
|
118
|
+
return await fetchEntry(source, rel);
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
async function loadRegistry(source) {
|
|
@@ -119,17 +151,147 @@ function loadProjectConfig(cwd) {
|
|
|
119
151
|
return null;
|
|
120
152
|
}
|
|
121
153
|
|
|
154
|
+
// --- auto-câblage du projet cible ------------------------------------------
|
|
155
|
+
/** Détecte la structure du projet Angular consommateur. */
|
|
156
|
+
function detectProject(cwd) {
|
|
157
|
+
const root = resolve(process.cwd(), cwd);
|
|
158
|
+
const out = { root, ngMajor: null, ngJsonPath: null, ngJson: null, projName: null, sourceRoot: 'src', stylesFile: null, appConfig: null, hasTailwind: false };
|
|
159
|
+
|
|
160
|
+
const pkgPath = join(root, 'package.json');
|
|
161
|
+
if (existsSync(pkgPath)) {
|
|
162
|
+
try {
|
|
163
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
164
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
165
|
+
const ver = deps['@angular/core'];
|
|
166
|
+
const m = ver && ver.match(/(\d+)/);
|
|
167
|
+
if (m) out.ngMajor = Number(m[1]);
|
|
168
|
+
out.hasTailwind = !!deps['tailwindcss'];
|
|
169
|
+
} catch {}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const ngJsonPath = join(root, 'angular.json');
|
|
173
|
+
if (existsSync(ngJsonPath)) {
|
|
174
|
+
try {
|
|
175
|
+
const ngJson = JSON.parse(readFileSync(ngJsonPath, 'utf8'));
|
|
176
|
+
out.ngJsonPath = ngJsonPath;
|
|
177
|
+
out.ngJson = ngJson;
|
|
178
|
+
for (const [name, proj] of Object.entries(ngJson.projects || {})) {
|
|
179
|
+
const build = proj.architect?.build || proj.targets?.build;
|
|
180
|
+
if (build) {
|
|
181
|
+
out.projName = name;
|
|
182
|
+
out.sourceRoot = proj.sourceRoot || 'src';
|
|
183
|
+
const styles = build.options?.styles || [];
|
|
184
|
+
out.stylesFile = styles.find((s) => typeof s === 'string' && /\.(scss|css)$/.test(s)) || `${out.sourceRoot}/styles.scss`;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch {}
|
|
189
|
+
}
|
|
190
|
+
out.appConfig = join(root, out.sourceRoot, 'app', 'app.config.ts');
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Versions CoreUI/CDK alignées sur la version d'Angular du projet. */
|
|
195
|
+
function coreuiVersions(ngMajor) {
|
|
196
|
+
const map = { 19: '^5.4', 20: '^5.5', 21: '^5.6', 22: '^5.6' };
|
|
197
|
+
const cu = map[ngMajor] || '^5.5';
|
|
198
|
+
const ng = ngMajor ? `^${ngMajor}.0.0` : '^20.0.0';
|
|
199
|
+
return {
|
|
200
|
+
list: [`@coreui/angular@${cu}`, '@coreui/coreui@~5.4', `@coreui/icons-angular@${cu}`, `@angular/cdk@${ng}`, `@angular/animations@${ng}`],
|
|
201
|
+
ng,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function runNpmInstall(root, pkgs, { dryRun, dev = false } = {}) {
|
|
206
|
+
if (!pkgs.length) return;
|
|
207
|
+
const cmd = `npm install ${dev ? '-D ' : ''}${pkgs.join(' ')} --legacy-peer-deps`;
|
|
208
|
+
console.log(` ${c.cyan('$')} ${cmd}`);
|
|
209
|
+
if (dryRun) return;
|
|
210
|
+
try {
|
|
211
|
+
execSync(cmd, { cwd: root, stdio: 'inherit' });
|
|
212
|
+
} catch {
|
|
213
|
+
console.log(c.yellow(` ⚠ npm install a échoué — lance-le manuellement : ${cmd}`));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Ajoute une ligne à un fichier si absente (création si besoin). */
|
|
218
|
+
function ensureLine(path, line, { dryRun, prepend = false } = {}) {
|
|
219
|
+
let content = existsSync(path) ? readFileSync(path, 'utf8') : '';
|
|
220
|
+
if (content.includes(line.trim())) return false;
|
|
221
|
+
content = prepend ? `${line}\n${content}` : `${content}${content.endsWith('\n') || content === '' ? '' : '\n'}${line}\n`;
|
|
222
|
+
if (!dryRun) {
|
|
223
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
224
|
+
writeFileSync(path, content);
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Crée/complète tailwind.config.js avec le preset partagé. */
|
|
230
|
+
function wireTailwind(root, dryRun) {
|
|
231
|
+
const path = join(root, 'tailwind.config.js');
|
|
232
|
+
if (!existsSync(path)) {
|
|
233
|
+
const content = `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n presets: [require('./tailwind.preset.cjs')],\n content: ['./src/**/*.{html,ts}'],\n};\n`;
|
|
234
|
+
if (!dryRun) writeFileSync(path, content);
|
|
235
|
+
return 'créé';
|
|
236
|
+
}
|
|
237
|
+
let s = readFileSync(path, 'utf8');
|
|
238
|
+
if (s.includes('tailwind.preset.cjs')) return 'déjà présent';
|
|
239
|
+
const m = s.match(/(module\.exports\s*=\s*\{|export\s+default\s*\{)/);
|
|
240
|
+
if (!m) return 'à compléter à la main';
|
|
241
|
+
const idx = m.index + m[0].length;
|
|
242
|
+
s = `${s.slice(0, idx)}\n presets: [require('./tailwind.preset.cjs')],${s.slice(idx)}`;
|
|
243
|
+
if (!dryRun) writeFileSync(path, s);
|
|
244
|
+
return 'preset ajouté';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Ajoute le CSS CoreUI au tableau styles d'angular.json. */
|
|
248
|
+
function wireCoreuiCss(proj, dryRun) {
|
|
249
|
+
if (!proj.ngJson || !proj.projName) return 'angular.json introuvable';
|
|
250
|
+
const cssPath = 'node_modules/@coreui/coreui/dist/css/coreui.min.css';
|
|
251
|
+
const p = proj.ngJson.projects[proj.projName];
|
|
252
|
+
const build = p.architect?.build || p.targets?.build;
|
|
253
|
+
build.options = build.options || {};
|
|
254
|
+
build.options.styles = build.options.styles || [];
|
|
255
|
+
if (build.options.styles.includes(cssPath)) return 'déjà présent';
|
|
256
|
+
build.options.styles.unshift(cssPath);
|
|
257
|
+
if (!dryRun) writeFileSync(proj.ngJsonPath, JSON.stringify(proj.ngJson, null, 2) + '\n');
|
|
258
|
+
return 'ajouté';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Ajoute provideAnimations() dans app.config.ts. */
|
|
262
|
+
function wireProvideAnimations(appConfigPath, dryRun) {
|
|
263
|
+
if (!existsSync(appConfigPath)) return 'app.config.ts introuvable';
|
|
264
|
+
let s = readFileSync(appConfigPath, 'utf8');
|
|
265
|
+
if (s.includes('provideAnimations')) return 'déjà présent';
|
|
266
|
+
if (!s.includes('@angular/platform-browser/animations')) {
|
|
267
|
+
s = `import { provideAnimations } from '@angular/platform-browser/animations';\n${s}`;
|
|
268
|
+
}
|
|
269
|
+
const m = s.match(/providers\s*:\s*\[/);
|
|
270
|
+
if (!m) return 'providers[] introuvable';
|
|
271
|
+
const idx = m.index + m[0].length;
|
|
272
|
+
s = `${s.slice(0, idx)}\n provideAnimations(),${s.slice(idx)}`;
|
|
273
|
+
if (!dryRun) writeFileSync(appConfigPath, s);
|
|
274
|
+
return 'ajouté';
|
|
275
|
+
}
|
|
276
|
+
|
|
122
277
|
// --- parseur d'arguments ---------------------------------------------------
|
|
123
278
|
function parseArgs(argv) {
|
|
124
279
|
const positionals = [];
|
|
125
|
-
const flags = {
|
|
280
|
+
const flags = {
|
|
281
|
+
cwd: '.', dir: undefined, overwrite: false, dryRun: false,
|
|
282
|
+
registry: undefined, token: undefined, repo: undefined, ref: undefined, install: true,
|
|
283
|
+
};
|
|
126
284
|
for (let i = 0; i < argv.length; i++) {
|
|
127
285
|
const a = argv[i];
|
|
128
286
|
if (a === '--overwrite') flags.overwrite = true;
|
|
129
287
|
else if (a === '--dry-run') flags.dryRun = true;
|
|
288
|
+
else if (a === '--no-install') flags.install = false;
|
|
130
289
|
else if (a === '--cwd') flags.cwd = argv[++i];
|
|
131
290
|
else if (a === '--dir') flags.dir = argv[++i];
|
|
132
291
|
else if (a === '--registry') flags.registry = argv[++i];
|
|
292
|
+
else if (a === '--token') flags.token = argv[++i];
|
|
293
|
+
else if (a === '--repo') flags.repo = argv[++i];
|
|
294
|
+
else if (a === '--ref') flags.ref = argv[++i];
|
|
133
295
|
else if (a.startsWith('--')) fail(`Option inconnue: ${a}`);
|
|
134
296
|
else positionals.push(a);
|
|
135
297
|
}
|
|
@@ -137,7 +299,9 @@ function parseArgs(argv) {
|
|
|
137
299
|
}
|
|
138
300
|
|
|
139
301
|
function sourceLabel(source) {
|
|
140
|
-
|
|
302
|
+
if (source.kind === 'local') return c.dim('(registry local)');
|
|
303
|
+
if (source.kind === 'url') return c.dim(`(registry: ${source.base})`);
|
|
304
|
+
return c.dim(`(registry GitHub: ${source.repo}@${source.ref})`);
|
|
141
305
|
}
|
|
142
306
|
|
|
143
307
|
// --- commandes -------------------------------------------------------------
|
|
@@ -222,14 +386,45 @@ async function cmdAdd(names, flags) {
|
|
|
222
386
|
}
|
|
223
387
|
|
|
224
388
|
console.log(log.join('\n'));
|
|
389
|
+
|
|
390
|
+
// --- Dépendances npm + câblage CoreUI automatiques ---
|
|
391
|
+
const proj = detectProject(flags.cwd);
|
|
392
|
+
const usesCoreui = npmDeps.has('@coreui/angular');
|
|
393
|
+
|
|
394
|
+
// Liste à installer : deps registry (hors @coreui/angular et @angular/cdk gérés à part) + peers CoreUI alignés
|
|
395
|
+
const installList = [...npmDeps].filter((d) => d !== '@coreui/angular' && d !== '@angular/cdk');
|
|
396
|
+
if (usesCoreui) {
|
|
397
|
+
const { list } = coreuiVersions(proj.ngMajor);
|
|
398
|
+
installList.push(...list);
|
|
399
|
+
console.log(
|
|
400
|
+
c.dim(
|
|
401
|
+
`\n CoreUI détecté → versions alignées sur Angular ${proj.ngMajor ?? '?'} ` +
|
|
402
|
+
`(${coreuiVersions(proj.ngMajor).list.join(' ')})`,
|
|
403
|
+
),
|
|
404
|
+
);
|
|
405
|
+
} else if (npmDeps.has('@angular/cdk')) {
|
|
406
|
+
installList.push(`@angular/cdk${proj.ngMajor ? '@^' + proj.ngMajor + '.0.0' : ''}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
225
409
|
console.log('');
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
410
|
+
if (installList.length) {
|
|
411
|
+
if (flags.install) runNpmInstall(proj.root, installList, { dryRun: flags.dryRun });
|
|
412
|
+
else {
|
|
413
|
+
console.log(c.cyan(' Dépendances npm (à installer — sauté: --no-install):'));
|
|
414
|
+
console.log(` ${c.bold('npm install ' + installList.join(' '))}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Câblage CoreUI (CSS + animations) si nécessaire
|
|
419
|
+
if (usesCoreui) {
|
|
420
|
+
console.log(c.cyan('\n Câblage CoreUI:'));
|
|
421
|
+
console.log(` ${c.green('✓')} angular.json styles — ${wireCoreuiCss(proj, flags.dryRun)}`);
|
|
422
|
+
console.log(` ${c.green('✓')} app.config.ts provideAnimations() — ${wireProvideAnimations(proj.appConfig, flags.dryRun)}`);
|
|
229
423
|
}
|
|
424
|
+
|
|
230
425
|
console.log(
|
|
231
426
|
c.dim(
|
|
232
|
-
|
|
427
|
+
`\n ${written} fichier(s) ${flags.dryRun ? 'seraient écrits' : 'écrits'}` +
|
|
233
428
|
(skipped ? `, ${skipped} ignoré(s)` : '') +
|
|
234
429
|
'.\n',
|
|
235
430
|
),
|
|
@@ -293,12 +488,30 @@ async function cmdInit(flags) {
|
|
|
293
488
|
}
|
|
294
489
|
|
|
295
490
|
console.log(log.join('\n'));
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
console.log(
|
|
300
|
-
|
|
301
|
-
|
|
491
|
+
|
|
492
|
+
// --- Câblage automatique du projet cible ---
|
|
493
|
+
const proj = detectProject(flags.cwd);
|
|
494
|
+
console.log(c.cyan('\n Câblage automatique:'));
|
|
495
|
+
|
|
496
|
+
// 1. Fichier de styles global : directives Tailwind (si absentes) + @import tokens
|
|
497
|
+
const stylesFile = proj.stylesFile ? join(proj.root, proj.stylesFile) : join(projectRoot, dir, 'styles.scss');
|
|
498
|
+
if (!existsSync(stylesFile) || !readFileSync(stylesFile, 'utf8').includes('@tailwind base')) {
|
|
499
|
+
ensureLine(stylesFile, '@tailwind base;\n@tailwind components;\n@tailwind utilities;', { dryRun: flags.dryRun, prepend: true });
|
|
500
|
+
}
|
|
501
|
+
const addedImport = ensureLine(stylesFile, `@import './styles/guford-ui-tokens.css';`, { dryRun: flags.dryRun });
|
|
502
|
+
console.log(` ${addedImport ? c.green('✓') : c.dim('•')} tokens importés → ${relative(process.cwd(), stylesFile)}`);
|
|
503
|
+
|
|
504
|
+
// 2. tailwind.config.js (preset partagé)
|
|
505
|
+
console.log(` ${c.green('✓')} tailwind.config.js — ${wireTailwind(proj.root, flags.dryRun)}`);
|
|
506
|
+
|
|
507
|
+
// 3. Peer-deps de base (+ Tailwind si absent)
|
|
508
|
+
if (flags.install) {
|
|
509
|
+
runNpmInstall(proj.root, ['clsx', 'tailwind-merge'], { dryRun: flags.dryRun });
|
|
510
|
+
if (!proj.hasTailwind) runNpmInstall(proj.root, ['tailwindcss@3', 'postcss', 'autoprefixer'], { dryRun: flags.dryRun, dev: true });
|
|
511
|
+
} else {
|
|
512
|
+
console.log(c.dim(' • npm install clsx tailwind-merge' + (proj.hasTailwind ? '' : ' (+ -D tailwindcss@3 postcss autoprefixer)') + ' (sauté: --no-install)'));
|
|
513
|
+
}
|
|
514
|
+
|
|
302
515
|
console.log(
|
|
303
516
|
c.dim(
|
|
304
517
|
`\n ${written} fichier(s) ${flags.dryRun ? 'seraient écrits' : 'écrits'}` +
|
|
@@ -321,9 +534,20 @@ function help() {
|
|
|
321
534
|
${c.cyan('Options:')}
|
|
322
535
|
--cwd <path> Projet cible (défaut: .)
|
|
323
536
|
--dir <path> Racine d'installation (défaut: guford-ui.json puis src)
|
|
324
|
-
--registry <url> Base
|
|
537
|
+
--registry <url> Base HTTP statique du registry (sinon $GUFORD_UI_REGISTRY)
|
|
538
|
+
--repo <owner/repo> Dépôt GitHub (défaut: ${DEFAULT_REPO})
|
|
539
|
+
--ref <branch|tag> Réf GitHub (défaut: ${DEFAULT_REF}) — pin sur un tag pour reproductibilité
|
|
540
|
+
--token <token> Token GitHub pour dépôt privé (sinon $GUFORD_UI_TOKEN / $GITHUB_TOKEN)
|
|
541
|
+
--no-install Ne lance pas npm install (affiche les commandes)
|
|
325
542
|
--overwrite Écrase les fichiers existants
|
|
326
|
-
--dry-run Simule sans écrire
|
|
543
|
+
--dry-run Simule sans écrire ni installer
|
|
544
|
+
|
|
545
|
+
${c.cyan('Auto-câblage:')} init/add modifient le projet cible (npm install avec
|
|
546
|
+
CoreUI aligné sur votre Angular, @import des tokens, preset Tailwind,
|
|
547
|
+
CSS CoreUI dans angular.json, provideAnimations dans app.config.ts).
|
|
548
|
+
|
|
549
|
+
${c.cyan('Dépôt privé:')} crée un PAT GitHub (Contents: read), puis
|
|
550
|
+
${c.dim('set GUFORD_UI_TOKEN=<token> # Windows (cmd) | $env:GUFORD_UI_TOKEN="<token>" (PowerShell)')}
|
|
327
551
|
`);
|
|
328
552
|
}
|
|
329
553
|
|