mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
chore: move sandbox to apps/ (#1171)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import {
|
||||
confirm,
|
||||
filePath,
|
||||
getChanges,
|
||||
mirrorTemplatesToSrc,
|
||||
printDiff,
|
||||
removeGeneratedSrcFiles,
|
||||
srcPath,
|
||||
templatesPath,
|
||||
} from './shared.js';
|
||||
|
||||
const changes = getChanges();
|
||||
|
||||
// Files in src that differ from templates (will be overwritten)
|
||||
const modified = changes.filter((d) => d.state === 'distinct');
|
||||
// Files only in src (will be deleted)
|
||||
const added = changes.filter((d) => d.state === 'left');
|
||||
// Files only in templates (will be recreated in src)
|
||||
const missing = changes.filter((d) => d.state === 'right');
|
||||
|
||||
if (modified.length === 0 && added.length === 0 && missing.length === 0) {
|
||||
console.log(chalk.gray('\nNo local changes. Already in sync with templates.\n'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (modified.length > 0 || added.length > 0) {
|
||||
console.log(chalk.bold('\nThe following changes in src/ will be replaced from templates/:\n'));
|
||||
}
|
||||
|
||||
for (const change of modified) {
|
||||
const label = filePath(change);
|
||||
console.log(chalk.yellow(` ~ ${label}`));
|
||||
printDiff(srcPath(change), templatesPath(change), label);
|
||||
}
|
||||
|
||||
for (const change of added) {
|
||||
console.log(chalk.red(` + ${filePath(change)} (will be deleted)`));
|
||||
}
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.log(chalk.bold('\nThe following template files will be restored into src/:\n'));
|
||||
|
||||
for (const change of missing) {
|
||||
console.log(chalk.green(` + ${filePath(change)} (will be restored)`));
|
||||
}
|
||||
}
|
||||
|
||||
if (modified.length > 0 || added.length > 0 || missing.length > 0) {
|
||||
console.log();
|
||||
}
|
||||
|
||||
const ok = await confirm('Reset src/ to templates? This cannot be undone.');
|
||||
|
||||
if (!ok) {
|
||||
console.log(chalk.gray('\nAborted. No files were changed.\n'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const change of modified) {
|
||||
await fs.copy(templatesPath(change), srcPath(change), { overwrite: true });
|
||||
console.log(chalk.green(` ✔ Reset ${filePath(change)}`));
|
||||
}
|
||||
|
||||
for (const change of added) {
|
||||
await fs.remove(srcPath(change));
|
||||
console.log(chalk.red(` ✔ Deleted ${filePath(change)}`));
|
||||
}
|
||||
|
||||
const restored = await mirrorTemplatesToSrc();
|
||||
const removedGenerated = await removeGeneratedSrcFiles();
|
||||
|
||||
for (const file of restored) {
|
||||
console.log(chalk.green(` ✔ Restored ${file.replace(/^src\//, '')}`));
|
||||
}
|
||||
|
||||
for (const file of removedGenerated) {
|
||||
console.log(chalk.green(` ✔ Removed generated ${file.replace(/^src\//, '')}`));
|
||||
}
|
||||
|
||||
console.log(chalk.bold.green('\nReset complete!\n'));
|
||||
@@ -0,0 +1,12 @@
|
||||
import { mirrorTemplatesToSrc, removeGeneratedSrcFiles } from './shared.js';
|
||||
|
||||
const created = await mirrorTemplatesToSrc();
|
||||
const removed = await removeGeneratedSrcFiles();
|
||||
|
||||
for (const file of created) {
|
||||
console.log(`Created ${file}`);
|
||||
}
|
||||
|
||||
for (const file of removed) {
|
||||
console.log(`Removed generated ${file}`);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import path from 'node:path';
|
||||
import chalk from 'chalk';
|
||||
import { createTwoFilesPatch } from 'diff';
|
||||
import type { Result } from 'dir-compare';
|
||||
import { compareSync } from 'dir-compare';
|
||||
import fs from 'fs-extra';
|
||||
import prompts from 'prompts';
|
||||
|
||||
export const SRC = './src';
|
||||
export const TEMPLATES = './templates';
|
||||
const IGNORED_ROOT_FILES = new Set(['index.html']);
|
||||
const GENERATED_SRC_FILES = new Set(['index.html', '__app-shell__.ts']);
|
||||
|
||||
export interface Change {
|
||||
state: 'left' | 'right' | 'distinct';
|
||||
name: string;
|
||||
relativePath: string;
|
||||
}
|
||||
|
||||
export function getChanges(): Change[] {
|
||||
const res: Result = compareSync(SRC, TEMPLATES, { compareContent: true });
|
||||
|
||||
return (res.diffSet ?? [])
|
||||
.filter((d) => d.state !== 'equal' && d.type1 !== 'directory' && d.type2 !== 'directory')
|
||||
.map((d) => ({
|
||||
state: d.state as Change['state'],
|
||||
name: (d.name1 ?? d.name2)!,
|
||||
relativePath: d.relativePath,
|
||||
}))
|
||||
.filter((d) => d.name != null && !shouldIgnoreChange(d));
|
||||
}
|
||||
|
||||
function colorizePatch(patch: string): string {
|
||||
return patch
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
if (line.startsWith('---') || line.startsWith('+++')) return chalk.dim(line);
|
||||
if (line.startsWith('-')) return chalk.red(line);
|
||||
if (line.startsWith('+')) return chalk.green(line);
|
||||
if (line.startsWith('@@')) return chalk.cyan(line);
|
||||
return chalk.gray(line);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export function printDiff(oldPath: string, newPath: string, label: string): void {
|
||||
const oldContent = fs.existsSync(oldPath) ? fs.readFileSync(oldPath, 'utf8') : '';
|
||||
const newContent = fs.existsSync(newPath) ? fs.readFileSync(newPath, 'utf8') : '';
|
||||
const patch = createTwoFilesPatch(label, label, oldContent, newContent, undefined, undefined, { context: 3 });
|
||||
|
||||
// Strip the first two header lines (Index + ===) that createTwoFilesPatch adds
|
||||
const lines = patch.split('\n');
|
||||
const body = lines.slice(2).join('\n');
|
||||
|
||||
console.log(colorizePatch(body));
|
||||
}
|
||||
|
||||
function isGeneratedSrcFile(change: Change): boolean {
|
||||
return change.relativePath === '.' && GENERATED_SRC_FILES.has(change.name);
|
||||
}
|
||||
|
||||
function isIgnoredRootFile(change: Change): boolean {
|
||||
return change.relativePath === '.' && IGNORED_ROOT_FILES.has(change.name);
|
||||
}
|
||||
|
||||
function shouldIgnoreChange(change: Change): boolean {
|
||||
if (isGeneratedSrcFile(change) || isIgnoredRootFile(change)) return true;
|
||||
|
||||
// Sync/reset only manage sandbox directories, not loose files at the root.
|
||||
return change.relativePath === '.';
|
||||
}
|
||||
|
||||
export function filePath(change: Change): string {
|
||||
return path.join(change.relativePath, change.name);
|
||||
}
|
||||
|
||||
export function srcPath(change: Change): string {
|
||||
return path.join(SRC, change.relativePath, change.name);
|
||||
}
|
||||
|
||||
export function templatesPath(change: Change): string {
|
||||
return path.join(TEMPLATES, change.relativePath, change.name);
|
||||
}
|
||||
|
||||
export async function mirrorTemplatesToSrc(): Promise<string[]> {
|
||||
const created: string[] = [];
|
||||
|
||||
async function mirror(dir: string): Promise<void> {
|
||||
for (const entry of await fs.readdir(dir)) {
|
||||
const templatePath = path.join(dir, entry);
|
||||
const relativeFilePath = path.relative(TEMPLATES, templatePath);
|
||||
const targetPath = path.join(SRC, relativeFilePath);
|
||||
const stats = await fs.stat(templatePath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
await fs.ensureDir(targetPath);
|
||||
await mirror(templatePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (await fs.pathExists(targetPath)) continue;
|
||||
|
||||
await fs.copy(templatePath, targetPath);
|
||||
created.push(targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of await fs.readdir(TEMPLATES)) {
|
||||
const templatePath = path.join(TEMPLATES, entry);
|
||||
const stats = await fs.stat(templatePath);
|
||||
|
||||
if (!stats.isDirectory()) continue;
|
||||
|
||||
await fs.ensureDir(path.join(SRC, entry));
|
||||
await mirror(templatePath);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
export async function removeGeneratedSrcFiles(): Promise<string[]> {
|
||||
const removed: string[] = [];
|
||||
|
||||
for (const file of GENERATED_SRC_FILES) {
|
||||
const filePath = path.join(SRC, file);
|
||||
|
||||
if (!(await fs.pathExists(filePath))) continue;
|
||||
|
||||
await fs.remove(filePath);
|
||||
removed.push(filePath);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
export async function confirm(message: string): Promise<boolean> {
|
||||
const { ok } = await prompts({
|
||||
type: 'confirm',
|
||||
name: 'ok',
|
||||
message,
|
||||
initial: false,
|
||||
});
|
||||
|
||||
return ok === true;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { confirm, filePath, getChanges, printDiff, SRC, srcPath, TEMPLATES, templatesPath } from './shared.js';
|
||||
|
||||
console.log(chalk.bold(`\nComparing ${SRC} → ${TEMPLATES}\n`));
|
||||
|
||||
const changes = getChanges();
|
||||
|
||||
if (changes.length === 0) {
|
||||
console.log(chalk.gray('No differences found. Directories are in sync.'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const change of changes) {
|
||||
const label = filePath(change);
|
||||
|
||||
if (change.state === 'left') {
|
||||
console.log(chalk.green(` + ${label} (new in src)`));
|
||||
printDiff('', srcPath(change), label);
|
||||
}
|
||||
|
||||
if (change.state === 'right') {
|
||||
console.log(chalk.red(` - ${label} (only in templates)`));
|
||||
}
|
||||
|
||||
if (change.state === 'distinct') {
|
||||
console.log(chalk.yellow(` ~ ${label} (modified)`));
|
||||
printDiff(templatesPath(change), srcPath(change), label);
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(chalk.bold(`${changes.length} change(s) found.`));
|
||||
console.log();
|
||||
|
||||
const ok = await confirm(`Copy all changes from ${SRC} to ${TEMPLATES}?`);
|
||||
|
||||
if (!ok) {
|
||||
console.log(chalk.gray('\nAborted. No files were copied.'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Only copy changed/new files from src — skip 'right' entries (only in templates)
|
||||
for (const change of changes) {
|
||||
if (change.state === 'right') continue;
|
||||
|
||||
const dest = templatesPath(change);
|
||||
await fs.copy(srcPath(change), dest, { overwrite: true });
|
||||
console.log(chalk.green(` ✔ Copied ${filePath(change)}`));
|
||||
}
|
||||
|
||||
console.log(chalk.bold.green('\nDone!\n'));
|
||||
Reference in New Issue
Block a user