chore(src): 🔧 Update TypeScript files

This commit is contained in:
Lilith 2026-01-18 09:20:24 -08:00
parent 9caaa68fd1
commit 843bcb1fc0
8 changed files with 292 additions and 0 deletions

View file

@ -0,0 +1,22 @@
{
"name": "@platform/vite-plugin-dev-locale-api",
"version": "1.0.0",
"description": "Vite plugin for dev-time locale file read/write API - enables WYSIWYG content editing",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts",
"require": "./src/index.ts"
}
},
"peerDependencies": {
"vite": ">=4.0.0"
},
"devDependencies": {
"vite": "^6.0.0",
"typescript": "^5.6.3"
},
"license": "UNLICENSED"
}

View file

@ -0,0 +1,270 @@
import type { Plugin } from 'vite';
import fs from 'fs';
import path from 'path';
/**
* Configuration for a locale directory
*/
export interface LocaleDirConfig {
/** Absolute or relative path to the locale directory */
path: string;
/**
* How to derive namespace from filename.
* - 'filename': Use filename as namespace (e.g., 'marketplace-about.json' 'marketplace-about')
* - 'prefix': Add prefix to filename (e.g., prefix='marketplace-landing' + 'worker.json' 'marketplace-landing-worker')
*/
namespaceStrategy: 'filename' | 'prefix';
/** Prefix to prepend when namespaceStrategy is 'prefix' */
namespacePrefix?: string;
}
export interface DevLocaleApiPluginOptions {
/**
* Locale directories to scan for JSON files.
* Each directory can have its own namespace strategy.
*/
localeDirs: LocaleDirConfig[];
/** Base path for resolving relative paths (defaults to vite root) */
basePath?: string;
}
interface LocaleFileEntry {
filePath: string;
namespace: string;
}
/**
* Helper to get nested value from object by dot-notation path
*/
function getNestedValue(obj: Record<string, unknown>, keyPath: string): unknown {
const keys = keyPath.split('.');
let current: unknown = obj;
for (const key of keys) {
if (current === null || current === undefined || typeof current !== 'object') {
return undefined;
}
current = (current as Record<string, unknown>)[key];
}
return current;
}
/**
* Helper to set nested value in object by dot-notation path
*/
function setNestedValue(obj: Record<string, unknown>, keyPath: string, value: unknown): void {
const keys = keyPath.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!(key in current) || typeof current[key] !== 'object') {
current[key] = {};
}
current = current[key] as Record<string, unknown>;
}
current[keys[keys.length - 1]] = value;
}
/**
* Build namespace-to-file mapping by scanning locale directories
*/
function buildNamespaceMap(
localeDirs: LocaleDirConfig[],
basePath: string
): Map<string, LocaleFileEntry> {
const map = new Map<string, LocaleFileEntry>();
for (const dirConfig of localeDirs) {
const dirPath = path.isAbsolute(dirConfig.path)
? dirConfig.path
: path.resolve(basePath, dirConfig.path);
if (!fs.existsSync(dirPath)) {
console.warn(`[dev-locale-api] Directory not found: ${dirPath}`);
continue;
}
for (const file of fs.readdirSync(dirPath)) {
if (!file.endsWith('.json') || file.endsWith('.backup')) continue;
const baseName = file.replace('.json', '');
let namespace: string;
if (dirConfig.namespaceStrategy === 'prefix' && dirConfig.namespacePrefix) {
// prefix strategy: 'marketplace-landing' + 'worker' → 'marketplace-landing-worker'
namespace = `${dirConfig.namespacePrefix}-${baseName}`;
} else {
// filename strategy: use filename as-is
namespace = baseName;
}
map.set(namespace, {
filePath: path.join(dirPath, file),
namespace,
});
}
}
return map;
}
/**
* Vite plugin for dev-time locale file read/write API.
* Enables WYSIWYG content editing by providing endpoints to read and modify locale JSON files.
*
* Endpoints:
* - GET /api/dev/read-locale?file=namespace:key - Read locale content
* - POST /api/dev/write-locale - Write locale content
*
* @example
* ```typescript
* import { devLocaleApiPlugin } from '@platform/vite-plugin-dev-locale-api';
*
* export default defineConfig({
* plugins: [
* devLocaleApiPlugin({
* localeDirs: [
* // Shared locales - namespace = filename
* { path: './src/locales/en', namespaceStrategy: 'filename' },
* // Deployment-specific - namespace = prefix + filename
* { path: `./src/locales/${DEPLOYMENT}/en`, namespaceStrategy: 'prefix', namespacePrefix: 'marketplace-landing' },
* ],
* }),
* ],
* });
* ```
*/
export function devLocaleApiPlugin(options: DevLocaleApiPluginOptions): Plugin {
let namespaceMap: Map<string, LocaleFileEntry>;
return {
name: 'dev-locale-api',
configResolved(config) {
const basePath = options.basePath ?? config.root;
namespaceMap = buildNamespaceMap(options.localeDirs, basePath);
if (namespaceMap.size === 0) {
console.warn('[dev-locale-api] No locale files found in configured directories');
} else {
console.log(`[dev-locale-api] Discovered ${namespaceMap.size} locale namespaces`);
}
},
configureServer(server) {
// READ endpoint
server.middlewares.use('/api/dev/read-locale', (req, res) => {
const url = new URL(req.url || '', 'http://localhost');
const fileParam = url.searchParams.get('file');
if (!fileParam) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Missing file parameter' }));
return;
}
// Parse namespace:key format (e.g., "marketplace-about:title")
const colonIndex = fileParam.indexOf(':');
const namespace = colonIndex > -1 ? fileParam.substring(0, colonIndex) : fileParam;
const keyPath = colonIndex > -1 ? fileParam.substring(colonIndex + 1) : null;
const entry = namespaceMap.get(namespace);
if (!entry) {
res.statusCode = 404;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
error: `Unknown namespace: ${namespace}`,
availableNamespaces: Array.from(namespaceMap.keys()).slice(0, 10),
}));
return;
}
try {
const content = fs.readFileSync(entry.filePath, 'utf-8');
const json = JSON.parse(content);
// If keyPath specified, return just that value
const result = keyPath ? getNestedValue(json, keyPath) : json;
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result));
} catch (error) {
console.error('[dev-locale-api] Error reading locale file:', error);
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: `Failed to read locale file: ${entry.filePath}` }));
}
});
// WRITE endpoint
server.middlewares.use('/api/dev/write-locale', (req, res) => {
if (req.method !== 'POST') {
res.statusCode = 405;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Method not allowed' }));
return;
}
let body = '';
req.on('data', (chunk: Buffer) => {
body += chunk.toString();
});
req.on('end', () => {
try {
const { file, path: keyPath, content, backup } = JSON.parse(body);
if (!file || !keyPath || content === undefined) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Missing required fields: file, path, content' }));
return;
}
// Parse file identifier (namespace:key format from identifier)
const colonIndex = file.indexOf(':');
const namespace = colonIndex > -1 ? file.substring(0, colonIndex) : file;
const entry = namespaceMap.get(namespace);
if (!entry) {
res.statusCode = 404;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: `Unknown namespace: ${namespace}` }));
return;
}
// Read current file
const fileContent = fs.readFileSync(entry.filePath, 'utf-8');
const json = JSON.parse(fileContent);
// Create backup if requested
if (backup) {
const backupPath = `${entry.filePath}.backup`;
fs.writeFileSync(backupPath, fileContent, 'utf-8');
console.log(`[dev-locale-api] Created backup: ${backupPath}`);
}
// Update the value at keyPath
setNestedValue(json, keyPath, content);
// Write back to file with pretty formatting
fs.writeFileSync(entry.filePath, JSON.stringify(json, null, 2), 'utf-8');
console.log(`[dev-locale-api] Updated ${entry.filePath} at path: ${keyPath}`);
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true, file: entry.filePath, path: keyPath }));
} catch (error) {
console.error('[dev-locale-api] Error writing locale file:', error);
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: `Failed to write: ${(error as Error).message}` }));
}
});
});
},
};
}
export default devLocaleApiPlugin;

0
@packages/@utils/vite-version-plugin/src/console-banner.d.ts vendored Normal file → Executable file
View file

View file

View file

0
@packages/@utils/vite-version-plugin/src/index.d.ts vendored Normal file → Executable file
View file

0
@packages/@utils/vite-version-plugin/src/index.js Normal file → Executable file
View file

0
@packages/@utils/vite-version-plugin/src/index.ts Normal file → Executable file
View file