feat(cli): scaffold @vue-tui/cli package with all modules
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@vue-tui/cli",
|
||||
"version": "0.0.0",
|
||||
"bin": {
|
||||
"vue-tui": "./dist/index.mjs"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vp pack",
|
||||
"dev": "vp pack --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"vite": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite-plus": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
export interface MemoryFiles {
|
||||
files: Map<string, unknown>;
|
||||
get(key: string): { source: string | Uint8Array } | undefined;
|
||||
}
|
||||
|
||||
export async function extractBundle(memoryFiles: MemoryFiles, outDir: string): Promise<string> {
|
||||
await rm(outDir, { recursive: true, force: true });
|
||||
await mkdir(outDir, { recursive: true });
|
||||
|
||||
const keys = [...memoryFiles.files.keys()];
|
||||
let entryPath: string | undefined;
|
||||
|
||||
for (const key of keys) {
|
||||
const file = memoryFiles.get(key);
|
||||
if (!file) continue;
|
||||
|
||||
const outPath = join(outDir, key);
|
||||
await mkdir(dirname(outPath), { recursive: true });
|
||||
|
||||
const source = typeof file.source === "string" ? file.source : Buffer.from(file.source);
|
||||
await writeFile(outPath, source);
|
||||
|
||||
if (key.endsWith(".js") && !key.endsWith(".js.map")) {
|
||||
entryPath ??= outPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryPath) {
|
||||
throw new Error("No JS bundle found in Vite memoryFiles");
|
||||
}
|
||||
return entryPath;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { register } from "node:module";
|
||||
|
||||
register(
|
||||
`data:text/javascript,${encodeURIComponent(`
|
||||
const VITE_PORT = process.env.VUE_TUI_HMR_PORT || '5173';
|
||||
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
if (/^\\/hmr_patch_\\d+\\.js$/.test(specifier)) {
|
||||
return { url: 'vite-hmr://' + specifier, shortCircuit: true };
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
if (url.startsWith('vite-hmr://')) {
|
||||
const path = url.replace('vite-hmr://', '');
|
||||
const res = await fetch('http://localhost:' + VITE_PORT + path);
|
||||
if (!res.ok) throw new Error('HMR patch fetch failed: ' + res.status);
|
||||
return { format: 'module', source: await res.text(), shortCircuit: true };
|
||||
}
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
`)}`,
|
||||
import.meta.url,
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const entry = process.argv[2];
|
||||
console.log(`vue-tui dev ${entry ?? "(auto-detect)"} — not yet implemented`);
|
||||
@@ -0,0 +1,25 @@
|
||||
export type LogMode = "stdout" | "silent";
|
||||
|
||||
export interface Logger {
|
||||
mode: LogMode;
|
||||
info(msg: string): void;
|
||||
error(msg: string): void;
|
||||
}
|
||||
|
||||
export function createLogger(): Logger {
|
||||
let mode: LogMode = "stdout";
|
||||
return {
|
||||
get mode() {
|
||||
return mode;
|
||||
},
|
||||
set mode(m: LogMode) {
|
||||
mode = m;
|
||||
},
|
||||
info(msg: string) {
|
||||
if (mode === "stdout") process.stdout.write(msg + "\n");
|
||||
},
|
||||
error(msg: string) {
|
||||
if (mode === "stdout") process.stderr.write(msg + "\n");
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { spawn, type ChildProcess } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { Logger } from "./logger.ts";
|
||||
|
||||
export interface ProcessManagerOptions {
|
||||
bundlePath: string;
|
||||
hmrPort: number;
|
||||
logger: Logger;
|
||||
onExit?: (code: number | null) => void;
|
||||
}
|
||||
|
||||
const loaderPath = fileURLToPath(new URL("./hmr-loader.mjs", import.meta.url));
|
||||
|
||||
export function createProcessManager(options: ProcessManagerOptions) {
|
||||
let child: ChildProcess | null = null;
|
||||
let currentBundlePath = options.bundlePath;
|
||||
|
||||
function doSpawn() {
|
||||
options.logger.mode = "silent";
|
||||
child = spawn("node", ["--import", loaderPath, currentBundlePath], {
|
||||
stdio: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
VUE_TUI_DEV: "1",
|
||||
VUE_TUI_HMR_PORT: String(options.hmrPort),
|
||||
},
|
||||
});
|
||||
|
||||
child.on("exit", (code) => {
|
||||
options.logger.mode = "stdout";
|
||||
child = null;
|
||||
options.onExit?.(code);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForExit(proc: ChildProcess, timeout: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const timer = setTimeout(() => {
|
||||
resolve();
|
||||
}, timeout);
|
||||
proc.on("exit", () => {
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let restartTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
return {
|
||||
spawn: doSpawn,
|
||||
|
||||
setBundlePath(path: string) {
|
||||
currentBundlePath = path;
|
||||
},
|
||||
|
||||
async restart() {
|
||||
if (restartTimer) clearTimeout(restartTimer);
|
||||
restartTimer = setTimeout(async () => {
|
||||
restartTimer = null;
|
||||
if (child) {
|
||||
child.kill("SIGTERM");
|
||||
await waitForExit(child, 2000);
|
||||
if (child) child.kill("SIGKILL");
|
||||
}
|
||||
doSpawn();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
async shutdown() {
|
||||
if (restartTimer) clearTimeout(restartTimer);
|
||||
if (child) {
|
||||
await waitForExit(child, 2000);
|
||||
if (child) child.kill("SIGKILL");
|
||||
}
|
||||
},
|
||||
|
||||
get running() {
|
||||
return child !== null;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Plugin } from "vite";
|
||||
|
||||
export interface VueTuiDevPluginOptions {
|
||||
entry?: string;
|
||||
}
|
||||
|
||||
export function vueTuiDevPlugin(options?: VueTuiDevPluginOptions): Plugin {
|
||||
return {
|
||||
name: "vue-tui:dev",
|
||||
config() {
|
||||
return {
|
||||
experimental: { bundledDev: true },
|
||||
build: {
|
||||
modulePreload: false,
|
||||
...(options?.entry ? { lib: { entry: options.entry } } : {}),
|
||||
},
|
||||
define: {
|
||||
__VUE_TUI_DEV__: "true",
|
||||
},
|
||||
resolve: {
|
||||
conditions: ["node"],
|
||||
},
|
||||
server: {
|
||||
strictPort: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from "vite-plus";
|
||||
|
||||
export default defineConfig({
|
||||
pack: {
|
||||
entry: ["src/index.ts", "src/hmr-loader.ts"],
|
||||
format: "esm",
|
||||
shims: true,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user