@docyrus/cli 0.3.1 → 0.5.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 +51 -0
- package/dist/cli.js +1609 -43
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -1,25 +1,475 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { join, resolve, dirname } from 'path';
|
|
3
|
+
import { URL as URL$1, fileURLToPath } from 'url';
|
|
2
4
|
import { Command } from 'commander';
|
|
3
5
|
import { RestApiClient, AuthenticationError as AuthenticationError$1 } from '@docyrus/api-client';
|
|
4
6
|
import { createServer } from 'http';
|
|
5
|
-
import { URL, fileURLToPath } from 'url';
|
|
6
7
|
import { randomBytes, createDecipheriv, createCipheriv, scryptSync, createHash } from 'crypto';
|
|
7
8
|
import open from 'open';
|
|
8
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
|
|
9
|
-
import { join, resolve, dirname } from 'path';
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
|
|
10
10
|
import { homedir, arch, release, platform, hostname, userInfo } from 'os';
|
|
11
11
|
import Conf from 'conf';
|
|
12
12
|
import chalk4 from 'chalk';
|
|
13
|
-
import
|
|
14
|
-
import { select, input, password, confirm } from '@inquirer/prompts';
|
|
15
|
-
import { exec } from 'child_process';
|
|
13
|
+
import ora2 from 'ora';
|
|
14
|
+
import { select, input, password, confirm, checkbox } from '@inquirer/prompts';
|
|
15
|
+
import { exec, execFileSync } from 'child_process';
|
|
16
16
|
import { promisify } from 'util';
|
|
17
|
-
import { access, constants, writeFile, readFile, mkdir, cp, rm, readdir } from 'fs/promises';
|
|
17
|
+
import { access, constants, writeFile, readFile, mkdir, chmod, cp, rm, readdir } from 'fs/promises';
|
|
18
18
|
import { downloadTemplate } from 'giget';
|
|
19
19
|
import { generateFromOpenAPI } from '@docyrus/tanstack-db-generator';
|
|
20
20
|
|
|
21
|
+
var __create = Object.create;
|
|
22
|
+
var __defProp = Object.defineProperty;
|
|
23
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
24
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
25
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
26
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
27
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
28
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
29
|
+
}) : x)(function(x) {
|
|
30
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
31
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
32
|
+
});
|
|
33
|
+
var __esm = (fn, res) => function __init() {
|
|
34
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
35
|
+
};
|
|
36
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
37
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
38
|
+
};
|
|
39
|
+
var __copyProps = (to, from, except, desc) => {
|
|
40
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
41
|
+
for (let key of __getOwnPropNames(from))
|
|
42
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
43
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
44
|
+
}
|
|
45
|
+
return to;
|
|
46
|
+
};
|
|
47
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
48
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
49
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
50
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
51
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
52
|
+
__defProp(target, "default", { value: mod, enumerable: true }) ,
|
|
53
|
+
mod
|
|
54
|
+
));
|
|
55
|
+
var init_esm_shims = __esm({
|
|
56
|
+
"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/package.json
|
|
61
|
+
var require_package = __commonJS({
|
|
62
|
+
"../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/package.json"(exports$1, module) {
|
|
63
|
+
module.exports = {
|
|
64
|
+
name: "dotenv",
|
|
65
|
+
version: "17.3.1",
|
|
66
|
+
description: "Loads environment variables from .env file",
|
|
67
|
+
main: "lib/main.js",
|
|
68
|
+
types: "lib/main.d.ts",
|
|
69
|
+
exports: {
|
|
70
|
+
".": {
|
|
71
|
+
types: "./lib/main.d.ts",
|
|
72
|
+
require: "./lib/main.js",
|
|
73
|
+
default: "./lib/main.js"
|
|
74
|
+
},
|
|
75
|
+
"./config": "./config.js",
|
|
76
|
+
"./config.js": "./config.js",
|
|
77
|
+
"./lib/env-options": "./lib/env-options.js",
|
|
78
|
+
"./lib/env-options.js": "./lib/env-options.js",
|
|
79
|
+
"./lib/cli-options": "./lib/cli-options.js",
|
|
80
|
+
"./lib/cli-options.js": "./lib/cli-options.js",
|
|
81
|
+
"./package.json": "./package.json"
|
|
82
|
+
},
|
|
83
|
+
scripts: {
|
|
84
|
+
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
85
|
+
lint: "standard",
|
|
86
|
+
pretest: "npm run lint && npm run dts-check",
|
|
87
|
+
test: "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
88
|
+
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
89
|
+
prerelease: "npm test",
|
|
90
|
+
release: "standard-version"
|
|
91
|
+
},
|
|
92
|
+
repository: {
|
|
93
|
+
type: "git",
|
|
94
|
+
url: "git://github.com/motdotla/dotenv.git"
|
|
95
|
+
},
|
|
96
|
+
homepage: "https://github.com/motdotla/dotenv#readme",
|
|
97
|
+
funding: "https://dotenvx.com",
|
|
98
|
+
keywords: [
|
|
99
|
+
"dotenv",
|
|
100
|
+
"env",
|
|
101
|
+
".env",
|
|
102
|
+
"environment",
|
|
103
|
+
"variables",
|
|
104
|
+
"config",
|
|
105
|
+
"settings"
|
|
106
|
+
],
|
|
107
|
+
readmeFilename: "README.md",
|
|
108
|
+
license: "BSD-2-Clause",
|
|
109
|
+
devDependencies: {
|
|
110
|
+
"@types/node": "^18.11.3",
|
|
111
|
+
decache: "^4.6.2",
|
|
112
|
+
sinon: "^14.0.1",
|
|
113
|
+
standard: "^17.0.0",
|
|
114
|
+
"standard-version": "^9.5.0",
|
|
115
|
+
tap: "^19.2.0",
|
|
116
|
+
typescript: "^4.8.4"
|
|
117
|
+
},
|
|
118
|
+
engines: {
|
|
119
|
+
node: ">=12"
|
|
120
|
+
},
|
|
121
|
+
browser: {
|
|
122
|
+
fs: false
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/lib/main.js
|
|
129
|
+
var require_main = __commonJS({
|
|
130
|
+
"../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/lib/main.js"(exports$1, module) {
|
|
131
|
+
init_esm_shims();
|
|
132
|
+
var fs = __require("fs");
|
|
133
|
+
var path2 = __require("path");
|
|
134
|
+
var os = __require("os");
|
|
135
|
+
var crypto2 = __require("crypto");
|
|
136
|
+
var packageJson = require_package();
|
|
137
|
+
var version = packageJson.version;
|
|
138
|
+
var TIPS = [
|
|
139
|
+
"\u{1F510} encrypt with Dotenvx: https://dotenvx.com",
|
|
140
|
+
"\u{1F510} prevent committing .env to code: https://dotenvx.com/precommit",
|
|
141
|
+
"\u{1F510} prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
142
|
+
"\u{1F916} agentic secret storage: https://dotenvx.com/as2",
|
|
143
|
+
"\u26A1\uFE0F secrets for agents: https://dotenvx.com/as2",
|
|
144
|
+
"\u{1F6E1}\uFE0F auth for agents: https://vestauth.com",
|
|
145
|
+
"\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
|
|
146
|
+
"\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
|
|
147
|
+
"\u2699\uFE0F enable debug logging with { debug: true }",
|
|
148
|
+
"\u2699\uFE0F override existing env vars with { override: true }",
|
|
149
|
+
"\u2699\uFE0F suppress all logs with { quiet: true }",
|
|
150
|
+
"\u2699\uFE0F write to custom object with { processEnv: myObject }",
|
|
151
|
+
"\u2699\uFE0F load multiple .env files with { path: ['.env.local', '.env'] }"
|
|
152
|
+
];
|
|
153
|
+
function _getRandomTip() {
|
|
154
|
+
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
155
|
+
}
|
|
156
|
+
function parseBoolean(value) {
|
|
157
|
+
if (typeof value === "string") {
|
|
158
|
+
return !["false", "0", "no", "off", ""].includes(value.toLowerCase());
|
|
159
|
+
}
|
|
160
|
+
return Boolean(value);
|
|
161
|
+
}
|
|
162
|
+
function supportsAnsi() {
|
|
163
|
+
return process.stdout.isTTY;
|
|
164
|
+
}
|
|
165
|
+
function dim(text) {
|
|
166
|
+
return supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
|
|
167
|
+
}
|
|
168
|
+
var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
|
|
169
|
+
function parse(src) {
|
|
170
|
+
const obj = {};
|
|
171
|
+
let lines = src.toString();
|
|
172
|
+
lines = lines.replace(/\r\n?/mg, "\n");
|
|
173
|
+
let match;
|
|
174
|
+
while ((match = LINE.exec(lines)) != null) {
|
|
175
|
+
const key = match[1];
|
|
176
|
+
let value = match[2] || "";
|
|
177
|
+
value = value.trim();
|
|
178
|
+
const maybeQuote = value[0];
|
|
179
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
|
|
180
|
+
if (maybeQuote === '"') {
|
|
181
|
+
value = value.replace(/\\n/g, "\n");
|
|
182
|
+
value = value.replace(/\\r/g, "\r");
|
|
183
|
+
}
|
|
184
|
+
obj[key] = value;
|
|
185
|
+
}
|
|
186
|
+
return obj;
|
|
187
|
+
}
|
|
188
|
+
function _parseVault(options) {
|
|
189
|
+
options = options || {};
|
|
190
|
+
const vaultPath = _vaultPath(options);
|
|
191
|
+
options.path = vaultPath;
|
|
192
|
+
const result = DotenvModule.configDotenv(options);
|
|
193
|
+
if (!result.parsed) {
|
|
194
|
+
const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
195
|
+
err.code = "MISSING_DATA";
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
const keys = _dotenvKey(options).split(",");
|
|
199
|
+
const length = keys.length;
|
|
200
|
+
let decrypted;
|
|
201
|
+
for (let i = 0; i < length; i++) {
|
|
202
|
+
try {
|
|
203
|
+
const key = keys[i].trim();
|
|
204
|
+
const attrs = _instructions(result, key);
|
|
205
|
+
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
206
|
+
break;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (i + 1 >= length) {
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return DotenvModule.parse(decrypted);
|
|
214
|
+
}
|
|
215
|
+
function _warn(message) {
|
|
216
|
+
console.error(`[dotenv@${version}][WARN] ${message}`);
|
|
217
|
+
}
|
|
218
|
+
function _debug(message) {
|
|
219
|
+
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
220
|
+
}
|
|
221
|
+
function _log(message) {
|
|
222
|
+
console.log(`[dotenv@${version}] ${message}`);
|
|
223
|
+
}
|
|
224
|
+
function _dotenvKey(options) {
|
|
225
|
+
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
|
|
226
|
+
return options.DOTENV_KEY;
|
|
227
|
+
}
|
|
228
|
+
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
|
|
229
|
+
return process.env.DOTENV_KEY;
|
|
230
|
+
}
|
|
231
|
+
return "";
|
|
232
|
+
}
|
|
233
|
+
function _instructions(result, dotenvKey) {
|
|
234
|
+
let uri;
|
|
235
|
+
try {
|
|
236
|
+
uri = new URL(dotenvKey);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error.code === "ERR_INVALID_URL") {
|
|
239
|
+
const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
|
|
240
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
241
|
+
throw err;
|
|
242
|
+
}
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
const key = uri.password;
|
|
246
|
+
if (!key) {
|
|
247
|
+
const err = new Error("INVALID_DOTENV_KEY: Missing key part");
|
|
248
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
249
|
+
throw err;
|
|
250
|
+
}
|
|
251
|
+
const environment = uri.searchParams.get("environment");
|
|
252
|
+
if (!environment) {
|
|
253
|
+
const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
254
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
258
|
+
const ciphertext = result.parsed[environmentKey];
|
|
259
|
+
if (!ciphertext) {
|
|
260
|
+
const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
261
|
+
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
262
|
+
throw err;
|
|
263
|
+
}
|
|
264
|
+
return { ciphertext, key };
|
|
265
|
+
}
|
|
266
|
+
function _vaultPath(options) {
|
|
267
|
+
let possibleVaultPath = null;
|
|
268
|
+
if (options && options.path && options.path.length > 0) {
|
|
269
|
+
if (Array.isArray(options.path)) {
|
|
270
|
+
for (const filepath of options.path) {
|
|
271
|
+
if (fs.existsSync(filepath)) {
|
|
272
|
+
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
|
|
280
|
+
}
|
|
281
|
+
if (fs.existsSync(possibleVaultPath)) {
|
|
282
|
+
return possibleVaultPath;
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
function _resolveHome(envPath) {
|
|
287
|
+
return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
288
|
+
}
|
|
289
|
+
function _configVault(options) {
|
|
290
|
+
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
291
|
+
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
292
|
+
if (debug || !quiet) {
|
|
293
|
+
_log("Loading env from encrypted .env.vault");
|
|
294
|
+
}
|
|
295
|
+
const parsed = DotenvModule._parseVault(options);
|
|
296
|
+
let processEnv = process.env;
|
|
297
|
+
if (options && options.processEnv != null) {
|
|
298
|
+
processEnv = options.processEnv;
|
|
299
|
+
}
|
|
300
|
+
DotenvModule.populate(processEnv, parsed, options);
|
|
301
|
+
return { parsed };
|
|
302
|
+
}
|
|
303
|
+
function configDotenv(options) {
|
|
304
|
+
const dotenvPath = path2.resolve(process.cwd(), ".env");
|
|
305
|
+
let encoding = "utf8";
|
|
306
|
+
let processEnv = process.env;
|
|
307
|
+
if (options && options.processEnv != null) {
|
|
308
|
+
processEnv = options.processEnv;
|
|
309
|
+
}
|
|
310
|
+
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
311
|
+
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
312
|
+
if (options && options.encoding) {
|
|
313
|
+
encoding = options.encoding;
|
|
314
|
+
} else {
|
|
315
|
+
if (debug) {
|
|
316
|
+
_debug("No encoding is specified. UTF-8 is used by default");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
let optionPaths = [dotenvPath];
|
|
320
|
+
if (options && options.path) {
|
|
321
|
+
if (!Array.isArray(options.path)) {
|
|
322
|
+
optionPaths = [_resolveHome(options.path)];
|
|
323
|
+
} else {
|
|
324
|
+
optionPaths = [];
|
|
325
|
+
for (const filepath of options.path) {
|
|
326
|
+
optionPaths.push(_resolveHome(filepath));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
let lastError;
|
|
331
|
+
const parsedAll = {};
|
|
332
|
+
for (const path3 of optionPaths) {
|
|
333
|
+
try {
|
|
334
|
+
const parsed = DotenvModule.parse(fs.readFileSync(path3, { encoding }));
|
|
335
|
+
DotenvModule.populate(parsedAll, parsed, options);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
if (debug) {
|
|
338
|
+
_debug(`Failed to load ${path3} ${e.message}`);
|
|
339
|
+
}
|
|
340
|
+
lastError = e;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
344
|
+
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
345
|
+
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
346
|
+
if (debug || !quiet) {
|
|
347
|
+
const keysCount = Object.keys(populated).length;
|
|
348
|
+
const shortPaths = [];
|
|
349
|
+
for (const filePath of optionPaths) {
|
|
350
|
+
try {
|
|
351
|
+
const relative = path2.relative(process.cwd(), filePath);
|
|
352
|
+
shortPaths.push(relative);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
if (debug) {
|
|
355
|
+
_debug(`Failed to load ${filePath} ${e.message}`);
|
|
356
|
+
}
|
|
357
|
+
lastError = e;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
361
|
+
}
|
|
362
|
+
if (lastError) {
|
|
363
|
+
return { parsed: parsedAll, error: lastError };
|
|
364
|
+
} else {
|
|
365
|
+
return { parsed: parsedAll };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function config(options) {
|
|
369
|
+
if (_dotenvKey(options).length === 0) {
|
|
370
|
+
return DotenvModule.configDotenv(options);
|
|
371
|
+
}
|
|
372
|
+
const vaultPath = _vaultPath(options);
|
|
373
|
+
if (!vaultPath) {
|
|
374
|
+
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
375
|
+
return DotenvModule.configDotenv(options);
|
|
376
|
+
}
|
|
377
|
+
return DotenvModule._configVault(options);
|
|
378
|
+
}
|
|
379
|
+
function decrypt2(encrypted, keyStr) {
|
|
380
|
+
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
381
|
+
let ciphertext = Buffer.from(encrypted, "base64");
|
|
382
|
+
const nonce = ciphertext.subarray(0, 12);
|
|
383
|
+
const authTag = ciphertext.subarray(-16);
|
|
384
|
+
ciphertext = ciphertext.subarray(12, -16);
|
|
385
|
+
try {
|
|
386
|
+
const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
|
|
387
|
+
aesgcm.setAuthTag(authTag);
|
|
388
|
+
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
389
|
+
} catch (error) {
|
|
390
|
+
const isRange = error instanceof RangeError;
|
|
391
|
+
const invalidKeyLength = error.message === "Invalid key length";
|
|
392
|
+
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
393
|
+
if (isRange || invalidKeyLength) {
|
|
394
|
+
const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
395
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
396
|
+
throw err;
|
|
397
|
+
} else if (decryptionFailed) {
|
|
398
|
+
const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
399
|
+
err.code = "DECRYPTION_FAILED";
|
|
400
|
+
throw err;
|
|
401
|
+
} else {
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function populate(processEnv, parsed, options = {}) {
|
|
407
|
+
const debug = Boolean(options && options.debug);
|
|
408
|
+
const override = Boolean(options && options.override);
|
|
409
|
+
const populated = {};
|
|
410
|
+
if (typeof parsed !== "object") {
|
|
411
|
+
const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
412
|
+
err.code = "OBJECT_REQUIRED";
|
|
413
|
+
throw err;
|
|
414
|
+
}
|
|
415
|
+
for (const key of Object.keys(parsed)) {
|
|
416
|
+
if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
417
|
+
if (override === true) {
|
|
418
|
+
processEnv[key] = parsed[key];
|
|
419
|
+
populated[key] = parsed[key];
|
|
420
|
+
}
|
|
421
|
+
if (debug) {
|
|
422
|
+
if (override === true) {
|
|
423
|
+
_debug(`"${key}" is already defined and WAS overwritten`);
|
|
424
|
+
} else {
|
|
425
|
+
_debug(`"${key}" is already defined and was NOT overwritten`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
processEnv[key] = parsed[key];
|
|
430
|
+
populated[key] = parsed[key];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return populated;
|
|
434
|
+
}
|
|
435
|
+
var DotenvModule = {
|
|
436
|
+
configDotenv,
|
|
437
|
+
_configVault,
|
|
438
|
+
_parseVault,
|
|
439
|
+
config,
|
|
440
|
+
decrypt: decrypt2,
|
|
441
|
+
parse,
|
|
442
|
+
populate
|
|
443
|
+
};
|
|
444
|
+
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
445
|
+
module.exports._configVault = DotenvModule._configVault;
|
|
446
|
+
module.exports._parseVault = DotenvModule._parseVault;
|
|
447
|
+
module.exports.config = DotenvModule.config;
|
|
448
|
+
module.exports.decrypt = DotenvModule.decrypt;
|
|
449
|
+
module.exports.parse = DotenvModule.parse;
|
|
450
|
+
module.exports.populate = DotenvModule.populate;
|
|
451
|
+
module.exports = DotenvModule;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// src/cli.ts
|
|
456
|
+
init_esm_shims();
|
|
457
|
+
|
|
458
|
+
// src/commands/index.ts
|
|
459
|
+
init_esm_shims();
|
|
460
|
+
|
|
461
|
+
// src/commands/login.ts
|
|
462
|
+
init_esm_shims();
|
|
463
|
+
|
|
464
|
+
// src/auth/index.ts
|
|
465
|
+
init_esm_shims();
|
|
466
|
+
|
|
467
|
+
// src/auth/credential-auth.ts
|
|
468
|
+
init_esm_shims();
|
|
469
|
+
|
|
21
470
|
// src/config/constants.ts
|
|
22
|
-
|
|
471
|
+
init_esm_shims();
|
|
472
|
+
var DOCYRUS_API_URL = "https://alpha-api.docyrus.com";
|
|
23
473
|
var OAUTH_CLIENT_ID = "90565525-8283-4881-82a9-8613eb82ae27";
|
|
24
474
|
var OAUTH_SCOPES = "offline_access Read.All Users.Read Users.Read.All DS.Read.All".split(" ");
|
|
25
475
|
var OAUTH_CALLBACK_PORT_MIN = 9876;
|
|
@@ -36,11 +486,13 @@ var TOKEN_KEYS = {
|
|
|
36
486
|
GITHUB_TOKEN: "githubToken"
|
|
37
487
|
// For private template access - persists after logout
|
|
38
488
|
};
|
|
489
|
+
var REGISTRY_BASE_URL = "https://ui.docy.app/r";
|
|
39
490
|
var CLI_NAME = "docyrus";
|
|
40
491
|
var CLI_VERSION = "0.0.1";
|
|
41
492
|
var NPM_PACKAGE_NAME = "docyrus";
|
|
42
493
|
|
|
43
494
|
// src/utils/errors.ts
|
|
495
|
+
init_esm_shims();
|
|
44
496
|
var CliError = class extends Error {
|
|
45
497
|
exitCode;
|
|
46
498
|
suggestion;
|
|
@@ -107,6 +559,36 @@ var ConflictingFlagsError = class extends CliError {
|
|
|
107
559
|
this.name = "ConflictingFlagsError";
|
|
108
560
|
}
|
|
109
561
|
};
|
|
562
|
+
var ComponentNotFoundError = class extends CliError {
|
|
563
|
+
constructor(name) {
|
|
564
|
+
super(
|
|
565
|
+
`"${name}" not found in the registry.`,
|
|
566
|
+
1,
|
|
567
|
+
"Check the name and try again. Run `docyrus add --help` for usage."
|
|
568
|
+
);
|
|
569
|
+
this.name = "ComponentNotFoundError";
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var PremiumAuthError = class extends CliError {
|
|
573
|
+
constructor(name) {
|
|
574
|
+
super(
|
|
575
|
+
`"${name}" is a premium component. Authentication required.`,
|
|
576
|
+
1,
|
|
577
|
+
"Run `docyrus login` to authenticate, then try again."
|
|
578
|
+
);
|
|
579
|
+
this.name = "PremiumAuthError";
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
var MissingConfigError = class extends CliError {
|
|
583
|
+
constructor() {
|
|
584
|
+
super(
|
|
585
|
+
"components.json not found.",
|
|
586
|
+
1,
|
|
587
|
+
"Run `npx shadcn@latest init` first to initialize your project."
|
|
588
|
+
);
|
|
589
|
+
this.name = "MissingConfigError";
|
|
590
|
+
}
|
|
591
|
+
};
|
|
110
592
|
|
|
111
593
|
// src/auth/credential-auth.ts
|
|
112
594
|
function getApiClient() {
|
|
@@ -195,6 +677,12 @@ async function refreshAccessToken(refreshToken) {
|
|
|
195
677
|
throw new AuthenticationError("Session expired. Please log in again.");
|
|
196
678
|
}
|
|
197
679
|
}
|
|
680
|
+
|
|
681
|
+
// src/auth/oauth-auth.ts
|
|
682
|
+
init_esm_shims();
|
|
683
|
+
|
|
684
|
+
// src/auth/oauth-server.ts
|
|
685
|
+
init_esm_shims();
|
|
198
686
|
var SUCCESS_HTML = `
|
|
199
687
|
<!DOCTYPE html>
|
|
200
688
|
<html>
|
|
@@ -377,7 +865,7 @@ async function startCallbackServer(expectedState) {
|
|
|
377
865
|
rejectResult(new TimeoutError("OAuth authentication timed out."));
|
|
378
866
|
}, OAUTH_TIMEOUT_MS);
|
|
379
867
|
const server = createServer((req, res) => {
|
|
380
|
-
const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
|
|
868
|
+
const url = new URL$1(req.url || "/", `http://127.0.0.1:${port}`);
|
|
381
869
|
if (url.pathname === "/callback") {
|
|
382
870
|
const code = url.searchParams.get("code");
|
|
383
871
|
const state2 = url.searchParams.get("state");
|
|
@@ -421,6 +909,9 @@ async function startCallbackServer(expectedState) {
|
|
|
421
909
|
cleanup
|
|
422
910
|
};
|
|
423
911
|
}
|
|
912
|
+
|
|
913
|
+
// src/utils/browser.ts
|
|
914
|
+
init_esm_shims();
|
|
424
915
|
async function openBrowser(url) {
|
|
425
916
|
await open(url);
|
|
426
917
|
}
|
|
@@ -505,12 +996,24 @@ async function loginWithOAuth(options = {}) {
|
|
|
505
996
|
cleanup();
|
|
506
997
|
}
|
|
507
998
|
}
|
|
999
|
+
|
|
1000
|
+
// src/storage/index.ts
|
|
1001
|
+
init_esm_shims();
|
|
1002
|
+
|
|
1003
|
+
// src/storage/file-storage.ts
|
|
1004
|
+
init_esm_shims();
|
|
1005
|
+
|
|
1006
|
+
// src/utils/platform.ts
|
|
1007
|
+
init_esm_shims();
|
|
508
1008
|
function getHomeDir() {
|
|
509
1009
|
return homedir();
|
|
510
1010
|
}
|
|
511
1011
|
function getConfigDir() {
|
|
512
1012
|
return join(getHomeDir(), CONFIG_DIR);
|
|
513
1013
|
}
|
|
1014
|
+
|
|
1015
|
+
// src/storage/encryption.ts
|
|
1016
|
+
init_esm_shims();
|
|
514
1017
|
var ALGORITHM = "aes-256-gcm";
|
|
515
1018
|
var KEY_LENGTH = 32;
|
|
516
1019
|
var IV_LENGTH = 16;
|
|
@@ -626,6 +1129,7 @@ var FileStorage = class {
|
|
|
626
1129
|
};
|
|
627
1130
|
|
|
628
1131
|
// src/storage/keychain-storage.ts
|
|
1132
|
+
init_esm_shims();
|
|
629
1133
|
var keytar = null;
|
|
630
1134
|
async function getKeytar() {
|
|
631
1135
|
if (keytar !== null) {
|
|
@@ -696,6 +1200,7 @@ var KeychainStorage = class {
|
|
|
696
1200
|
};
|
|
697
1201
|
|
|
698
1202
|
// src/storage/cli-token-manager.ts
|
|
1203
|
+
init_esm_shims();
|
|
699
1204
|
var CliTokenManager = class {
|
|
700
1205
|
keychain;
|
|
701
1206
|
fileStorage;
|
|
@@ -795,8 +1300,15 @@ function getTokenManager() {
|
|
|
795
1300
|
}
|
|
796
1301
|
return instance;
|
|
797
1302
|
}
|
|
1303
|
+
|
|
1304
|
+
// src/config/index.ts
|
|
1305
|
+
init_esm_shims();
|
|
1306
|
+
|
|
1307
|
+
// src/config/config-manager.ts
|
|
1308
|
+
init_esm_shims();
|
|
798
1309
|
var defaults = {
|
|
799
|
-
telemetryEnabled: true
|
|
1310
|
+
telemetryEnabled: true,
|
|
1311
|
+
helpTheme: "default"
|
|
800
1312
|
};
|
|
801
1313
|
var ConfigManager = class {
|
|
802
1314
|
config;
|
|
@@ -826,6 +1338,12 @@ var ConfigManager = class {
|
|
|
826
1338
|
}
|
|
827
1339
|
};
|
|
828
1340
|
var configManager = new ConfigManager();
|
|
1341
|
+
|
|
1342
|
+
// src/ui/index.ts
|
|
1343
|
+
init_esm_shims();
|
|
1344
|
+
|
|
1345
|
+
// src/ui/logger.ts
|
|
1346
|
+
init_esm_shims();
|
|
829
1347
|
var logger = {
|
|
830
1348
|
success(message) {
|
|
831
1349
|
console.info(chalk4.green("\u2713"), message);
|
|
@@ -860,6 +1378,9 @@ var logger = {
|
|
|
860
1378
|
return chalk4.cyan.underline(url);
|
|
861
1379
|
}
|
|
862
1380
|
};
|
|
1381
|
+
|
|
1382
|
+
// src/ui/output.ts
|
|
1383
|
+
init_esm_shims();
|
|
863
1384
|
var state = {
|
|
864
1385
|
format: "text",
|
|
865
1386
|
data: {}
|
|
@@ -944,6 +1465,7 @@ var output = {
|
|
|
944
1465
|
};
|
|
945
1466
|
|
|
946
1467
|
// src/ui/messages.ts
|
|
1468
|
+
init_esm_shims();
|
|
947
1469
|
var MESSAGES = {
|
|
948
1470
|
// Login
|
|
949
1471
|
LOGIN_SUCCESS: (email) => `Successfully logged in as ${email}`,
|
|
@@ -990,7 +1512,7 @@ var MESSAGES = {
|
|
|
990
1512
|
CREATE_SUCCESS: "Project created successfully!",
|
|
991
1513
|
CREATE_DOWNLOAD_OPENAPI: "Download OpenAPI spec for code generation?",
|
|
992
1514
|
CREATE_DOWNLOADING_OPENAPI: "Downloading OpenAPI specification...",
|
|
993
|
-
CREATE_OPENAPI_SUCCESS: (
|
|
1515
|
+
CREATE_OPENAPI_SUCCESS: (path2) => `OpenAPI spec saved to ${path2}`,
|
|
994
1516
|
// TanStack DB Generator
|
|
995
1517
|
TANSTACK_GENERATOR_PROMPT: "Generate TanStack Query collections from OpenAPI spec?",
|
|
996
1518
|
TANSTACK_GENERATOR_RUNNING: "Generating TanStack Query collections...",
|
|
@@ -1012,10 +1534,58 @@ var MESSAGES = {
|
|
|
1012
1534
|
DOCYRUS_TOKEN_REQUIRED: "Docyrus token required for template download",
|
|
1013
1535
|
DOCYRUS_TOKEN_PROMPT: "Enter Docyrus token:",
|
|
1014
1536
|
DOCYRUS_TOKEN_SAVED: "Docyrus token saved successfully",
|
|
1015
|
-
DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again."
|
|
1537
|
+
DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again.",
|
|
1538
|
+
// Add command
|
|
1539
|
+
ADD_RESOLVING: "Resolving components and dependencies...",
|
|
1540
|
+
ADD_INSTALLING_ITEM: (name) => `Installing ${name}...`,
|
|
1541
|
+
ADD_INSTALLING_DEPS: (count) => `Installing ${count} dependenc${count === 1 ? "y" : "ies"}...`,
|
|
1542
|
+
ADD_SUCCESS: (items, files) => `Done! Added ${items} item(s), ${files} file(s).`,
|
|
1543
|
+
ADD_NO_ITEMS: "Please specify at least one item to add.",
|
|
1544
|
+
ADD_NOT_FOUND: (name) => `"${name}" not found in the registry.`,
|
|
1545
|
+
ADD_PREMIUM_AUTH: (name) => `"${name}" is a premium component. Authentication required.`,
|
|
1546
|
+
ADD_FILE_EXISTS: (path2) => `File ${path2} already exists. Overwrite?`,
|
|
1547
|
+
ADD_FILE_SKIPPED: (path2) => `Skipped: ${path2}`,
|
|
1548
|
+
ADD_DRY_RUN_HEADER: "Dry run \u2014 the following changes would be made:",
|
|
1549
|
+
ADD_MISSING_CONFIG: "components.json not found.",
|
|
1550
|
+
ADD_MISSING_CONFIG_HINT: "Run `npx shadcn@latest init` first to initialize your project.",
|
|
1551
|
+
ADD_DETECTED_STYLE: (style) => `Detected style: ${style}`,
|
|
1552
|
+
ADD_DEPRECATED_BASE_STYLE: 'Your components.json uses a deprecated "base-*" style. The radix/base variant distinction has been removed. All components now use the unified registry (@docyrus/ui-*).',
|
|
1553
|
+
// List command
|
|
1554
|
+
LIST_HEADER: "Available Docyrus UI registry items:",
|
|
1555
|
+
LIST_FETCHING: "Fetching registry index...",
|
|
1556
|
+
LIST_FETCHING_NPM: "Fetching @docyrus npm packages...",
|
|
1557
|
+
LIST_NPM_SUCCESS: "Fetched npm packages",
|
|
1558
|
+
LIST_INSTALL_HINT: "Install with: docyrus add <name>",
|
|
1559
|
+
LIST_NPM_HEADER: "Published @docyrus npm packages:",
|
|
1560
|
+
LIST_NPM_LINK: "https://www.npmjs.com/settings/docyrus/packages",
|
|
1561
|
+
// Commitlint command
|
|
1562
|
+
COMMITLINT_DESCRIPTION: "Set up commitlint, husky, and lint-staged in your project",
|
|
1563
|
+
COMMITLINT_NO_PROJECT: "Could not find a package.json.",
|
|
1564
|
+
COMMITLINT_NO_PROJECT_HINT: "Run this command from within a JavaScript/TypeScript project.",
|
|
1565
|
+
COMMITLINT_IS_MONOREPO: "Is this a monorepo?",
|
|
1566
|
+
COMMITLINT_DETECTED_WORKSPACES: (count) => `Detected ${count} workspace(s)`,
|
|
1567
|
+
COMMITLINT_NO_WORKSPACES: "No workspaces detected. Enter scopes manually.",
|
|
1568
|
+
COMMITLINT_SCOPE_STRATEGY: "How should scopes be used?",
|
|
1569
|
+
COMMITLINT_SELECT_SCOPES: "Select scopes to include:",
|
|
1570
|
+
COMMITLINT_ADDITIONAL_SCOPES: "Enter additional scopes (comma-separated, or leave empty):",
|
|
1571
|
+
COMMITLINT_CONFIRM_SCOPES: (scopes) => `Scopes: ${scopes.join(", ")}`,
|
|
1572
|
+
COMMITLINT_CONFIG_EXISTS: "commitlint.config.ts already exists. Overwrite?",
|
|
1573
|
+
COMMITLINT_HUSKY_EXISTS: ".husky/ directory already exists. Overwrite hooks?",
|
|
1574
|
+
COMMITLINT_STEP_CONFIG: "Creating commitlint.config.ts",
|
|
1575
|
+
COMMITLINT_STEP_HUSKY: "Setting up Husky hooks",
|
|
1576
|
+
COMMITLINT_STEP_SCRIPT: "Creating commit-linter.sh script",
|
|
1577
|
+
COMMITLINT_STEP_PACKAGE_JSON: "Updating package.json",
|
|
1578
|
+
COMMITLINT_STEP_INSTALL: "Installing dependencies",
|
|
1579
|
+
COMMITLINT_STEP_INIT_HUSKY: "Initializing Husky",
|
|
1580
|
+
COMMITLINT_SUCCESS: "Commitlint setup complete!",
|
|
1581
|
+
COMMITLINT_SUCCESS_HINT: "Your commits will now be validated against Conventional Commits format.",
|
|
1582
|
+
COMMITLINT_DETECTED_PM: (pm) => `Package manager: ${pm}`
|
|
1016
1583
|
};
|
|
1584
|
+
|
|
1585
|
+
// src/ui/spinner.ts
|
|
1586
|
+
init_esm_shims();
|
|
1017
1587
|
function createSpinner(text) {
|
|
1018
|
-
return
|
|
1588
|
+
return ora2({
|
|
1019
1589
|
text,
|
|
1020
1590
|
spinner: "dots"
|
|
1021
1591
|
});
|
|
@@ -1042,6 +1612,9 @@ async function withSpinner(text, fn, options) {
|
|
|
1042
1612
|
throw error;
|
|
1043
1613
|
}
|
|
1044
1614
|
}
|
|
1615
|
+
|
|
1616
|
+
// src/ui/prompts.ts
|
|
1617
|
+
init_esm_shims();
|
|
1045
1618
|
async function promptEmail(defaultValue) {
|
|
1046
1619
|
return input({
|
|
1047
1620
|
message: MESSAGES.LOGIN_PROMPT_EMAIL,
|
|
@@ -1066,12 +1639,80 @@ async function promptPassword() {
|
|
|
1066
1639
|
}
|
|
1067
1640
|
});
|
|
1068
1641
|
}
|
|
1069
|
-
|
|
1642
|
+
|
|
1643
|
+
// src/ui/progress.ts
|
|
1644
|
+
init_esm_shims();
|
|
1645
|
+
var SYMBOLS = {
|
|
1070
1646
|
pending: chalk4.dim("\u25CB"),
|
|
1071
1647
|
in_progress: chalk4.cyan("\u25D0"),
|
|
1072
1648
|
completed: chalk4.green("\u2713"),
|
|
1073
1649
|
failed: chalk4.red("\u2717")
|
|
1074
|
-
}
|
|
1650
|
+
};
|
|
1651
|
+
function createMultiStepProgress(title, stepLabels) {
|
|
1652
|
+
const steps = stepLabels.map((label) => ({
|
|
1653
|
+
label,
|
|
1654
|
+
status: "pending"
|
|
1655
|
+
}));
|
|
1656
|
+
let spinner = null;
|
|
1657
|
+
let currentStep = -1;
|
|
1658
|
+
const render = () => {
|
|
1659
|
+
console.log();
|
|
1660
|
+
console.log(chalk4.bold(title));
|
|
1661
|
+
console.log();
|
|
1662
|
+
for (const step of steps) {
|
|
1663
|
+
const symbol = SYMBOLS[step.status];
|
|
1664
|
+
const text = step.status === "in_progress" ? chalk4.cyan(step.label) : step.status === "completed" ? chalk4.green(step.label) : step.status === "failed" ? chalk4.red(step.label) : chalk4.dim(step.label);
|
|
1665
|
+
console.log(` ${symbol} ${text}`);
|
|
1666
|
+
if (step.status === "failed" && step.error) {
|
|
1667
|
+
console.log(` ${chalk4.red(step.error)}`);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
console.log();
|
|
1671
|
+
};
|
|
1672
|
+
return {
|
|
1673
|
+
start(stepIndex) {
|
|
1674
|
+
if (stepIndex < 0 || stepIndex >= steps.length) return;
|
|
1675
|
+
if (spinner) {
|
|
1676
|
+
spinner.stop();
|
|
1677
|
+
}
|
|
1678
|
+
currentStep = stepIndex;
|
|
1679
|
+
steps[stepIndex].status = "in_progress";
|
|
1680
|
+
spinner = ora2({
|
|
1681
|
+
text: steps[stepIndex].label,
|
|
1682
|
+
color: "cyan"
|
|
1683
|
+
}).start();
|
|
1684
|
+
},
|
|
1685
|
+
complete(stepIndex) {
|
|
1686
|
+
if (stepIndex < 0 || stepIndex >= steps.length) return;
|
|
1687
|
+
if (spinner && currentStep === stepIndex) {
|
|
1688
|
+
spinner.succeed(chalk4.green(steps[stepIndex].label));
|
|
1689
|
+
spinner = null;
|
|
1690
|
+
}
|
|
1691
|
+
steps[stepIndex].status = "completed";
|
|
1692
|
+
},
|
|
1693
|
+
fail(stepIndex, error) {
|
|
1694
|
+
if (stepIndex < 0 || stepIndex >= steps.length) return;
|
|
1695
|
+
if (spinner && currentStep === stepIndex) {
|
|
1696
|
+
spinner.fail(chalk4.red(steps[stepIndex].label));
|
|
1697
|
+
spinner = null;
|
|
1698
|
+
}
|
|
1699
|
+
steps[stepIndex].status = "failed";
|
|
1700
|
+
steps[stepIndex].error = error;
|
|
1701
|
+
},
|
|
1702
|
+
update(stepIndex, message) {
|
|
1703
|
+
if (spinner && currentStep === stepIndex) {
|
|
1704
|
+
spinner.text = message;
|
|
1705
|
+
}
|
|
1706
|
+
},
|
|
1707
|
+
finish() {
|
|
1708
|
+
if (spinner) {
|
|
1709
|
+
spinner.stop();
|
|
1710
|
+
spinner = null;
|
|
1711
|
+
}
|
|
1712
|
+
render();
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1075
1716
|
function createSimpleProgress() {
|
|
1076
1717
|
let spinner = null;
|
|
1077
1718
|
return (step, current, total) => {
|
|
@@ -1080,7 +1721,7 @@ function createSimpleProgress() {
|
|
|
1080
1721
|
spinner = null;
|
|
1081
1722
|
}
|
|
1082
1723
|
const text = current && total ? `${step} (${current}/${total})` : step;
|
|
1083
|
-
spinner =
|
|
1724
|
+
spinner = ora2({
|
|
1084
1725
|
text,
|
|
1085
1726
|
color: "cyan"
|
|
1086
1727
|
}).start();
|
|
@@ -1091,6 +1732,71 @@ function createSimpleProgress() {
|
|
|
1091
1732
|
};
|
|
1092
1733
|
}
|
|
1093
1734
|
|
|
1735
|
+
// src/ui/help.ts
|
|
1736
|
+
init_esm_shims();
|
|
1737
|
+
var palettes = {
|
|
1738
|
+
default: {
|
|
1739
|
+
title: (str) => chalk4.bold.cyan(str),
|
|
1740
|
+
usage: (str) => chalk4.yellow(str),
|
|
1741
|
+
command: (str) => chalk4.green(str),
|
|
1742
|
+
argument: (str) => chalk4.magenta(str),
|
|
1743
|
+
description: (str) => chalk4.dim(str)
|
|
1744
|
+
},
|
|
1745
|
+
monochrome: {
|
|
1746
|
+
title: (str) => chalk4.bold(str),
|
|
1747
|
+
usage: (str) => chalk4.bold(str),
|
|
1748
|
+
command: (str) => chalk4.white(str),
|
|
1749
|
+
argument: (str) => chalk4.underline(str),
|
|
1750
|
+
description: (str) => chalk4.dim(str)
|
|
1751
|
+
},
|
|
1752
|
+
warm: {
|
|
1753
|
+
title: (str) => chalk4.bold.yellow(str),
|
|
1754
|
+
usage: (str) => chalk4.bold.red(str),
|
|
1755
|
+
command: (str) => chalk4.yellow(str),
|
|
1756
|
+
argument: (str) => chalk4.red(str),
|
|
1757
|
+
description: (str) => chalk4.dim(str)
|
|
1758
|
+
},
|
|
1759
|
+
cool: {
|
|
1760
|
+
title: (str) => chalk4.bold.blue(str),
|
|
1761
|
+
usage: (str) => chalk4.cyan(str),
|
|
1762
|
+
command: (str) => chalk4.blue(str),
|
|
1763
|
+
argument: (str) => chalk4.cyan(str),
|
|
1764
|
+
description: (str) => chalk4.dim(str)
|
|
1765
|
+
},
|
|
1766
|
+
neon: {
|
|
1767
|
+
title: (str) => chalk4.bold.magenta(str),
|
|
1768
|
+
usage: (str) => chalk4.cyan(str),
|
|
1769
|
+
command: (str) => chalk4.green(str),
|
|
1770
|
+
argument: (str) => chalk4.yellow(str),
|
|
1771
|
+
description: (str) => chalk4.dim(str)
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
var HELP_THEMES = [
|
|
1775
|
+
"default",
|
|
1776
|
+
"monochrome",
|
|
1777
|
+
"warm",
|
|
1778
|
+
"cool",
|
|
1779
|
+
"neon"
|
|
1780
|
+
];
|
|
1781
|
+
function previewTheme(theme) {
|
|
1782
|
+
const p = palettes[theme];
|
|
1783
|
+
return `${p.title("Commands:")} ${p.command("add")} ${p.argument("<name>")} ${p.description("description")}`;
|
|
1784
|
+
}
|
|
1785
|
+
function getHelpConfig(theme = "default") {
|
|
1786
|
+
const p = palettes[theme];
|
|
1787
|
+
return {
|
|
1788
|
+
sortSubcommands: true,
|
|
1789
|
+
sortOptions: true,
|
|
1790
|
+
styleTitle: p.title,
|
|
1791
|
+
styleUsage: p.usage,
|
|
1792
|
+
styleCommandText: p.command,
|
|
1793
|
+
styleOptionText: p.command,
|
|
1794
|
+
styleArgumentText: p.argument,
|
|
1795
|
+
styleSubcommandText: p.command,
|
|
1796
|
+
styleDescriptionText: p.description
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1094
1800
|
// src/commands/login.ts
|
|
1095
1801
|
async function handleCredentialLogin(email) {
|
|
1096
1802
|
const lastEmail = configManager.get("lastLoginEmail");
|
|
@@ -1170,6 +1876,7 @@ function registerLoginCommand(program2) {
|
|
|
1170
1876
|
}
|
|
1171
1877
|
|
|
1172
1878
|
// src/commands/logout.ts
|
|
1879
|
+
init_esm_shims();
|
|
1173
1880
|
function registerLogoutCommand(program2) {
|
|
1174
1881
|
program2.command("logout").description("Log out from Docyrus").action(async () => {
|
|
1175
1882
|
const tokenManager = getTokenManager();
|
|
@@ -1192,7 +1899,11 @@ function registerLogoutCommand(program2) {
|
|
|
1192
1899
|
});
|
|
1193
1900
|
}
|
|
1194
1901
|
|
|
1902
|
+
// src/commands/whoami.ts
|
|
1903
|
+
init_esm_shims();
|
|
1904
|
+
|
|
1195
1905
|
// src/utils/auth-guard.ts
|
|
1906
|
+
init_esm_shims();
|
|
1196
1907
|
function decodeJwtPayload(token) {
|
|
1197
1908
|
try {
|
|
1198
1909
|
const parts = token.split(".");
|
|
@@ -1286,6 +1997,12 @@ function registerWhoamiCommand(program2) {
|
|
|
1286
1997
|
}
|
|
1287
1998
|
});
|
|
1288
1999
|
}
|
|
2000
|
+
|
|
2001
|
+
// src/commands/create.ts
|
|
2002
|
+
init_esm_shims();
|
|
2003
|
+
|
|
2004
|
+
// src/utils/linter-config.ts
|
|
2005
|
+
init_esm_shims();
|
|
1289
2006
|
var ESLINT_CONFIGS = {
|
|
1290
2007
|
react: {
|
|
1291
2008
|
imports: ["baseConfig", "reactConfig"],
|
|
@@ -1354,6 +2071,9 @@ async function applyLinterConfig(targetDir, framework, linter, onProgress) {
|
|
|
1354
2071
|
};
|
|
1355
2072
|
await writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2));
|
|
1356
2073
|
}
|
|
2074
|
+
|
|
2075
|
+
// src/utils/alias-config.ts
|
|
2076
|
+
init_esm_shims();
|
|
1357
2077
|
async function applyAliasConfig(targetDir, framework, aliasPrefix) {
|
|
1358
2078
|
if (aliasPrefix === "@" || aliasPrefix === "@/") return;
|
|
1359
2079
|
const config = {
|
|
@@ -1426,6 +2146,9 @@ async function updateImportsRecursive(dir, newPrefix, framework) {
|
|
|
1426
2146
|
}
|
|
1427
2147
|
}
|
|
1428
2148
|
}
|
|
2149
|
+
|
|
2150
|
+
// src/utils/api-client.ts
|
|
2151
|
+
init_esm_shims();
|
|
1429
2152
|
function createAuthenticatedClient(accessToken) {
|
|
1430
2153
|
const apiUrl = configManager.get("apiUrl") || DOCYRUS_API_URL;
|
|
1431
2154
|
const client = new RestApiClient({ baseURL: apiUrl });
|
|
@@ -1441,6 +2164,9 @@ async function downloadOpenApiSpec(accessToken, targetDir, filename = "openapi.j
|
|
|
1441
2164
|
await writeFile(targetPath, JSON.stringify(spec, null, 2));
|
|
1442
2165
|
return targetPath;
|
|
1443
2166
|
}
|
|
2167
|
+
|
|
2168
|
+
// src/utils/ui-variant-swap.ts
|
|
2169
|
+
init_esm_shims();
|
|
1444
2170
|
async function applyUIVariants(targetDir, uiLibrary) {
|
|
1445
2171
|
const variantsDir = join(targetDir, "__ui-variants__");
|
|
1446
2172
|
try {
|
|
@@ -1458,6 +2184,9 @@ async function applyUIVariants(targetDir, uiLibrary) {
|
|
|
1458
2184
|
}
|
|
1459
2185
|
await rm(variantsDir, { recursive: true, force: true });
|
|
1460
2186
|
}
|
|
2187
|
+
|
|
2188
|
+
// src/utils/ui-library-setup.ts
|
|
2189
|
+
init_esm_shims();
|
|
1461
2190
|
var execAsync = promisify(exec);
|
|
1462
2191
|
var DICEUI_COMPONENTS = "action-bar,avatar-group,badge-overflow,checkbox-group,circular-progress,color-picker,color-swatch,combobox,compare-slider,cropper,editable,file-upload,gauge,kanban,key-value,listbox,mask-input,media-player,mention,phone-input,qr-code,rating,relative-time-card,responsive-dialog,scroll-spy,scroller,segmented-input,sortable,speed-dial,stack,stat,status,stepper,swap,tags-input,time-picker,timeline,tour".split(",");
|
|
1463
2192
|
var UI_LIBRARY_COMPATIBILITY = {
|
|
@@ -1707,6 +2436,9 @@ async function addHeroUIProviderReact(targetDir) {
|
|
|
1707
2436
|
} catch {
|
|
1708
2437
|
}
|
|
1709
2438
|
}
|
|
2439
|
+
|
|
2440
|
+
// src/utils/state-management-setup.ts
|
|
2441
|
+
init_esm_shims();
|
|
1710
2442
|
var STATE_MANAGEMENT_VERSIONS = {
|
|
1711
2443
|
zustand: { name: "zustand", version: "5.0.11" },
|
|
1712
2444
|
"tanstack-query": { name: "@tanstack/react-query", version: "5.90.20" },
|
|
@@ -1901,7 +2633,7 @@ import { vueQueryOptions } from './lib/query-client';
|
|
|
1901
2633
|
|
|
1902
2634
|
// src/commands/create.ts
|
|
1903
2635
|
var execAsync2 = promisify(exec);
|
|
1904
|
-
var SHADCN_BASE_COLORS = "zinc,
|
|
2636
|
+
var SHADCN_BASE_COLORS = "slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky".split(",");
|
|
1905
2637
|
function validateConflictingFlags(options) {
|
|
1906
2638
|
const frameworkFlags = [
|
|
1907
2639
|
options.nextjs,
|
|
@@ -2069,13 +2801,13 @@ var PACKAGE_MANAGERS = [
|
|
|
2069
2801
|
{ name: "yarn", label: "yarn", description: "Fast, reliable, and secure" },
|
|
2070
2802
|
{ name: "bun", label: "bun", description: "All-in-one JavaScript runtime" }
|
|
2071
2803
|
];
|
|
2072
|
-
var TEMPLATE_REPO_BASE = "Docyrus/docyrus-
|
|
2804
|
+
var TEMPLATE_REPO_BASE = "Docyrus/docyrus-ui/templates";
|
|
2073
2805
|
function getTemplateRepo(framework) {
|
|
2074
2806
|
return `${TEMPLATE_REPO_BASE}/${framework}`;
|
|
2075
2807
|
}
|
|
2076
|
-
async function directoryExists(
|
|
2808
|
+
async function directoryExists(path2) {
|
|
2077
2809
|
try {
|
|
2078
|
-
await access(
|
|
2810
|
+
await access(path2, constants.F_OK);
|
|
2079
2811
|
return true;
|
|
2080
2812
|
} catch {
|
|
2081
2813
|
return false;
|
|
@@ -2265,20 +2997,36 @@ Examples:
|
|
|
2265
2997
|
);
|
|
2266
2998
|
}
|
|
2267
2999
|
await cp(localTemplateDir, targetDir, { recursive: true });
|
|
3000
|
+
if (!process.env.DOCYRUS_DEVKIT_PATH) {
|
|
3001
|
+
const { config } = await Promise.resolve().then(() => __toESM(require_main(), 1));
|
|
3002
|
+
config({ path: resolve(cliPackageDir, "..", "..", ".env") });
|
|
3003
|
+
}
|
|
2268
3004
|
const pkgJsonPath = join(targetDir, "package.json");
|
|
2269
3005
|
const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
|
|
2270
|
-
const
|
|
3006
|
+
const monorepoRoot = resolve(cliPackageDir, "..", "..");
|
|
3007
|
+
const devkitPath = process.env.DOCYRUS_DEVKIT_PATH;
|
|
3008
|
+
const packagesDirs = [
|
|
3009
|
+
resolve(monorepoRoot, "packages"),
|
|
3010
|
+
...devkitPath ? [resolve(devkitPath, "packages")] : [resolve(monorepoRoot, "..", "docyrus-devkit", "packages")]
|
|
3011
|
+
];
|
|
2271
3012
|
for (const depType of ["dependencies", "devDependencies"]) {
|
|
2272
3013
|
const deps = pkgJson[depType];
|
|
2273
3014
|
if (!deps) continue;
|
|
2274
3015
|
for (const [name2, version] of Object.entries(deps)) {
|
|
2275
3016
|
if (name2.startsWith("@docyrus/") && version === "latest") {
|
|
2276
3017
|
const pkgName = name2.replace("@docyrus/", "");
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
3018
|
+
let found = false;
|
|
3019
|
+
for (const packagesDir of packagesDirs) {
|
|
3020
|
+
const localPkgDir = resolve(packagesDir, pkgName);
|
|
3021
|
+
try {
|
|
3022
|
+
await access(localPkgDir, constants.F_OK);
|
|
3023
|
+
deps[name2] = `file:${localPkgDir}`;
|
|
3024
|
+
found = true;
|
|
3025
|
+
break;
|
|
3026
|
+
} catch {
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (!found) {
|
|
2282
3030
|
logger.warn(`Local package not found for ${name2}, keeping "latest"`);
|
|
2283
3031
|
}
|
|
2284
3032
|
}
|
|
@@ -2303,7 +3051,7 @@ Examples:
|
|
|
2303
3051
|
await withSpinner(
|
|
2304
3052
|
MESSAGES.CREATE_DOWNLOADING,
|
|
2305
3053
|
async () => {
|
|
2306
|
-
const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-
|
|
3054
|
+
const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-ui");
|
|
2307
3055
|
if (existsSync(gigetCachePath)) {
|
|
2308
3056
|
await rm(gigetCachePath, { recursive: true, force: true });
|
|
2309
3057
|
}
|
|
@@ -2455,8 +3203,11 @@ ${errorMessage}`
|
|
|
2455
3203
|
logger.dim(MESSAGES.API_CLIENT_DOCS);
|
|
2456
3204
|
});
|
|
2457
3205
|
}
|
|
3206
|
+
|
|
3207
|
+
// src/commands/generate.ts
|
|
3208
|
+
init_esm_shims();
|
|
2458
3209
|
function registerGenerateCommand(program2) {
|
|
2459
|
-
const generate = program2.command("generate").description("
|
|
3210
|
+
const generate = program2.command("generate").description("Generate code from OpenAPI specs (e.g. TanStack Query collections)");
|
|
2460
3211
|
generate.command("api-spec").description("Download OpenAPI specification from Docyrus API").option("-o, --output <path>", "Output file path", "openapi.json").action(async (options) => {
|
|
2461
3212
|
const token = await requireAuth();
|
|
2462
3213
|
const outputPath = resolve(options.output);
|
|
@@ -2539,6 +3290,9 @@ function findSpecFile(specPath) {
|
|
|
2539
3290
|
}
|
|
2540
3291
|
return null;
|
|
2541
3292
|
}
|
|
3293
|
+
|
|
3294
|
+
// src/commands/upgrade.ts
|
|
3295
|
+
init_esm_shims();
|
|
2542
3296
|
var execAsync3 = promisify(exec);
|
|
2543
3297
|
async function getLatestVersion() {
|
|
2544
3298
|
try {
|
|
@@ -2633,6 +3387,7 @@ function registerUpgradeCommand(program2) {
|
|
|
2633
3387
|
}
|
|
2634
3388
|
|
|
2635
3389
|
// src/commands/completion.ts
|
|
3390
|
+
init_esm_shims();
|
|
2636
3391
|
var BASH_COMPLETION = `
|
|
2637
3392
|
###-begin-${CLI_NAME}-completions-###
|
|
2638
3393
|
_${CLI_NAME}_completions() {
|
|
@@ -2641,11 +3396,11 @@ _${CLI_NAME}_completions() {
|
|
|
2641
3396
|
args=("\${COMP_WORDS[@]}")
|
|
2642
3397
|
|
|
2643
3398
|
# Commands
|
|
2644
|
-
type_list="login logout whoami create generate upgrade completion info help"
|
|
3399
|
+
type_list="login logout whoami create add commitlint generate upgrade completion info help"
|
|
2645
3400
|
|
|
2646
3401
|
# Subcommands for generate
|
|
2647
3402
|
if [[ \${args[1]} == "generate" ]]; then
|
|
2648
|
-
type_list="db"
|
|
3403
|
+
type_list="api-spec db"
|
|
2649
3404
|
fi
|
|
2650
3405
|
|
|
2651
3406
|
COMPREPLY=($(compgen -W "\${type_list}" -- \${cur_word}))
|
|
@@ -2663,15 +3418,18 @@ _${CLI_NAME}() {
|
|
|
2663
3418
|
'logout:Log out from Docyrus'
|
|
2664
3419
|
'whoami:Display the current logged-in user'
|
|
2665
3420
|
'create:Create a new Docyrus project from template'
|
|
2666
|
-
'generate:
|
|
3421
|
+
'generate:Generate code from OpenAPI specs'
|
|
2667
3422
|
'upgrade:Upgrade Docyrus CLI to the latest version'
|
|
2668
3423
|
'completion:Generate shell completion script'
|
|
3424
|
+
'add:Add a component from the registry'
|
|
3425
|
+
'commitlint:Set up commitlint, husky, and lint-staged'
|
|
2669
3426
|
'info:Display CLI and environment information'
|
|
2670
3427
|
'help:Display help for command'
|
|
2671
3428
|
)
|
|
2672
3429
|
|
|
2673
3430
|
local -a generate_commands
|
|
2674
3431
|
generate_commands=(
|
|
3432
|
+
'api-spec:Download OpenAPI specification from Docyrus API'
|
|
2675
3433
|
'db:Generate TanStack Query collections from OpenAPI spec'
|
|
2676
3434
|
)
|
|
2677
3435
|
|
|
@@ -2704,13 +3462,16 @@ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "login" -d "Log in to Docy
|
|
|
2704
3462
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "logout" -d "Log out from Docyrus"
|
|
2705
3463
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "whoami" -d "Display the current logged-in user"
|
|
2706
3464
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "create" -d "Create a new Docyrus project"
|
|
2707
|
-
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "generate" -d "
|
|
3465
|
+
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "generate" -d "Generate code from OpenAPI specs"
|
|
2708
3466
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "upgrade" -d "Upgrade Docyrus CLI"
|
|
2709
3467
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "completion" -d "Generate shell completion"
|
|
3468
|
+
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "add" -d "Add a component from the registry"
|
|
3469
|
+
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "commitlint" -d "Set up commitlint and husky"
|
|
2710
3470
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "info" -d "Display CLI and environment information"
|
|
2711
3471
|
complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "help" -d "Display help"
|
|
2712
3472
|
|
|
2713
3473
|
# generate subcommands
|
|
3474
|
+
complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "api-spec" -d "Download OpenAPI specification"
|
|
2714
3475
|
complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "db" -d "Generate TanStack Query collections"
|
|
2715
3476
|
###-end-${CLI_NAME}-completions-###
|
|
2716
3477
|
`.trim();
|
|
@@ -2752,6 +3513,9 @@ function registerCompletionCommand(program2) {
|
|
|
2752
3513
|
}
|
|
2753
3514
|
});
|
|
2754
3515
|
}
|
|
3516
|
+
|
|
3517
|
+
// src/commands/info.ts
|
|
3518
|
+
init_esm_shims();
|
|
2755
3519
|
function registerInfoCommand(program2) {
|
|
2756
3520
|
program2.command("info").description("Display CLI and environment information").action(async () => {
|
|
2757
3521
|
const tokenManager = getTokenManager();
|
|
@@ -2804,18 +3568,40 @@ function registerInfoCommand(program2) {
|
|
|
2804
3568
|
}
|
|
2805
3569
|
});
|
|
2806
3570
|
}
|
|
3571
|
+
|
|
3572
|
+
// src/commands/config.ts
|
|
3573
|
+
init_esm_shims();
|
|
3574
|
+
function showConfig(isLoggedIn, email, githubToken) {
|
|
3575
|
+
const currentTheme = configManager.get("helpTheme");
|
|
3576
|
+
logger.log("Current configuration:");
|
|
3577
|
+
logger.newline();
|
|
3578
|
+
logger.log(` Docyrus Login: ${isLoggedIn ? `\u2713 ${email}` : "\u2717 Not logged in"}`);
|
|
3579
|
+
logger.log(` Docyrus Token: ${githubToken ? "\u2713 Configured" : "\u2717 Not configured"}`);
|
|
3580
|
+
logger.log(` Help Theme: ${currentTheme}`);
|
|
3581
|
+
logger.newline();
|
|
3582
|
+
}
|
|
2807
3583
|
function registerConfigCommand(program2) {
|
|
2808
|
-
program2.command("config").description("Manage CLI configuration").option("--token", "Update Docyrus token for private template access").option("--show", "Show current configuration").action(async (options) => {
|
|
3584
|
+
program2.command("config").description("Manage CLI configuration").option("--token", "Update Docyrus token for private template access").option("--theme", "Change help output color theme").option("--show", "Show current configuration").action(async (options) => {
|
|
2809
3585
|
const tokenManager = getTokenManager();
|
|
2810
3586
|
if (options.show) {
|
|
2811
3587
|
const githubToken2 = await tokenManager.getGithubToken();
|
|
2812
3588
|
const isLoggedIn2 = await tokenManager.isLoggedIn();
|
|
2813
3589
|
const email2 = await tokenManager.getUserEmail();
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
3590
|
+
showConfig(isLoggedIn2, email2, githubToken2);
|
|
3591
|
+
return;
|
|
3592
|
+
}
|
|
3593
|
+
if (options.theme) {
|
|
3594
|
+
const currentTheme = configManager.get("helpTheme");
|
|
3595
|
+
const theme = await select({
|
|
3596
|
+
message: "Select help color theme:",
|
|
3597
|
+
choices: HELP_THEMES.map((t) => ({
|
|
3598
|
+
name: `${t.padEnd(12)} ${previewTheme(t)}`,
|
|
3599
|
+
value: t
|
|
3600
|
+
})),
|
|
3601
|
+
default: currentTheme
|
|
3602
|
+
});
|
|
3603
|
+
configManager.set("helpTheme", theme);
|
|
3604
|
+
logger.success(`Help theme set to "${theme}"`);
|
|
2819
3605
|
return;
|
|
2820
3606
|
}
|
|
2821
3607
|
if (options.token) {
|
|
@@ -2838,12 +3624,786 @@ function registerConfigCommand(program2) {
|
|
|
2838
3624
|
const githubToken = await tokenManager.getGithubToken();
|
|
2839
3625
|
const isLoggedIn = await tokenManager.isLoggedIn();
|
|
2840
3626
|
const email = await tokenManager.getUserEmail();
|
|
2841
|
-
|
|
3627
|
+
showConfig(isLoggedIn, email, githubToken);
|
|
3628
|
+
logger.log("To update token: docyrus config --token");
|
|
3629
|
+
logger.log("To change theme: docyrus config --theme");
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
// src/commands/add.ts
|
|
3634
|
+
init_esm_shims();
|
|
3635
|
+
function findProjectRoot() {
|
|
3636
|
+
let dir = process.cwd();
|
|
3637
|
+
while (dir !== dirname(dir)) {
|
|
3638
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
3639
|
+
return dir;
|
|
3640
|
+
}
|
|
3641
|
+
dir = dirname(dir);
|
|
3642
|
+
}
|
|
3643
|
+
throw new CliError(
|
|
3644
|
+
"Could not find a package.json in the current directory or any parent directory.",
|
|
3645
|
+
1,
|
|
3646
|
+
"Run this command from within a JavaScript/TypeScript project."
|
|
3647
|
+
);
|
|
3648
|
+
}
|
|
3649
|
+
async function readComponentsJson(projectRoot) {
|
|
3650
|
+
const configPath = join(projectRoot, "components.json");
|
|
3651
|
+
if (!existsSync(configPath)) {
|
|
3652
|
+
throw new MissingConfigError();
|
|
3653
|
+
}
|
|
3654
|
+
const raw = await readFile(configPath, "utf-8");
|
|
3655
|
+
return JSON.parse(raw);
|
|
3656
|
+
}
|
|
3657
|
+
function detectPackageManager2(projectRoot) {
|
|
3658
|
+
if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
3659
|
+
if (existsSync(join(projectRoot, "yarn.lock"))) return "yarn";
|
|
3660
|
+
if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
|
|
3661
|
+
return "npm";
|
|
3662
|
+
}
|
|
3663
|
+
function normalizeRegistryName(input4) {
|
|
3664
|
+
if (input4.startsWith("@docyrus/")) return input4;
|
|
3665
|
+
if (input4.startsWith("hooks-")) return `@docyrus/${input4}`;
|
|
3666
|
+
if (input4.startsWith("utils-")) return `@docyrus/${input4}`;
|
|
3667
|
+
return `@docyrus/ui-${input4}`;
|
|
3668
|
+
}
|
|
3669
|
+
async function fetchRegistryItem(name, token) {
|
|
3670
|
+
const url = `${REGISTRY_BASE_URL}/${encodeURIComponent(name)}.json`;
|
|
3671
|
+
const headers = {
|
|
3672
|
+
Accept: "application/json"
|
|
3673
|
+
};
|
|
3674
|
+
if (token) {
|
|
3675
|
+
headers.Authorization = `Bearer ${token}`;
|
|
3676
|
+
}
|
|
3677
|
+
let response;
|
|
3678
|
+
try {
|
|
3679
|
+
response = await fetch(url, { headers });
|
|
3680
|
+
} catch {
|
|
3681
|
+
throw new NetworkError();
|
|
3682
|
+
}
|
|
3683
|
+
if (response.status === 401 || response.status === 403) {
|
|
3684
|
+
throw new PremiumAuthError(name);
|
|
3685
|
+
}
|
|
3686
|
+
if (response.status === 404) {
|
|
3687
|
+
throw new ComponentNotFoundError(name);
|
|
3688
|
+
}
|
|
3689
|
+
if (!response.ok) {
|
|
3690
|
+
throw new NetworkError(`Failed to fetch "${name}" from registry (HTTP ${response.status})`);
|
|
3691
|
+
}
|
|
3692
|
+
return response.json();
|
|
3693
|
+
}
|
|
3694
|
+
async function resolveAllDependencies(names, token) {
|
|
3695
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
3696
|
+
const queue = [...names];
|
|
3697
|
+
while (queue.length > 0) {
|
|
3698
|
+
const name = queue.shift();
|
|
3699
|
+
if (resolved.has(name)) continue;
|
|
3700
|
+
const item = await fetchRegistryItem(name, token);
|
|
3701
|
+
resolved.set(name, item);
|
|
3702
|
+
if (item.registryDependencies?.length > 0) {
|
|
3703
|
+
for (const dep of item.registryDependencies) {
|
|
3704
|
+
if (!resolved.has(dep)) {
|
|
3705
|
+
queue.push(dep);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
return resolved;
|
|
3711
|
+
}
|
|
3712
|
+
function resolveTargetDir(projectRoot) {
|
|
3713
|
+
if (existsSync(join(projectRoot, "src"))) {
|
|
3714
|
+
return join(projectRoot, "src");
|
|
3715
|
+
}
|
|
3716
|
+
return projectRoot;
|
|
3717
|
+
}
|
|
3718
|
+
async function writeItemFiles(item, baseDir, overwrite) {
|
|
3719
|
+
const written = [];
|
|
3720
|
+
for (const file of item.files) {
|
|
3721
|
+
const targetPath = join(baseDir, file.path);
|
|
3722
|
+
const targetDir = dirname(targetPath);
|
|
3723
|
+
if (!existsSync(targetDir)) {
|
|
3724
|
+
await mkdir(targetDir, { recursive: true });
|
|
3725
|
+
}
|
|
3726
|
+
if (existsSync(targetPath) && !overwrite) {
|
|
3727
|
+
const shouldOverwrite = await confirm({
|
|
3728
|
+
message: MESSAGES.ADD_FILE_EXISTS(file.path),
|
|
3729
|
+
default: false
|
|
3730
|
+
});
|
|
3731
|
+
if (!shouldOverwrite) {
|
|
3732
|
+
logger.dim(` ${MESSAGES.ADD_FILE_SKIPPED(file.path)}`);
|
|
3733
|
+
continue;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
await writeFile(targetPath, file.content, "utf-8");
|
|
3737
|
+
written.push(file.path);
|
|
3738
|
+
}
|
|
3739
|
+
return written;
|
|
3740
|
+
}
|
|
3741
|
+
async function getExistingDependencies(projectRoot) {
|
|
3742
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
3743
|
+
try {
|
|
3744
|
+
const raw = await readFile(pkgPath, "utf-8");
|
|
3745
|
+
const pkg = JSON.parse(raw);
|
|
3746
|
+
const deps = /* @__PURE__ */ new Set();
|
|
3747
|
+
for (const key of Object.keys(pkg.dependencies || {})) {
|
|
3748
|
+
deps.add(key);
|
|
3749
|
+
}
|
|
3750
|
+
for (const key of Object.keys(pkg.devDependencies || {})) {
|
|
3751
|
+
deps.add(key);
|
|
3752
|
+
}
|
|
3753
|
+
return deps;
|
|
3754
|
+
} catch {
|
|
3755
|
+
return /* @__PURE__ */ new Set();
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
function installNpmDependencies(deps, projectRoot) {
|
|
3759
|
+
if (deps.length === 0) return;
|
|
3760
|
+
const pm = detectPackageManager2(projectRoot);
|
|
3761
|
+
const pmArgs = {
|
|
3762
|
+
npm: ["install", ...deps],
|
|
3763
|
+
pnpm: ["add", ...deps],
|
|
3764
|
+
yarn: ["add", ...deps],
|
|
3765
|
+
bun: ["add", ...deps]
|
|
3766
|
+
};
|
|
3767
|
+
execFileSync(pm, pmArgs[pm], { cwd: projectRoot, stdio: "pipe" });
|
|
3768
|
+
}
|
|
3769
|
+
function handleDryRun(items, baseDir, allNewDeps) {
|
|
3770
|
+
logger.newline();
|
|
3771
|
+
logger.bold(MESSAGES.ADD_DRY_RUN_HEADER);
|
|
3772
|
+
logger.newline();
|
|
3773
|
+
for (const [name, item] of items) {
|
|
3774
|
+
logger.info(`${item.title || name}`);
|
|
3775
|
+
for (const file of item.files) {
|
|
3776
|
+
const targetPath = join(baseDir, file.path);
|
|
3777
|
+
const exists = existsSync(targetPath);
|
|
3778
|
+
logger.dim(` ${exists ? "(overwrite)" : "(create)"} ${file.path}`);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
if (allNewDeps.length > 0) {
|
|
2842
3782
|
logger.newline();
|
|
2843
|
-
logger.
|
|
2844
|
-
logger.
|
|
3783
|
+
logger.info("Dependencies to install:");
|
|
3784
|
+
logger.dim(` ${allNewDeps.join(", ")}`);
|
|
3785
|
+
}
|
|
3786
|
+
logger.newline();
|
|
3787
|
+
}
|
|
3788
|
+
function registerAddCommand(program2) {
|
|
3789
|
+
program2.command("add [items...]").description("Add Docyrus UI components, hooks, or utilities to your project").option("-o, --overwrite", "Overwrite existing files without prompting").option("-d, --dry-run", "Show what would be installed without making changes").option("-p, --path <path>", "Custom target path for files").addHelpText("after", `
|
|
3790
|
+
Examples:
|
|
3791
|
+
$ docyrus add button Add a component
|
|
3792
|
+
$ docyrus add button dialog Add multiple components
|
|
3793
|
+
$ docyrus add hooks-debounce Add a hook
|
|
3794
|
+
$ docyrus add utils-cn Add a utility
|
|
3795
|
+
$ docyrus add button --dry-run Preview installation
|
|
3796
|
+
$ docyrus add button -o Overwrite existing files
|
|
3797
|
+
`).action(async (items, options) => {
|
|
3798
|
+
if (!items || items.length === 0) {
|
|
3799
|
+
throw new CliError(
|
|
3800
|
+
MESSAGES.ADD_NO_ITEMS,
|
|
3801
|
+
1,
|
|
3802
|
+
"Usage: docyrus add <item...>\nExample: docyrus add button dialog hooks-debounce"
|
|
3803
|
+
);
|
|
3804
|
+
}
|
|
3805
|
+
const projectRoot = findProjectRoot();
|
|
3806
|
+
const config = await readComponentsJson(projectRoot);
|
|
3807
|
+
const baseDir = options.path ? join(projectRoot, options.path) : resolveTargetDir(projectRoot);
|
|
2845
3808
|
logger.newline();
|
|
2846
|
-
logger.
|
|
3809
|
+
logger.dim(MESSAGES.ADD_DETECTED_STYLE(config.style));
|
|
3810
|
+
if (config.style.startsWith("base")) {
|
|
3811
|
+
logger.warn(MESSAGES.ADD_DEPRECATED_BASE_STYLE);
|
|
3812
|
+
}
|
|
3813
|
+
let token;
|
|
3814
|
+
try {
|
|
3815
|
+
token = await requireAuth();
|
|
3816
|
+
} catch {
|
|
3817
|
+
}
|
|
3818
|
+
const registryNames = items.map((item) => normalizeRegistryName(item));
|
|
3819
|
+
const allItems = await withSpinner(
|
|
3820
|
+
MESSAGES.ADD_RESOLVING,
|
|
3821
|
+
() => resolveAllDependencies(registryNames, token),
|
|
3822
|
+
{ successText: `Resolved ${registryNames.length} item(s)` }
|
|
3823
|
+
);
|
|
3824
|
+
const existingDeps = await getExistingDependencies(projectRoot);
|
|
3825
|
+
const allNewDeps = [];
|
|
3826
|
+
for (const item of allItems.values()) {
|
|
3827
|
+
for (const dep of item.dependencies) {
|
|
3828
|
+
if (!existingDeps.has(dep) && !allNewDeps.includes(dep)) {
|
|
3829
|
+
allNewDeps.push(dep);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
if (options.dryRun) {
|
|
3834
|
+
handleDryRun(allItems, baseDir, allNewDeps);
|
|
3835
|
+
return;
|
|
3836
|
+
}
|
|
3837
|
+
let totalFiles = 0;
|
|
3838
|
+
for (const [name, item] of allItems) {
|
|
3839
|
+
const written = await withSpinner(
|
|
3840
|
+
MESSAGES.ADD_INSTALLING_ITEM(item.title || name),
|
|
3841
|
+
() => writeItemFiles(item, baseDir, !!options.overwrite),
|
|
3842
|
+
{ successText: `${item.title || name}` }
|
|
3843
|
+
);
|
|
3844
|
+
totalFiles += written.length;
|
|
3845
|
+
}
|
|
3846
|
+
if (allNewDeps.length > 0) {
|
|
3847
|
+
await withSpinner(
|
|
3848
|
+
MESSAGES.ADD_INSTALLING_DEPS(allNewDeps.length),
|
|
3849
|
+
async () => installNpmDependencies(allNewDeps, projectRoot),
|
|
3850
|
+
{ successText: `${allNewDeps.length} dependenc${allNewDeps.length === 1 ? "y" : "ies"} installed` }
|
|
3851
|
+
);
|
|
3852
|
+
}
|
|
3853
|
+
logger.newline();
|
|
3854
|
+
logger.success(MESSAGES.ADD_SUCCESS(allItems.size, totalFiles));
|
|
3855
|
+
if (allNewDeps.length > 0) {
|
|
3856
|
+
logger.dim(` Dependencies: ${allNewDeps.join(", ")}`);
|
|
3857
|
+
}
|
|
3858
|
+
logger.newline();
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
// src/commands/commitlint.ts
|
|
3863
|
+
init_esm_shims();
|
|
3864
|
+
|
|
3865
|
+
// src/utils/commitlint-setup.ts
|
|
3866
|
+
init_esm_shims();
|
|
3867
|
+
function findProjectRoot2() {
|
|
3868
|
+
let dir = process.cwd();
|
|
3869
|
+
while (dir !== dirname(dir)) {
|
|
3870
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
3871
|
+
return dir;
|
|
3872
|
+
}
|
|
3873
|
+
dir = dirname(dir);
|
|
3874
|
+
}
|
|
3875
|
+
throw new CliError(
|
|
3876
|
+
"Could not find a package.json.",
|
|
3877
|
+
1,
|
|
3878
|
+
"Run this command from within a JavaScript/TypeScript project."
|
|
3879
|
+
);
|
|
3880
|
+
}
|
|
3881
|
+
function detectPackageManager3(projectRoot) {
|
|
3882
|
+
if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
3883
|
+
if (existsSync(join(projectRoot, "yarn.lock"))) return "yarn";
|
|
3884
|
+
if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
|
|
3885
|
+
return "npm";
|
|
3886
|
+
}
|
|
3887
|
+
async function detectWorkspaceScopes(projectRoot) {
|
|
3888
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
3889
|
+
const pnpmPath = join(projectRoot, "pnpm-workspace.yaml");
|
|
3890
|
+
if (existsSync(pnpmPath)) {
|
|
3891
|
+
const content = await readFile(pnpmPath, "utf-8");
|
|
3892
|
+
const patterns = content.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.trim().replace(/^-\s*['"]?/, "").replace(/['"]?\s*$/, ""));
|
|
3893
|
+
for (const pattern of patterns) {
|
|
3894
|
+
const baseDir = pattern.replace(/\/?\*+$/, "");
|
|
3895
|
+
const fullDir = join(projectRoot, baseDir);
|
|
3896
|
+
if (existsSync(fullDir)) {
|
|
3897
|
+
const entries = readdirSync(fullDir, { withFileTypes: true });
|
|
3898
|
+
for (const entry of entries) {
|
|
3899
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
3900
|
+
scopes.add(entry.name);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
try {
|
|
3907
|
+
const pkg = JSON.parse(await readFile(join(projectRoot, "package.json"), "utf-8"));
|
|
3908
|
+
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages ?? [];
|
|
3909
|
+
for (const pattern of workspaces) {
|
|
3910
|
+
const baseDir = pattern.replace(/\/?\*+$/, "");
|
|
3911
|
+
const fullDir = join(projectRoot, baseDir);
|
|
3912
|
+
if (existsSync(fullDir)) {
|
|
3913
|
+
const entries = readdirSync(fullDir, { withFileTypes: true });
|
|
3914
|
+
for (const entry of entries) {
|
|
3915
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
3916
|
+
scopes.add(entry.name);
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
} catch {
|
|
3922
|
+
}
|
|
3923
|
+
scopes.add("root");
|
|
3924
|
+
scopes.add("deps");
|
|
3925
|
+
scopes.add("release");
|
|
3926
|
+
return [...scopes].sort();
|
|
3927
|
+
}
|
|
3928
|
+
function generateCommitlintConfig(opts) {
|
|
3929
|
+
const { scopes, scopeStrategy } = opts;
|
|
3930
|
+
let scopeRules = "";
|
|
3931
|
+
if (scopeStrategy === "required") {
|
|
3932
|
+
const scopeList = scopes.map((s) => `'${s}'`).join(", ");
|
|
3933
|
+
scopeRules = `
|
|
3934
|
+
// Scope required and must be from the allowed list
|
|
3935
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
3936
|
+
'scope-empty': [2, 'never'],
|
|
3937
|
+
'scope-enum': [2, 'always', [${scopeList}]],`;
|
|
3938
|
+
} else if (scopeStrategy === "optional") {
|
|
3939
|
+
const scopeList = scopes.map((s) => `'${s}'`).join(", ");
|
|
3940
|
+
scopeRules = `
|
|
3941
|
+
// Scope optional but if present must be from the allowed list
|
|
3942
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
3943
|
+
'scope-empty': [0],
|
|
3944
|
+
'scope-enum': [2, 'always', [${scopeList}]],`;
|
|
3945
|
+
}
|
|
3946
|
+
return `import { type UserConfig } from '@commitlint/types';
|
|
3947
|
+
|
|
3948
|
+
const config: UserConfig = {
|
|
3949
|
+
extends: ['@commitlint/config-conventional'],
|
|
3950
|
+
rules: {
|
|
3951
|
+
// Type required
|
|
3952
|
+
'type-empty': [2, 'never'],
|
|
3953
|
+
|
|
3954
|
+
// Subject required with length constraints
|
|
3955
|
+
'subject-empty': [2, 'never'],
|
|
3956
|
+
'subject-min-length': [2, 'always', 5],
|
|
3957
|
+
'subject-max-length': [2, 'always', 250],
|
|
3958
|
+
|
|
3959
|
+
// Header max length
|
|
3960
|
+
'header-max-length': [2, 'always', 100],
|
|
3961
|
+
${scopeRules}
|
|
3962
|
+
// Body (disabled \u2014 no line length limit)
|
|
3963
|
+
'body-max-line-length': [0, 'always', 200],
|
|
3964
|
+
|
|
3965
|
+
// BREAKING CHANGE (disabled by default)
|
|
3966
|
+
'trailer-exists': [0, 'always', 'BREAKING CHANGE']
|
|
3967
|
+
},
|
|
3968
|
+
|
|
3969
|
+
parserPreset: {
|
|
3970
|
+
parserOpts: {
|
|
3971
|
+
breakingHeaderPattern: /^(\\w*)(?:\\((.*)\\))?!: (.*)$/,
|
|
3972
|
+
noteKeywords: ['BREAKING CHANGE']
|
|
3973
|
+
}
|
|
3974
|
+
},
|
|
3975
|
+
|
|
3976
|
+
helpUrl:
|
|
3977
|
+
'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
|
|
3978
|
+
};
|
|
3979
|
+
|
|
3980
|
+
export default config;
|
|
3981
|
+
`;
|
|
3982
|
+
}
|
|
3983
|
+
function generateCommitMsgHook(packageManager) {
|
|
3984
|
+
const exec4 = packageManager === "pnpm" ? "pnpm exec" : packageManager === "yarn" ? "yarn" : packageManager === "bun" ? "bunx" : "npx --no-install";
|
|
3985
|
+
return `${exec4} commitlint --edit $1
|
|
3986
|
+
`;
|
|
3987
|
+
}
|
|
3988
|
+
function generatePreCommitHook() {
|
|
3989
|
+
return `# Skip if no code files are staged
|
|
3990
|
+
if git diff --cached --name-only | grep -qE '\\.(ts|tsx|js|jsx)$'; then
|
|
3991
|
+
npx --no-install lint-staged
|
|
3992
|
+
fi
|
|
3993
|
+
`;
|
|
3994
|
+
}
|
|
3995
|
+
function generateCommitLinterScript() {
|
|
3996
|
+
return `#!/usr/bin/env bash
|
|
3997
|
+
# Monorepo-aware tsc-files: finds the nearest tsconfig.json for each file,
|
|
3998
|
+
# groups files by their tsconfig directory, and runs tsc per group.
|
|
3999
|
+
|
|
4000
|
+
if [ $# -eq 0 ]; then
|
|
4001
|
+
exit 0
|
|
4002
|
+
fi
|
|
4003
|
+
|
|
4004
|
+
node -e "
|
|
4005
|
+
const fs = require('fs');
|
|
4006
|
+
const path = require('path');
|
|
4007
|
+
const { execFileSync } = require('child_process');
|
|
4008
|
+
|
|
4009
|
+
const files = process.argv.slice(1).map(f => path.resolve(f));
|
|
4010
|
+
const groups = {};
|
|
4011
|
+
|
|
4012
|
+
for (const file of files) {
|
|
4013
|
+
let dir = path.dirname(file);
|
|
4014
|
+
let tsconfigDir = null;
|
|
4015
|
+
|
|
4016
|
+
while (dir !== path.dirname(dir)) {
|
|
4017
|
+
if (fs.existsSync(path.join(dir, 'tsconfig.json'))) {
|
|
4018
|
+
tsconfigDir = dir;
|
|
4019
|
+
break;
|
|
4020
|
+
}
|
|
4021
|
+
dir = path.dirname(dir);
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
if (!tsconfigDir) continue;
|
|
4025
|
+
if (!groups[tsconfigDir]) groups[tsconfigDir] = [];
|
|
4026
|
+
groups[tsconfigDir].push(file);
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
let failed = false;
|
|
4030
|
+
|
|
4031
|
+
for (const [dir, groupFiles] of Object.entries(groups)) {
|
|
4032
|
+
const configPath = path.join(dir, 'tsconfig.json');
|
|
4033
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
4034
|
+
const tmpName = 'tsconfig.staged-' + process.pid + '.json';
|
|
4035
|
+
const tmpPath = path.join(dir, tmpName);
|
|
4036
|
+
|
|
4037
|
+
const excludePatterns = (config.exclude || []).map(p => p.replace(/\\\\/\\\\*\\\\*.*$/, '').replace(/\\\\/$/, ''));
|
|
4038
|
+
const filteredFiles = groupFiles.filter(f => {
|
|
4039
|
+
const rel = path.relative(dir, f).replace(/\\\\\\\\\\\\\\\\/g, '/');
|
|
4040
|
+
return !excludePatterns.some(p => rel === p || rel.startsWith(p + '/'));
|
|
4041
|
+
});
|
|
4042
|
+
if (filteredFiles.length === 0) continue;
|
|
4043
|
+
|
|
4044
|
+
const tmpConfig = {
|
|
4045
|
+
...config,
|
|
4046
|
+
compilerOptions: { ...config.compilerOptions, skipLibCheck: true, rootDir: undefined },
|
|
4047
|
+
files: filteredFiles,
|
|
4048
|
+
include: []
|
|
4049
|
+
};
|
|
4050
|
+
delete tmpConfig.compilerOptions.rootDir;
|
|
4051
|
+
|
|
4052
|
+
fs.writeFileSync(tmpPath, JSON.stringify(tmpConfig, null, 2));
|
|
4053
|
+
|
|
4054
|
+
try {
|
|
4055
|
+
execFileSync('npx', ['--no-install', 'tsc', '-p', tmpName, '--noEmit'], { cwd: dir, stdio: 'inherit' });
|
|
4056
|
+
} catch {
|
|
4057
|
+
failed = true;
|
|
4058
|
+
} finally {
|
|
4059
|
+
fs.unlinkSync(tmpPath);
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
if (failed) process.exit(1);
|
|
4064
|
+
" "$@"
|
|
4065
|
+
|
|
4066
|
+
exit $?
|
|
4067
|
+
`;
|
|
4068
|
+
}
|
|
4069
|
+
async function updatePackageJson(projectRoot) {
|
|
4070
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
4071
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
4072
|
+
pkg.devDependencies = {
|
|
4073
|
+
...pkg.devDependencies,
|
|
4074
|
+
"@commitlint/cli": "latest",
|
|
4075
|
+
"@commitlint/config-conventional": "latest",
|
|
4076
|
+
"@commitlint/types": "latest",
|
|
4077
|
+
husky: "latest",
|
|
4078
|
+
"lint-staged": "latest"
|
|
4079
|
+
};
|
|
4080
|
+
pkg.scripts = pkg.scripts ?? {};
|
|
4081
|
+
if (pkg.scripts.prepare && !pkg.scripts.prepare.includes("husky")) {
|
|
4082
|
+
pkg.scripts.prepare = `${pkg.scripts.prepare} && husky`;
|
|
4083
|
+
} else if (!pkg.scripts.prepare) {
|
|
4084
|
+
pkg.scripts.prepare = "husky";
|
|
4085
|
+
}
|
|
4086
|
+
pkg["lint-staged"] = {
|
|
4087
|
+
"*.{ts,tsx}": ["bash scripts/commit-linter.sh", "eslint --fix"]
|
|
4088
|
+
};
|
|
4089
|
+
await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
4090
|
+
`, "utf-8");
|
|
4091
|
+
}
|
|
4092
|
+
async function writeCommitlintFiles(opts) {
|
|
4093
|
+
const {
|
|
4094
|
+
projectRoot,
|
|
4095
|
+
scopes,
|
|
4096
|
+
scopeStrategy,
|
|
4097
|
+
packageManager
|
|
4098
|
+
} = opts;
|
|
4099
|
+
await writeFile(
|
|
4100
|
+
join(projectRoot, "commitlint.config.ts"),
|
|
4101
|
+
generateCommitlintConfig({ scopes, scopeStrategy }),
|
|
4102
|
+
"utf-8"
|
|
4103
|
+
);
|
|
4104
|
+
const huskyDir = join(projectRoot, ".husky");
|
|
4105
|
+
if (!existsSync(huskyDir)) {
|
|
4106
|
+
await mkdir(huskyDir, { recursive: true });
|
|
4107
|
+
}
|
|
4108
|
+
await writeFile(join(huskyDir, "commit-msg"), generateCommitMsgHook(packageManager), "utf-8");
|
|
4109
|
+
await writeFile(join(huskyDir, "pre-commit"), generatePreCommitHook(), "utf-8");
|
|
4110
|
+
const scriptsDir = join(projectRoot, "scripts");
|
|
4111
|
+
if (!existsSync(scriptsDir)) {
|
|
4112
|
+
await mkdir(scriptsDir, { recursive: true });
|
|
4113
|
+
}
|
|
4114
|
+
const scriptPath = join(scriptsDir, "commit-linter.sh");
|
|
4115
|
+
await writeFile(scriptPath, generateCommitLinterScript(), "utf-8");
|
|
4116
|
+
await chmod(scriptPath, "755");
|
|
4117
|
+
}
|
|
4118
|
+
function installDeps(projectRoot, packageManager) {
|
|
4119
|
+
execFileSync(packageManager, ["install"], {
|
|
4120
|
+
cwd: projectRoot,
|
|
4121
|
+
stdio: "pipe"
|
|
4122
|
+
});
|
|
4123
|
+
}
|
|
4124
|
+
function initHusky(projectRoot, packageManager) {
|
|
4125
|
+
const cmds = {
|
|
4126
|
+
pnpm: ["pnpm", ["exec", "husky"]],
|
|
4127
|
+
npm: ["npx", ["husky"]],
|
|
4128
|
+
yarn: ["yarn", ["husky"]],
|
|
4129
|
+
bun: ["bunx", ["husky"]]
|
|
4130
|
+
};
|
|
4131
|
+
const [cmd, args] = cmds[packageManager];
|
|
4132
|
+
execFileSync(cmd, args, {
|
|
4133
|
+
cwd: projectRoot,
|
|
4134
|
+
stdio: "pipe"
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
// src/commands/commitlint.ts
|
|
4139
|
+
function registerCommitlintCommand(program2) {
|
|
4140
|
+
program2.command("commitlint").description(MESSAGES.COMMITLINT_DESCRIPTION).option("--scopes <scopes>", "Comma-separated list of scopes").option("--monorepo", "Treat as monorepo").option("--no-monorepo", "Treat as single project").option("--scope-strategy <strategy>", "Scope strategy: required, optional, none").option("-y, --yes", "Accept defaults and overwrite existing files").action(async (options) => {
|
|
4141
|
+
const projectRoot = findProjectRoot2();
|
|
4142
|
+
const packageManager = detectPackageManager3(projectRoot);
|
|
4143
|
+
logger.newline();
|
|
4144
|
+
logger.dim(MESSAGES.COMMITLINT_DETECTED_PM(packageManager));
|
|
4145
|
+
const configExists = existsSync(join(projectRoot, "commitlint.config.ts"));
|
|
4146
|
+
const huskyExists = existsSync(join(projectRoot, ".husky"));
|
|
4147
|
+
if (configExists && !options.yes) {
|
|
4148
|
+
const overwrite = await confirm({
|
|
4149
|
+
message: MESSAGES.COMMITLINT_CONFIG_EXISTS,
|
|
4150
|
+
default: false
|
|
4151
|
+
});
|
|
4152
|
+
if (!overwrite) {
|
|
4153
|
+
logger.dim("Aborted.");
|
|
4154
|
+
return;
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
let skipHusky = false;
|
|
4158
|
+
if (huskyExists && !options.yes) {
|
|
4159
|
+
const overwrite = await confirm({
|
|
4160
|
+
message: MESSAGES.COMMITLINT_HUSKY_EXISTS,
|
|
4161
|
+
default: true
|
|
4162
|
+
});
|
|
4163
|
+
skipHusky = !overwrite;
|
|
4164
|
+
}
|
|
4165
|
+
const isMonorepo = options.monorepo !== void 0 ? options.monorepo : await confirm({
|
|
4166
|
+
message: MESSAGES.COMMITLINT_IS_MONOREPO,
|
|
4167
|
+
default: false
|
|
4168
|
+
});
|
|
4169
|
+
let scopeStrategy;
|
|
4170
|
+
if (options.scopeStrategy) {
|
|
4171
|
+
scopeStrategy = options.scopeStrategy;
|
|
4172
|
+
} else {
|
|
4173
|
+
scopeStrategy = await select({
|
|
4174
|
+
message: MESSAGES.COMMITLINT_SCOPE_STRATEGY,
|
|
4175
|
+
choices: [
|
|
4176
|
+
{
|
|
4177
|
+
name: isMonorepo ? "Required (recommended)" : "Required",
|
|
4178
|
+
value: "required",
|
|
4179
|
+
description: "fix(auth): ... \u2014 scope is mandatory"
|
|
4180
|
+
},
|
|
4181
|
+
{
|
|
4182
|
+
name: isMonorepo ? "Optional" : "Optional (recommended)",
|
|
4183
|
+
value: "optional",
|
|
4184
|
+
description: "fix: ... or fix(auth): ... \u2014 both valid"
|
|
4185
|
+
},
|
|
4186
|
+
{
|
|
4187
|
+
name: "None",
|
|
4188
|
+
value: "none",
|
|
4189
|
+
description: "fix: ... \u2014 scopes are not used"
|
|
4190
|
+
}
|
|
4191
|
+
],
|
|
4192
|
+
default: isMonorepo ? "required" : "optional"
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
let scopes = [];
|
|
4196
|
+
if (scopeStrategy !== "none") {
|
|
4197
|
+
if (options.scopes) {
|
|
4198
|
+
scopes = options.scopes.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
4199
|
+
} else {
|
|
4200
|
+
if (isMonorepo) {
|
|
4201
|
+
const detected = await detectWorkspaceScopes(projectRoot);
|
|
4202
|
+
if (detected.length > 0) {
|
|
4203
|
+
logger.dim(MESSAGES.COMMITLINT_DETECTED_WORKSPACES(detected.length));
|
|
4204
|
+
scopes = await checkbox({
|
|
4205
|
+
message: MESSAGES.COMMITLINT_SELECT_SCOPES,
|
|
4206
|
+
choices: detected.map((s) => ({
|
|
4207
|
+
name: s,
|
|
4208
|
+
value: s,
|
|
4209
|
+
checked: true
|
|
4210
|
+
}))
|
|
4211
|
+
});
|
|
4212
|
+
} else {
|
|
4213
|
+
logger.warn(MESSAGES.COMMITLINT_NO_WORKSPACES);
|
|
4214
|
+
scopes = [
|
|
4215
|
+
"root",
|
|
4216
|
+
"deps",
|
|
4217
|
+
"release"
|
|
4218
|
+
];
|
|
4219
|
+
}
|
|
4220
|
+
} else {
|
|
4221
|
+
scopes = [
|
|
4222
|
+
"root",
|
|
4223
|
+
"deps",
|
|
4224
|
+
"release"
|
|
4225
|
+
];
|
|
4226
|
+
}
|
|
4227
|
+
const additional = await input({
|
|
4228
|
+
message: MESSAGES.COMMITLINT_ADDITIONAL_SCOPES
|
|
4229
|
+
});
|
|
4230
|
+
if (additional.trim()) {
|
|
4231
|
+
const extra = additional.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
4232
|
+
for (const s of extra) {
|
|
4233
|
+
if (!scopes.includes(s)) {
|
|
4234
|
+
scopes.push(s);
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
scopes.sort();
|
|
4240
|
+
logger.newline();
|
|
4241
|
+
logger.info(MESSAGES.COMMITLINT_CONFIRM_SCOPES(scopes));
|
|
4242
|
+
}
|
|
4243
|
+
logger.newline();
|
|
4244
|
+
const progress = createMultiStepProgress("Setting up Commitlint", [
|
|
4245
|
+
MESSAGES.COMMITLINT_STEP_CONFIG,
|
|
4246
|
+
MESSAGES.COMMITLINT_STEP_HUSKY,
|
|
4247
|
+
MESSAGES.COMMITLINT_STEP_SCRIPT,
|
|
4248
|
+
MESSAGES.COMMITLINT_STEP_PACKAGE_JSON,
|
|
4249
|
+
MESSAGES.COMMITLINT_STEP_INSTALL,
|
|
4250
|
+
MESSAGES.COMMITLINT_STEP_INIT_HUSKY
|
|
4251
|
+
]);
|
|
4252
|
+
progress.start(0);
|
|
4253
|
+
await writeCommitlintFiles({
|
|
4254
|
+
projectRoot,
|
|
4255
|
+
scopes,
|
|
4256
|
+
scopeStrategy,
|
|
4257
|
+
packageManager
|
|
4258
|
+
});
|
|
4259
|
+
progress.complete(0);
|
|
4260
|
+
if (skipHusky) {
|
|
4261
|
+
progress.complete(1);
|
|
4262
|
+
} else {
|
|
4263
|
+
progress.start(1);
|
|
4264
|
+
progress.complete(1);
|
|
4265
|
+
}
|
|
4266
|
+
progress.start(2);
|
|
4267
|
+
progress.complete(2);
|
|
4268
|
+
progress.start(3);
|
|
4269
|
+
await updatePackageJson(projectRoot);
|
|
4270
|
+
progress.complete(3);
|
|
4271
|
+
progress.start(4);
|
|
4272
|
+
try {
|
|
4273
|
+
installDeps(projectRoot, packageManager);
|
|
4274
|
+
progress.complete(4);
|
|
4275
|
+
} catch (error) {
|
|
4276
|
+
progress.fail(4, error instanceof Error ? error.message : "Failed to install dependencies");
|
|
4277
|
+
progress.finish();
|
|
4278
|
+
throw error;
|
|
4279
|
+
}
|
|
4280
|
+
progress.start(5);
|
|
4281
|
+
try {
|
|
4282
|
+
initHusky(projectRoot, packageManager);
|
|
4283
|
+
progress.complete(5);
|
|
4284
|
+
} catch (error) {
|
|
4285
|
+
progress.fail(5, error instanceof Error ? error.message : "Failed to initialize husky");
|
|
4286
|
+
progress.finish();
|
|
4287
|
+
throw error;
|
|
4288
|
+
}
|
|
4289
|
+
progress.finish();
|
|
4290
|
+
logger.newline();
|
|
4291
|
+
logger.success(MESSAGES.COMMITLINT_SUCCESS);
|
|
4292
|
+
logger.dim(MESSAGES.COMMITLINT_SUCCESS_HINT);
|
|
4293
|
+
logger.newline();
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
// src/commands/list.ts
|
|
4298
|
+
init_esm_shims();
|
|
4299
|
+
async function fetchRegistryIndex() {
|
|
4300
|
+
const url = `${REGISTRY_BASE_URL}/__index.json`;
|
|
4301
|
+
let response;
|
|
4302
|
+
try {
|
|
4303
|
+
response = await fetch(url, { headers: { Accept: "application/json" } });
|
|
4304
|
+
} catch {
|
|
4305
|
+
throw new NetworkError();
|
|
4306
|
+
}
|
|
4307
|
+
if (!response.ok) {
|
|
4308
|
+
throw new NetworkError(`Failed to fetch registry index (HTTP ${response.status})`);
|
|
4309
|
+
}
|
|
4310
|
+
return response.json();
|
|
4311
|
+
}
|
|
4312
|
+
async function fetchNpmPackages() {
|
|
4313
|
+
const url = "https://registry.npmjs.org/-/v1/search?text=%40docyrus&size=100";
|
|
4314
|
+
let response;
|
|
4315
|
+
try {
|
|
4316
|
+
response = await fetch(url, { headers: { Accept: "application/json" } });
|
|
4317
|
+
} catch {
|
|
4318
|
+
throw new NetworkError();
|
|
4319
|
+
}
|
|
4320
|
+
if (!response.ok) {
|
|
4321
|
+
throw new NetworkError(`Failed to fetch npm packages (HTTP ${response.status})`);
|
|
4322
|
+
}
|
|
4323
|
+
const data = await response.json();
|
|
4324
|
+
return data.objects.map((o) => ({
|
|
4325
|
+
name: o.package.name,
|
|
4326
|
+
version: o.package.version,
|
|
4327
|
+
description: o.package.description,
|
|
4328
|
+
date: o.package.date
|
|
4329
|
+
}));
|
|
4330
|
+
}
|
|
4331
|
+
function groupByType(items) {
|
|
4332
|
+
const groups = {};
|
|
4333
|
+
for (const item of items) {
|
|
4334
|
+
const category = item.type.replace("registry:", "");
|
|
4335
|
+
const group = groups[category] ??= [];
|
|
4336
|
+
group.push(item);
|
|
4337
|
+
}
|
|
4338
|
+
return groups;
|
|
4339
|
+
}
|
|
4340
|
+
function printRegistryList(items) {
|
|
4341
|
+
const groups = groupByType(items);
|
|
4342
|
+
const categoryLabels = {
|
|
4343
|
+
ui: "Components",
|
|
4344
|
+
hook: "Hooks",
|
|
4345
|
+
lib: "Utilities"
|
|
4346
|
+
};
|
|
4347
|
+
logger.newline();
|
|
4348
|
+
logger.bold(MESSAGES.LIST_HEADER);
|
|
4349
|
+
logger.newline();
|
|
4350
|
+
for (const [type, group] of Object.entries(groups)) {
|
|
4351
|
+
const label = categoryLabels[type] || type;
|
|
4352
|
+
logger.info(chalk4.bold(label));
|
|
4353
|
+
for (const item of group) {
|
|
4354
|
+
const name = chalk4.cyan(item.name);
|
|
4355
|
+
const desc = item.description ? chalk4.dim(` \u2014 ${item.description}`) : "";
|
|
4356
|
+
logger.log(` ${name}${desc}`);
|
|
4357
|
+
}
|
|
4358
|
+
logger.newline();
|
|
4359
|
+
}
|
|
4360
|
+
logger.dim(MESSAGES.LIST_INSTALL_HINT);
|
|
4361
|
+
logger.newline();
|
|
4362
|
+
}
|
|
4363
|
+
function printNpmPackages(packages) {
|
|
4364
|
+
logger.newline();
|
|
4365
|
+
logger.bold(MESSAGES.LIST_NPM_HEADER);
|
|
4366
|
+
logger.newline();
|
|
4367
|
+
for (const pkg of packages) {
|
|
4368
|
+
const name = chalk4.cyan(pkg.name);
|
|
4369
|
+
const version = chalk4.dim(`@${pkg.version}`);
|
|
4370
|
+
const desc = pkg.description ? chalk4.dim(` \u2014 ${pkg.description}`) : "";
|
|
4371
|
+
logger.log(` ${name}${version}${desc}`);
|
|
4372
|
+
}
|
|
4373
|
+
logger.dim(`
|
|
4374
|
+
${MESSAGES.LIST_NPM_LINK}`);
|
|
4375
|
+
logger.newline();
|
|
4376
|
+
}
|
|
4377
|
+
function registerListCommand(program2) {
|
|
4378
|
+
program2.command("list").alias("ls").description("List available Docyrus UI components, hooks, and utilities").option("--packages", "List published @docyrus npm packages instead").addHelpText("after", `
|
|
4379
|
+
Examples:
|
|
4380
|
+
$ docyrus list List registry components
|
|
4381
|
+
$ docyrus ls Shorthand alias
|
|
4382
|
+
$ docyrus list --packages List published npm packages
|
|
4383
|
+
$ docyrus list --json Output as JSON
|
|
4384
|
+
`).action(async (options) => {
|
|
4385
|
+
if (options.packages) {
|
|
4386
|
+
const packages = await withSpinner(
|
|
4387
|
+
MESSAGES.LIST_FETCHING_NPM,
|
|
4388
|
+
fetchNpmPackages,
|
|
4389
|
+
{ successText: MESSAGES.LIST_NPM_SUCCESS }
|
|
4390
|
+
);
|
|
4391
|
+
if (output.isJson()) {
|
|
4392
|
+
output.success("ok", { packages });
|
|
4393
|
+
} else {
|
|
4394
|
+
printNpmPackages(packages);
|
|
4395
|
+
}
|
|
4396
|
+
} else {
|
|
4397
|
+
const items = await withSpinner(
|
|
4398
|
+
MESSAGES.LIST_FETCHING,
|
|
4399
|
+
fetchRegistryIndex
|
|
4400
|
+
);
|
|
4401
|
+
if (output.isJson()) {
|
|
4402
|
+
output.success("ok", { items });
|
|
4403
|
+
} else {
|
|
4404
|
+
printRegistryList(items);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
2847
4407
|
});
|
|
2848
4408
|
}
|
|
2849
4409
|
|
|
@@ -2853,12 +4413,18 @@ function registerCommands(program2) {
|
|
|
2853
4413
|
registerLogoutCommand(program2);
|
|
2854
4414
|
registerWhoamiCommand(program2);
|
|
2855
4415
|
registerCreateCommand(program2);
|
|
4416
|
+
registerAddCommand(program2);
|
|
4417
|
+
registerListCommand(program2);
|
|
4418
|
+
registerCommitlintCommand(program2);
|
|
2856
4419
|
registerGenerateCommand(program2);
|
|
2857
4420
|
registerUpgradeCommand(program2);
|
|
2858
4421
|
registerCompletionCommand(program2);
|
|
2859
4422
|
registerInfoCommand(program2);
|
|
2860
4423
|
registerConfigCommand(program2);
|
|
2861
4424
|
}
|
|
4425
|
+
|
|
4426
|
+
// src/utils/update-checker.ts
|
|
4427
|
+
init_esm_shims();
|
|
2862
4428
|
function isNewerVersion2(current, latest) {
|
|
2863
4429
|
const currentParts = current.replace(/^v/, "").split(".").map(Number);
|
|
2864
4430
|
const latestParts = latest.replace(/^v/, "").split(".").map(Number);
|
|
@@ -2896,7 +4462,7 @@ async function checkForUpdates(packageName, currentVersion) {
|
|
|
2896
4462
|
|
|
2897
4463
|
// src/cli.ts
|
|
2898
4464
|
var program = new Command();
|
|
2899
|
-
program.name(CLI_NAME).description("Docyrus CLI - Authentication and project management tools").version(CLI_VERSION, "-v, --version", "Display version number").option("--json", "Output results in JSON format").hook("preAction", (thisCommand) => {
|
|
4465
|
+
program.name(CLI_NAME).description("Docyrus CLI - Authentication and project management tools").version(CLI_VERSION, "-v, --version", "Display version number").option("--json", "Output results in JSON format").configureHelp(getHelpConfig(configManager.get("helpTheme"))).hook("preAction", (thisCommand) => {
|
|
2900
4466
|
const opts = thisCommand.opts();
|
|
2901
4467
|
if (opts.json) {
|
|
2902
4468
|
output.setFormat("json");
|