Support binary portability files in UI and CLI

This commit is contained in:
dotta
2026-03-19 07:23:36 -05:00
parent dbc9375256
commit 6d564e0539
5 changed files with 155 additions and 36 deletions

View File

@@ -4,6 +4,7 @@ import path from "node:path";
import * as p from "@clack/prompts";
import type {
Company,
CompanyPortabilityFileEntry,
CompanyPortabilityExportResult,
CompanyPortabilityInclude,
CompanyPortabilityPreviewResult,
@@ -50,6 +51,30 @@ interface CompanyImportOptions extends BaseClientOptions {
dryRun?: boolean;
}
const binaryContentTypeByExtension: Record<string, string> = {
".gif": "image/gif",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".svg": "image/svg+xml",
".webp": "image/webp",
};
function readPortableFileEntry(filePath: string, contents: Buffer): CompanyPortabilityFileEntry {
const contentType = binaryContentTypeByExtension[path.extname(filePath).toLowerCase()];
if (!contentType) return contents.toString("utf8");
return {
encoding: "base64",
data: contents.toString("base64"),
contentType,
};
}
function portableFileEntryToWriteValue(entry: CompanyPortabilityFileEntry): string | Uint8Array {
if (typeof entry === "string") return entry;
return Buffer.from(entry.data, "base64");
}
function isUuidLike(value: string): boolean {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
}
@@ -95,7 +120,11 @@ function isGithubUrl(input: string): boolean {
return /^https?:\/\/github\.com\//i.test(input.trim());
}
async function collectPackageFiles(root: string, current: string, files: Record<string, string>): Promise<void> {
async function collectPackageFiles(
root: string,
current: string,
files: Record<string, CompanyPortabilityFileEntry>,
): Promise<void> {
const entries = await readdir(current, { withFileTypes: true });
for (const entry of entries) {
if (entry.name.startsWith(".git")) continue;
@@ -107,20 +136,21 @@ async function collectPackageFiles(root: string, current: string, files: Record<
if (!entry.isFile()) continue;
const isMarkdown = entry.name.endsWith(".md");
const isPaperclipYaml = entry.name === ".paperclip.yaml" || entry.name === ".paperclip.yml";
if (!isMarkdown && !isPaperclipYaml) continue;
const contentType = binaryContentTypeByExtension[path.extname(entry.name).toLowerCase()];
if (!isMarkdown && !isPaperclipYaml && !contentType) continue;
const relativePath = path.relative(root, absolutePath).replace(/\\/g, "/");
files[relativePath] = await readFile(absolutePath, "utf8");
files[relativePath] = readPortableFileEntry(relativePath, await readFile(absolutePath));
}
}
async function resolveInlineSourceFromPath(inputPath: string): Promise<{
rootPath: string;
files: Record<string, string>;
files: Record<string, CompanyPortabilityFileEntry>;
}> {
const resolved = path.resolve(inputPath);
const resolvedStat = await stat(resolved);
const rootDir = resolvedStat.isDirectory() ? resolved : path.dirname(resolved);
const files: Record<string, string> = {};
const files: Record<string, CompanyPortabilityFileEntry> = {};
await collectPackageFiles(rootDir, rootDir, files);
return {
rootPath: path.basename(rootDir),
@@ -135,7 +165,12 @@ async function writeExportToFolder(outDir: string, exported: CompanyPortabilityE
const normalized = relativePath.replace(/\\/g, "/");
const filePath = path.join(root, normalized);
await mkdir(path.dirname(filePath), { recursive: true });
await writeFile(filePath, content, "utf8");
const writeValue = portableFileEntryToWriteValue(content);
if (typeof writeValue === "string") {
await writeFile(filePath, writeValue, "utf8");
} else {
await writeFile(filePath, writeValue);
}
}
}
@@ -397,7 +432,7 @@ export function registerCompanyCommands(program: Command): void {
}
let sourcePayload:
| { type: "inline"; rootPath?: string | null; files: Record<string, string> }
| { type: "inline"; rootPath?: string | null; files: Record<string, CompanyPortabilityFileEntry> }
| { type: "url"; url: string }
| { type: "github"; url: string };