@khwarizmi/cli 0.3.1 → 0.4.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/README.md +26 -107
- package/package.json +6 -3
- package/src/index.js +43 -197
- package/src/init.js +20 -0
- package/src/util.js +29 -77
- package/src/registry-url.js +0 -5
package/README.md
CHANGED
|
@@ -1,107 +1,26 @@
|
|
|
1
|
-
# khwarizmi
|
|
2
|
-
|
|
3
|
-
CLI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
CLI juga memasang stylesheet global Khwarizmi UI dan mengimpornya otomatis ke
|
|
28
|
-
`src/app.css` atau `src/routes/app.css`. Jika tidak terdeteksi, CLI meminta path-nya.
|
|
29
|
-
|
|
30
|
-
Atau pasang komponen tertentu:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npx khwarizmi-ui add button
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Beberapa sekaligus:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx khwarizmi-ui add button card input select
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Semuanya sekaligus:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
npx khwarizmi-ui add --all
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Lihat semua yang tersedia:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
npx khwarizmi-ui list
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Perintah
|
|
55
|
-
|
|
56
|
-
| Perintah | Fungsi |
|
|
57
|
-
| --- | --- |
|
|
58
|
-
| `add <komponen...>` | Pasang satu atau beberapa komponen |
|
|
59
|
-
| `add --all` | Pasang semua komponen yang ada di registry |
|
|
60
|
-
| `list` | Daftar komponen yang tersedia |
|
|
61
|
-
|
|
62
|
-
## Opsi
|
|
63
|
-
|
|
64
|
-
| Opsi | Fungsi |
|
|
65
|
-
| --- | --- |
|
|
66
|
-
| `--all`, `-a` | Pasang semua komponen yang ada di registry |
|
|
67
|
-
| `--overwrite` | Timpa file yang sudah ada tanpa nanya |
|
|
68
|
-
| `--cwd <path>` | Jalankan di folder lain |
|
|
69
|
-
| `--no-deps` | Jangan pasang dependency npm |
|
|
70
|
-
| `--help`, `--version` | |
|
|
71
|
-
|
|
72
|
-
## Cara kerjanya
|
|
73
|
-
|
|
74
|
-
1. Ambil definisi komponen dari registry.
|
|
75
|
-
2. Pasang stylesheet global dan import-nya sekali.
|
|
76
|
-
3. Tulis file ke path tujuannya, mis. `src/lib/components/khwarizmi-ui/Button.svelte`.
|
|
77
|
-
4. Pasang dependency npm yang belum ada, pakai package manager yang terdeteksi
|
|
78
|
-
dari lockfile project kamu (npm, pnpm, yarn, atau bun).
|
|
79
|
-
|
|
80
|
-
File yang sudah ada tidak ditimpa diam-diam — kamu akan ditanya dulu, kecuali
|
|
81
|
-
memakai `--overwrite`. Kalau dijalankan bukan di terminal interaktif (mis. CI),
|
|
82
|
-
file yang sudah ada dilewati supaya aman.
|
|
83
|
-
|
|
84
|
-
## Prasyarat
|
|
85
|
-
|
|
86
|
-
Komponennya dibuat untuk **Svelte 5** (runes) dan **Tailwind CSS v4**.
|
|
87
|
-
Sebagian komponen memakai `lucide-svelte`; dependency ini dipasang otomatis oleh CLI.
|
|
88
|
-
|
|
89
|
-
## Catatan versi `npx`
|
|
90
|
-
|
|
91
|
-
Sesaat setelah rilis baru, registry npm mungkin masih menyebarkan versi terbaru.
|
|
92
|
-
Kalau `npx khwarizmi-ui` masih menjalankan versi lama atau hasilnya belum sesuai,
|
|
93
|
-
coba `@latest`:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
npx khwarizmi-ui@latest add --all
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Registry sendiri
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
KHWARIZMI_UI_REGISTRY=http://localhost:5173 npx khwarizmi-ui add button
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Lisensi
|
|
106
|
-
|
|
107
|
-
MIT
|
|
1
|
+
# @khwarizmi/cli
|
|
2
|
+
|
|
3
|
+
CLI inisialisasi untuk [`@khwarizmi/svelte-ui`](https://www.npmjs.com/package/@khwarizmi/svelte-ui).
|
|
4
|
+
CLI ini tidak menyalin source komponen ke project.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @khwarizmi/svelte-ui
|
|
8
|
+
npx @khwarizmi/cli init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`init` menambahkan import stylesheet package ke `src/app.css` atau
|
|
12
|
+
`src/routes/app.css`. Untuk lokasi lain:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx @khwarizmi/cli init --css src/styles/app.css
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Perintah ini idempoten: menjalankannya kembali tidak menggandakan import.
|
|
19
|
+
|
|
20
|
+
Setelah itu, gunakan komponen langsung dari package:
|
|
21
|
+
|
|
22
|
+
```svelte
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
import { Button, Card } from '@khwarizmi/svelte-ui';
|
|
25
|
+
</script>
|
|
26
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khwarizmi/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Inisialisasi @khwarizmi/svelte-ui di project Svelte.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"khwarizmi-ui": "./src/index.js"
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"README.md",
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test"
|
|
16
|
+
},
|
|
14
17
|
"engines": {
|
|
15
18
|
"node": ">=18"
|
|
16
19
|
},
|
|
@@ -19,7 +22,7 @@
|
|
|
19
22
|
"svelte",
|
|
20
23
|
"ui",
|
|
21
24
|
"components",
|
|
22
|
-
"
|
|
25
|
+
"init"
|
|
23
26
|
],
|
|
24
27
|
"license": "MIT",
|
|
25
28
|
"homepage": "https://ui.khwarizmi.co.id"
|
package/src/index.js
CHANGED
|
@@ -1,207 +1,53 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { createInterface } from 'node:readline/promises';
|
|
6
|
-
import { REGISTRY_URL } from './registry-url.js';
|
|
7
|
-
import { c, log, die, fetchJson, detectPackageManager, installArgs, missingDeps } from './util.js';
|
|
8
|
-
|
|
9
|
-
const VERSION = '0.3.1';
|
|
10
|
-
const cwd = process.cwd();
|
|
11
|
-
|
|
12
|
-
function usage() {
|
|
13
|
-
log.plain(`
|
|
14
|
-
${c.bold('khwarizmi-ui')} ${c.dim('v' + VERSION)}
|
|
15
|
-
|
|
16
|
-
${c.dim('Pasang komponen Khwarizmi UI ke project kamu.')}
|
|
17
|
-
|
|
18
|
-
${c.bold('Perintah')}
|
|
19
|
-
${c.orange('add')} <komponen...> Pasang satu atau beberapa komponen
|
|
20
|
-
${c.orange('add')} --all Pasang semua komponen sekaligus
|
|
21
|
-
${c.orange('list')} Lihat semua komponen yang tersedia
|
|
22
|
-
|
|
23
|
-
${c.bold('Opsi')}
|
|
24
|
-
--all, -a Pasang semua komponen yang ada di registry
|
|
25
|
-
--overwrite Timpa file yang sudah ada tanpa nanya
|
|
26
|
-
--cwd <path> Jalankan di folder lain
|
|
27
|
-
--no-deps Jangan pasang dependency npm
|
|
28
|
-
--help, --version
|
|
29
|
-
|
|
30
|
-
${c.bold('Contoh')}
|
|
31
|
-
${c.dim('$')} npx khwarizmi-ui add button
|
|
32
|
-
${c.dim('$')} npx khwarizmi-ui add button card input
|
|
33
|
-
${c.dim('$')} npx khwarizmi-ui add --all
|
|
34
|
-
`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function parseArgs(argv) {
|
|
38
|
-
const opts = { overwrite: false, deps: true, all: false, cwd };
|
|
39
|
-
const rest = [];
|
|
40
|
-
for (let i = 0; i < argv.length; i++) {
|
|
41
|
-
const a = argv[i];
|
|
42
|
-
if (a === '--overwrite') opts.overwrite = true;
|
|
43
|
-
else if (a === '--all' || a === '-a') opts.all = true;
|
|
44
|
-
else if (a === '--no-deps') opts.deps = false;
|
|
45
|
-
else if (a === '--cwd') opts.cwd = resolve(argv[++i] ?? '.');
|
|
46
|
-
else if (a === '--help' || a === '-h') opts.help = true;
|
|
47
|
-
else if (a === '--version' || a === '-v') opts.version = true;
|
|
48
|
-
else rest.push(a);
|
|
49
|
-
}
|
|
50
|
-
return { opts, rest };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function confirm(question) {
|
|
54
|
-
// Kalau bukan terminal interaktif (mis. di CI), jangan menggantung — tolak saja.
|
|
55
|
-
if (!process.stdin.isTTY) return false;
|
|
56
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
57
|
-
const answer = await rl.question(`${c.orange('?')} ${question} ${c.dim('(y/N)')} `);
|
|
58
|
-
rl.close();
|
|
59
|
-
return /^y(es)?$/i.test(answer.trim());
|
|
60
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { c, die, log } from './util.js';
|
|
4
|
+
import { init } from './init.js';
|
|
61
5
|
|
|
62
|
-
|
|
63
|
-
if (!styles) return;
|
|
6
|
+
const VERSION = '1.0.0';
|
|
64
7
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
writeFileSync(styleTarget, styles.content, 'utf8');
|
|
69
|
-
}
|
|
8
|
+
function usage() {
|
|
9
|
+
log.plain(`
|
|
10
|
+
${c.bold('khwarizmi-ui')} ${c.dim('v' + VERSION)}
|
|
70
11
|
|
|
71
|
-
|
|
72
|
-
let appCss = candidates.map((path) => join(opts.cwd, path)).find(existsSync);
|
|
12
|
+
${c.dim('Siapkan @khwarizmi/svelte-ui di project Svelte kamu.')}
|
|
73
13
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const answer = await rl.question(`${c.orange('?')} Path stylesheet global aplikasi ${c.dim('(contoh: src/app.css)')} `);
|
|
77
|
-
rl.close();
|
|
78
|
-
if (answer.trim()) {
|
|
79
|
-
const candidate = resolve(opts.cwd, answer.trim());
|
|
80
|
-
if (existsSync(candidate)) appCss = candidate;
|
|
81
|
-
else log.warn(`Stylesheet tidak ditemukan: ${answer.trim()}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
14
|
+
${c.bold('Perintah')}
|
|
15
|
+
${c.orange('init')} Tambahkan stylesheet global Khwarizmi UI
|
|
84
16
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
17
|
+
${c.bold('Opsi')}
|
|
18
|
+
--cwd <path> Jalankan di folder lain
|
|
19
|
+
--css <path> Pilih stylesheet global aplikasi
|
|
20
|
+
--help, --version
|
|
90
21
|
|
|
91
|
-
|
|
92
|
-
|
|
22
|
+
${c.bold('Contoh')}
|
|
23
|
+
${c.dim('$')} npm install @khwarizmi/svelte-ui
|
|
24
|
+
${c.dim('$')} npx @khwarizmi/cli init
|
|
25
|
+
`);
|
|
93
26
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Ambil item yang mau dipasang. Untuk --all cukup sekali request ke indeks,
|
|
109
|
-
* karena indeksnya sudah memuat isi file tiap komponen.
|
|
110
|
-
*/
|
|
111
|
-
async function resolveItems(names, opts) {
|
|
112
|
-
if (opts.all) {
|
|
113
|
-
const index = await fetchJson(`${REGISTRY_URL}/r/registry.json`);
|
|
114
|
-
if (!index) die('Registry tidak ditemukan.', REGISTRY_URL);
|
|
115
|
-
log.step(`Mengambil ${c.bold('semua')} komponen (${index.items.length})`);
|
|
116
|
-
return { items: index.items, globalStyles: index.globalStyles };
|
|
27
|
+
|
|
28
|
+
function parseArgs(argv) {
|
|
29
|
+
const opts = { cwd: process.cwd() };
|
|
30
|
+
const rest = [];
|
|
31
|
+
for (let i = 0; i < argv.length; i++) {
|
|
32
|
+
const arg = argv[i];
|
|
33
|
+
if (arg === '--cwd') opts.cwd = resolve(argv[++i] ?? '.');
|
|
34
|
+
else if (arg === '--css') opts.css = argv[++i];
|
|
35
|
+
else if (arg === '--help' || arg === '-h') opts.help = true;
|
|
36
|
+
else if (arg === '--version' || arg === '-v') opts.version = true;
|
|
37
|
+
else rest.push(arg);
|
|
117
38
|
}
|
|
39
|
+
return { opts, rest };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { opts, rest } = parseArgs(process.argv.slice(2));
|
|
43
|
+
const [command] = rest;
|
|
118
44
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
log.plain(` ${c.dim('Lihat daftarnya: npx khwarizmi-ui list')}`);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
globalStyles ??= item.globalStyles;
|
|
130
|
-
items.push(item);
|
|
45
|
+
if (opts.version) log.plain(VERSION);
|
|
46
|
+
else if (opts.help || !command) usage();
|
|
47
|
+
else if (command === 'init') {
|
|
48
|
+
try {
|
|
49
|
+
init(opts);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
die(error.message);
|
|
131
52
|
}
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async function addCommand(names, opts) {
|
|
136
|
-
if (!names.length && !opts.all) {
|
|
137
|
-
die('Sebutkan komponen yang mau dipasang.', 'satu: npx khwarizmi-ui add button | semua: npx khwarizmi-ui add --all');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const written = [];
|
|
141
|
-
const skipped = [];
|
|
142
|
-
const deps = new Set();
|
|
143
|
-
|
|
144
|
-
const { items, globalStyles } = await resolveItems(names, opts);
|
|
145
|
-
await ensureGlobalStyles(globalStyles, opts);
|
|
146
|
-
|
|
147
|
-
for (const item of items) {
|
|
148
|
-
for (const dep of item.dependencies ?? []) deps.add(dep);
|
|
149
|
-
|
|
150
|
-
for (const file of item.files ?? []) {
|
|
151
|
-
const target = file.target ?? file.path;
|
|
152
|
-
const dest = join(opts.cwd, target);
|
|
153
|
-
|
|
154
|
-
if (existsSync(dest) && !opts.overwrite) {
|
|
155
|
-
const ok = await confirm(`${target} sudah ada. Timpa?`);
|
|
156
|
-
if (!ok) {
|
|
157
|
-
skipped.push(target);
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
163
|
-
writeFileSync(dest, file.content, 'utf8');
|
|
164
|
-
written.push(target);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Dependency dipasang setelah semua file beres, biar package manager
|
|
169
|
-
// cuma jalan sekali walau komponen yang dipasang banyak.
|
|
170
|
-
if (opts.deps && deps.size) {
|
|
171
|
-
const need = missingDeps(opts.cwd, [...deps]);
|
|
172
|
-
if (need.length) {
|
|
173
|
-
const pm = detectPackageManager(opts.cwd);
|
|
174
|
-
log.step(`Memasang dependency lewat ${c.bold(pm)}: ${need.join(', ')}`);
|
|
175
|
-
const res = spawnSync(pm, installArgs[pm](need), {
|
|
176
|
-
cwd: opts.cwd,
|
|
177
|
-
stdio: 'inherit',
|
|
178
|
-
shell: process.platform === 'win32'
|
|
179
|
-
});
|
|
180
|
-
if (res.status !== 0) log.warn(`Gagal memasang dependency. Pasang manual: ${pm} add ${need.join(' ')}`);
|
|
181
|
-
else log.done('Dependency terpasang.');
|
|
182
|
-
} else {
|
|
183
|
-
log.done('Semua dependency sudah ada.');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
log.plain();
|
|
188
|
-
if (written.length) {
|
|
189
|
-
log.done(`${written.length} file dibuat:`);
|
|
190
|
-
for (const f of written) log.plain(` ${c.dim('•')} ${f}`);
|
|
191
|
-
}
|
|
192
|
-
if (skipped.length) {
|
|
193
|
-
log.warn(`${skipped.length} file dilewati:`);
|
|
194
|
-
for (const f of skipped) log.plain(` ${c.dim('•')} ${f}`);
|
|
195
|
-
}
|
|
196
|
-
if (!written.length && !skipped.length) die('Tidak ada yang dipasang.');
|
|
197
|
-
log.plain();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const { opts, rest } = parseArgs(process.argv.slice(2));
|
|
201
|
-
const [command, ...names] = rest;
|
|
202
|
-
|
|
203
|
-
if (opts.version) log.plain(VERSION);
|
|
204
|
-
else if (opts.help || !command) usage();
|
|
205
|
-
else if (command === 'list') await listCommand();
|
|
206
|
-
else if (command === 'add') await addCommand(names, opts);
|
|
207
|
-
else die(`Perintah tidak dikenal: ${command}`, 'jalankan: npx khwarizmi-ui --help');
|
|
53
|
+
} else die(`Perintah tidak dikenal: ${command}`, 'jalankan: khwarizmi-ui --help');
|
package/src/init.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { hasPackage, log } from './util.js';
|
|
4
|
+
|
|
5
|
+
export const STYLE_IMPORT = "@import '@khwarizmi/svelte-ui/styles.css';";
|
|
6
|
+
|
|
7
|
+
export function init(opts) {
|
|
8
|
+
if (!hasPackage(opts.cwd, '@khwarizmi/svelte-ui')) {
|
|
9
|
+
throw new Error('@khwarizmi/svelte-ui belum terpasang. Jalankan package manager untuk memasangnya terlebih dahulu.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const candidates = opts.css ? [opts.css] : ['src/app.css', 'src/routes/app.css'];
|
|
13
|
+
const relative = candidates.find((file) => existsSync(join(opts.cwd, file)));
|
|
14
|
+
if (!relative) throw new Error('Stylesheet global tidak ditemukan. Tentukan dengan --css <path>.');
|
|
15
|
+
|
|
16
|
+
const target = join(opts.cwd, relative);
|
|
17
|
+
const content = readFileSync(target, 'utf8');
|
|
18
|
+
if (!content.includes(STYLE_IMPORT)) writeFileSync(target, `${STYLE_IMPORT}\n${content}`, 'utf8');
|
|
19
|
+
log.done(`Khwarizmi UI siap di ${relative}`);
|
|
20
|
+
}
|
package/src/util.js
CHANGED
|
@@ -1,77 +1,29 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Deteksi package manager dari lockfile yang ada, biar dependency-nya
|
|
32
|
-
* dipasang pakai tool yang sama dengan yang dipakai project itu.
|
|
33
|
-
*/
|
|
34
|
-
export function detectPackageManager(cwd) {
|
|
35
|
-
if (existsSync(join(cwd, 'bun.lock')) || existsSync(join(cwd, 'bun.lockb'))) return 'bun';
|
|
36
|
-
if (existsSync(join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
37
|
-
if (existsSync(join(cwd, 'yarn.lock'))) return 'yarn';
|
|
38
|
-
return 'npm';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const installArgs = {
|
|
42
|
-
npm: (pkgs) => ['install', ...pkgs],
|
|
43
|
-
pnpm: (pkgs) => ['add', ...pkgs],
|
|
44
|
-
yarn: (pkgs) => ['add', ...pkgs],
|
|
45
|
-
bun: (pkgs) => ['add', ...pkgs]
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/** Dependency yang sudah ada di package.json nggak perlu dipasang ulang. */
|
|
49
|
-
export function missingDeps(cwd, deps) {
|
|
50
|
-
const pkgPath = join(cwd, 'package.json');
|
|
51
|
-
if (!existsSync(pkgPath)) return deps;
|
|
52
|
-
try {
|
|
53
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
54
|
-
const have = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
55
|
-
// Buang penanda versi ("lucide-svelte@^1.0.0" -> "lucide-svelte"),
|
|
56
|
-
// termasuk buat paket scoped yang namanya diawali "@".
|
|
57
|
-
return deps.filter((d) => {
|
|
58
|
-
const at = d.lastIndexOf('@');
|
|
59
|
-
const name = at > 0 ? d.slice(0, at) : d;
|
|
60
|
-
return !have[name];
|
|
61
|
-
});
|
|
62
|
-
} catch {
|
|
63
|
-
return deps;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function fetchJson(url) {
|
|
68
|
-
let res;
|
|
69
|
-
try {
|
|
70
|
-
res = await fetch(url);
|
|
71
|
-
} catch (err) {
|
|
72
|
-
die(`Gagal menghubungi registry: ${url}`, err.message);
|
|
73
|
-
}
|
|
74
|
-
if (res.status === 404) return null;
|
|
75
|
-
if (!res.ok) die(`Registry membalas ${res.status} untuk ${url}`);
|
|
76
|
-
return res.json();
|
|
77
|
-
}
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const on = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
5
|
+
const wrap = (code) => (text) => (on ? `\x1b[${code}m${text}\x1b[0m` : text);
|
|
6
|
+
|
|
7
|
+
export const c = { orange: wrap('38;5;208'), dim: wrap('2'), bold: wrap('1'), red: wrap('31'), green: wrap('32') };
|
|
8
|
+
export const log = {
|
|
9
|
+
done: (message) => console.log(`${c.green('✔')} ${message}`),
|
|
10
|
+
error: (message) => console.error(`${c.red('✖')} ${message}`),
|
|
11
|
+
plain: (message = '') => console.log(message)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function die(message, hint) {
|
|
15
|
+
log.error(message);
|
|
16
|
+
if (hint) log.plain(` ${c.dim(hint)}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function hasPackage(cwd, name) {
|
|
21
|
+
const path = join(cwd, 'package.json');
|
|
22
|
+
if (!existsSync(path)) return false;
|
|
23
|
+
try {
|
|
24
|
+
const pkg = JSON.parse(readFileSync(path, 'utf8'));
|
|
25
|
+
return Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/registry-url.js
DELETED