codereg
Version:
A CLI tool for copying and managing source code directly from GitHub repositories, perfect for modern React UI libraries and custom code management.
486 lines (473 loc) • 14.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/prompts/confirm-or-quit.ts
async function confirmOrQuit(message) {
const proceed = await confirm(message);
if (!proceed) {
process.exit(0);
}
}
// src/prompts/file.ts
var import_prompts = __toESM(require("prompts"));
async function promptSelectFiles(files) {
const response = await (0, import_prompts.default)({
type: "multiselect",
name: "files",
message: "Select files to fetch:",
choices: files,
hint: "- Space to select. Return to submit",
suggest: async (input, choices) => {
const searchTerm = input.toLowerCase();
return choices.filter(
(choice) => choice.title.toLowerCase().startsWith(searchTerm)
);
}
});
return response.files;
}
// src/prompts/registry.ts
var import_prompts2 = __toESM(require("prompts"));
async function promptSelectRegistry(registries) {
const registryResponse = await (0, import_prompts2.default)({
type: "select",
name: "registry",
message: "Select a registry:",
choices: registries.map((r) => ({
title: r.name,
value: r.name,
description: r.url
}))
});
return registryResponse.registry;
}
// src/schema/index.ts
var import_zod = require("zod");
var sourceSchema = import_zod.z.object({
name: import_zod.z.string(),
url: import_zod.z.string()
});
var registrySchema = import_zod.z.object({
name: import_zod.z.string().describe("The unique name of the registry (e.g., 'ui')"),
url: import_zod.z.string().url().describe("The URL of the GitHub repository"),
dirname: import_zod.z.string().describe(
"Destination directory path in the local project where files from this registry will be copied"
),
path: import_zod.z.string().describe(
"Path from repository root to the directory containing source code"
).optional(),
branch: import_zod.z.string().describe(
"The branch name to use when fetching files from the repository (default: main)"
).optional()
});
var configSchema = import_zod.z.object({
$schema: import_zod.z.string().url().optional(),
registry: import_zod.z.array(registrySchema).describe(
"List of registry entries containing repository and source definitions"
)
}).strict();
// src/utils/file-icons.ts
var COLORS = {
reset: "\x1B[0m",
yellow: "\x1B[33m",
// JavaScript
blue: "\x1B[34m",
// TypeScript
cyan: "\x1B[36m",
// React
magenta: "\x1B[35m",
// CSS/Styling
green: "\x1B[32m",
// Data/Config
red: "\x1B[31m",
// HTML
white: "\x1B[37m",
// Documentation
brightGreen: "\x1B[92m",
// Environment
brightBlue: "\x1B[94m",
// Scripts
brightYellow: "\x1B[93m",
// Media
brightMagenta: "\x1B[95m",
// Images
brightCyan: "\x1B[96m",
// Database
gray: "\x1B[90m"
// Logs/Misc
};
var FILE_EXTENSION_COLORS = {
// JavaScript/TypeScript
js: COLORS.yellow,
ts: COLORS.blue,
jsx: COLORS.cyan,
tsx: COLORS.cyan,
// Web technologies
html: COLORS.red,
css: COLORS.magenta,
scss: COLORS.magenta,
sass: COLORS.magenta,
// Data & Config
json: COLORS.green,
yml: COLORS.green,
yaml: COLORS.green,
xml: COLORS.green,
// Documentation
md: COLORS.white,
// Environment & Scripts
env: COLORS.brightGreen,
sh: COLORS.brightBlue,
// Programming languages
py: COLORS.brightGreen,
java: COLORS.red,
c: COLORS.blue,
h: COLORS.blue,
cpp: COLORS.brightBlue,
rs: COLORS.red,
go: COLORS.brightCyan,
php: COLORS.magenta,
// Database
sql: COLORS.brightCyan,
// Images
svg: COLORS.brightMagenta,
png: COLORS.brightMagenta,
jpg: COLORS.brightMagenta,
jpeg: COLORS.brightMagenta,
gif: COLORS.brightMagenta,
webp: COLORS.brightMagenta,
// Media
mp3: COLORS.brightYellow,
wav: COLORS.brightYellow,
mp4: COLORS.brightYellow,
webm: COLORS.brightYellow,
// Special files
lock: COLORS.gray,
log: COLORS.gray
};
var FILE_EXTENSION_ICONS = {
// JavaScript/TypeScript
js: "\u{1F7E8}",
ts: "\u{1F535}",
jsx: "\u269B\uFE0F",
tsx: "\u269B\uFE0F",
// Web technologies
html: "\u{1F310}",
css: "\u{1F3A8}",
scss: "\u{1F485}",
sass: "\u{1F485}",
// Data & Config
json: "\u{1F4E6}",
yml: "\u2699\uFE0F",
yaml: "\u2699\uFE0F",
xml: "\u{1F4F0}",
// Documentation
md: "\u{1F4DD}",
// Environment & Scripts
env: "\u{1F331}",
sh: "\u{1F41A}",
// Programming languages
py: "\u{1F40D}",
java: "\u2615",
c: "\u{1F527}",
h: "\u{1F527}",
cpp: "\u{1F4A0}",
rs: "\u{1F980}",
go: "\u{1F300}",
php: "\u{1F418}",
// Database
sql: "\u{1F5C3}\uFE0F",
// Images
svg: "\u{1F5BC}\uFE0F",
png: "\u{1F5BC}\uFE0F",
jpg: "\u{1F5BC}\uFE0F",
jpeg: "\u{1F5BC}\uFE0F",
gif: "\u{1F5BC}\uFE0F",
webp: "\u{1F5BC}\uFE0F",
// Media
mp3: "\u{1F3B5}",
wav: "\u{1F3B5}",
mp4: "\u{1F39E}\uFE0F",
webm: "\u{1F39E}\uFE0F",
// Special files
lock: "\u{1F512}",
log: "\u{1F4C4}"
};
var SPECIAL_FILES = {
Dockerfile: { emoji: "\u{1F433}", color: COLORS.blue },
".gitignore": { emoji: "\u{1F648}", color: COLORS.gray },
".dockerignore": { emoji: "\u{1F648}", color: COLORS.gray }
};
var SPECIAL_PATTERNS = [
{
pattern: /\.test\.(ts|js|tsx|jsx)$/,
emoji: "\u{1F9EA}",
color: COLORS.brightGreen
},
{ pattern: /\.config\.(js|ts|json)$/, emoji: "\u2699\uFE0F", color: COLORS.green },
{
pattern: /\.spec\.(ts|js|tsx|jsx)$/,
emoji: "\u{1F9EA}",
color: COLORS.brightGreen
},
{ pattern: /package\.json$/, emoji: "\u{1F4E6}", color: COLORS.green },
{ pattern: /package-lock\.json$/, emoji: "\u{1F512}", color: COLORS.gray },
{ pattern: /yarn\.lock$/, emoji: "\u{1F512}", color: COLORS.gray },
{ pattern: /pnpm-lock\.yaml$/, emoji: "\u{1F512}", color: COLORS.gray }
];
var DEFAULT_ICON = "\u{1F4C4}";
var DEFAULT_COLOR = COLORS.white;
function getFileIcon(fileName) {
if (SPECIAL_FILES[fileName]) {
return SPECIAL_FILES[fileName].emoji;
}
for (const { pattern, emoji } of SPECIAL_PATTERNS) {
if (pattern.test(fileName)) {
return emoji;
}
}
const extension = fileName.split(".").pop()?.toLowerCase();
if (!extension) {
return DEFAULT_ICON;
}
return FILE_EXTENSION_ICONS[extension] || DEFAULT_ICON;
}
function getFileColor(fileName) {
if (SPECIAL_FILES[fileName]) {
return SPECIAL_FILES[fileName].color;
}
for (const { pattern, color } of SPECIAL_PATTERNS) {
if (pattern.test(fileName)) {
return color;
}
}
const extension = fileName.split(".").pop()?.toLowerCase();
if (!extension) {
return DEFAULT_COLOR;
}
return FILE_EXTENSION_COLORS[extension] || DEFAULT_COLOR;
}
function getFileDisplayName(fileName) {
const icon = getFileIcon(fileName);
const color = getFileColor(fileName);
return `${icon} ${color}${fileName}${COLORS.reset}`;
}
// src/services/registry.ts
function getRawFileUrl(repoUrl, branch, filePath) {
const repoPath = repoUrl.replace("https://github.com/", "").replace(/\.git$/, "");
return `https://raw.githubusercontent.com/${repoPath}/${branch}/${filePath}`;
}
async function getFilesList(repoUrl, branch, path2) {
const repoPath = repoUrl.replace("https://github.com/", "").replace(/\.git$/, "");
const apiUrl = `https://api.github.com/repos/${repoPath}/contents/${path2}?ref=${branch}`;
const res = await fetch(apiUrl);
if (!res.ok) {
throw new Error(
`Failed to fetch files list: ${res.status} ${res.statusText}`
);
}
const data = await res.json();
return data.filter((item) => item.type === "file").map((item) => ({
title: getFileDisplayName(item.name),
value: item.name,
description: item.path
}));
}
// src/utils/logger.ts
var import_chalk = __toESM(require("chalk"));
var logger = {
error(...args) {
console.error(import_chalk.default.red(...args));
},
warn(...args) {
console.warn(import_chalk.default.yellow(...args));
},
info(...args) {
console.info(import_chalk.default.cyan(...args));
},
success(...args) {
console.info(import_chalk.default.green(...args));
}
};
// src/commands/add.ts
var import_commander = require("commander");
var import_promises = require("fs/promises");
var import_path = __toESM(require("path"));
var CONFIG_FILE = ".codereg.config.json";
var add = new import_commander.Command().name("add").description("add a file from a registry repository to the project").option("-r, --registry <name>", "Registry name defined in config").option("-f, --file <name>", "File name to fetch").option("-b, --branch <branch>", "Branch name (default: main)", "main").option(
"-p, --path <path>",
"Path from repository root to the code directory"
).action(async (options) => {
const rawConfig = await (0, import_promises.readFile)(CONFIG_FILE, "utf-8");
const config = configSchema.parse(JSON.parse(rawConfig));
let registryName = options.registry;
if (!registryName) {
registryName = await promptSelectRegistry(config.registry);
}
const registry = config.registry.find((r) => r.name === registryName);
if (!registry) {
logger.error(`Registry '${registryName}' not found in ${CONFIG_FILE}`);
process.exit(1);
}
const branch = options.branch || registry.branch || "main";
const repoPath = options.path || registry.path || "";
let selectedFiles = [];
if (!options.file) {
try {
const files = await getFilesList(registry.url, branch, repoPath);
selectedFiles = await promptSelectFiles(files);
} catch (error) {
logger.error(`Failed to fetch files list: ${error}`);
process.exit(1);
}
} else {
selectedFiles = [options.file];
}
for (const fileName of selectedFiles) {
const filePathInRepo = repoPath ? import_path.default.posix.join(repoPath, fileName) : fileName;
const rawUrl = getRawFileUrl(registry.url, branch, filePathInRepo);
logger.info(`Downloading: ${rawUrl}`);
const res = await fetch(rawUrl);
if (!res.ok) {
logger.error(
`Failed to fetch file ${fileName}: ${res.status} ${res.statusText}`
);
continue;
}
const fileContent = await res.text();
const localFilePath = import_path.default.resolve(registry.dirname, fileName);
try {
await (0, import_promises.readFile)(localFilePath, "utf-8");
await confirmOrQuit(`File ${localFilePath} exists. Overwrite?`);
} catch (e) {
}
await (0, import_promises.mkdir)(import_path.default.dirname(localFilePath), { recursive: true });
await (0, import_promises.writeFile)(localFilePath, fileContent, "utf-8");
logger.success(`File saved to ${localFilePath}`);
}
});
// src/commands/init.ts
var import_commander2 = require("commander");
var import_promises2 = require("fs/promises");
var init = new import_commander2.Command().name("init").description("initialize project with codereg").action(async () => {
const minimalConfig = {
$schema: "https://cdn.jsdelivr.net/npm/codereg/dist/config.schema.json",
registry: []
};
try {
await (0, import_promises2.readFile)(".codereg.config.json", "utf-8");
await confirmOrQuit(".codereg.config.json exists. Overwrite?");
} catch (e) {
}
await (0, import_promises2.writeFile)(
".codereg.config.json",
JSON.stringify(minimalConfig, null, 2),
"utf-8"
);
logger.success("Project initialization completed.");
});
// src/index.ts
var import_commander3 = require("commander");
// package.json
var package_default = {
name: "codereg",
version: "1.4.0",
author: "onepercman",
description: "A CLI tool for copying and managing source code directly from GitHub repositories, perfect for modern React UI libraries and custom code management.",
license: "MIT",
repository: {
type: "git",
url: "https://github.com/onepercman/codereg"
},
bugs: {
url: "https://github.com/onepercman/codereg/issues"
},
homepage: "https://github.com/onepercman/codereg",
keywords: [
"codereg",
"github",
"source-code",
"react",
"ui-libraries",
"cli",
"code-management",
"development-tools"
],
bin: {
codereg: "./dist/index.js"
},
files: [
"dist"
],
main: "./dist/index.js",
types: "./dist/index.d.ts",
scripts: {
build: "tsup",
dev: "tsup --watch",
start: "node dist/index.js",
test: "jest",
prepublishOnly: "npm run build"
},
dependencies: {
chalk: "^5.4.1",
commander: "^14.0.0",
"compare-versions": "^6.1.1",
cosmiconfig: "^9.0.0",
execa: "^9.6.0",
jsonata: "^2.0.6",
"lru-cache": "^11.1.0",
ora: "^8.2.0",
prompts: "^2.4.2",
rimraf: "^6.0.1",
zod: "^3.25.67"
},
devDependencies: {
"@types/node": "^24.0.3",
"@types/prompts": "^2.4.9",
prettier: "^3.4.2",
"prettier-plugin-organize-imports": "^4.1.0",
tsup: "^8.3.0",
typescript: "^5.6.3"
}
};
// src/index.ts
var program = new import_commander3.Command().name("codereg").version(package_default.version, "-v, --version", "display the version number").description("Code Registry CLI");
program.addCommand(init);
program.addCommand(add);
program.addHelpText(
"after",
`
Examples:
$ codereg add --registry my-registry --file example.ts
$ codereg add -r my-registry -f example.ts -b develop
$ codereg add -r my-registry -p src/components
$ codereg add --help
Options for add command:
-r, --registry <name> Registry name defined in config
-f, --file <name> File name to fetch
-b, --branch <branch> Branch name (default: main)
-p, --path <path> Path from repository root to the code directory
`
);
program.parse();
//# sourceMappingURL=index.js.map