"添加前端模板和运行代码模块"
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CLI entry: parse argv and dispatch install / uninstall.
|
||||
*/
|
||||
|
||||
import { main as installMain } from './install.js';
|
||||
import { main as uninstallMain } from './uninstall.js';
|
||||
|
||||
const command = process.argv[2];
|
||||
|
||||
switch (command) {
|
||||
case 'install':
|
||||
installMain();
|
||||
break;
|
||||
case 'uninstall':
|
||||
uninstallMain();
|
||||
break;
|
||||
case undefined:
|
||||
case '--help':
|
||||
case '-h':
|
||||
console.log(`
|
||||
@xagi/vite-plugin-design-mode CLI
|
||||
|
||||
Usage:
|
||||
npx @xagi/vite-plugin-design-mode <command>
|
||||
pnpm dlx @xagi/vite-plugin-design-mode <command>
|
||||
|
||||
Commands:
|
||||
install Add the plugin to package.json and vite.config
|
||||
uninstall Remove the plugin from package.json and vite.config
|
||||
|
||||
Notes:
|
||||
- Only edits config files; it does not run your package manager.
|
||||
- After install/uninstall, run install/remove yourself to sync node_modules.
|
||||
|
||||
Examples:
|
||||
pnpm dlx @xagi/vite-plugin-design-mode install
|
||||
npx @xagi/vite-plugin-design-mode install
|
||||
|
||||
pnpm dlx @xagi/vite-plugin-design-mode uninstall
|
||||
npx @xagi/vite-plugin-design-mode uninstall
|
||||
|
||||
More info: https://www.npmjs.com/package/@xagi/vite-plugin-design-mode
|
||||
`);
|
||||
break;
|
||||
default:
|
||||
console.error(`✗ Unknown command: ${command}`);
|
||||
console.error('Run with --help to see available commands.');
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Install flow: add devDependency + wire `appdevDesignMode()` in Vite config.
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { join, resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const PLUGIN_NAME = '@xagi/vite-plugin-design-mode';
|
||||
const VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];
|
||||
|
||||
/**
|
||||
* Read plugin version by walking up from this file until the package named PLUGIN_NAME is found.
|
||||
*/
|
||||
function getPluginVersion(): string {
|
||||
try {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
let currentDir = resolve(__dirname);
|
||||
const root = resolve('/');
|
||||
let depth = 0;
|
||||
const maxDepth = 5;
|
||||
|
||||
while (currentDir !== root && depth < maxDepth) {
|
||||
const packageJsonPath = join(currentDir, 'package.json');
|
||||
if (existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
||||
if (packageJson.name === PLUGIN_NAME && packageJson.version) {
|
||||
return packageJson.version;
|
||||
}
|
||||
} catch (_error) {
|
||||
// Invalid JSON — keep walking
|
||||
}
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
depth++;
|
||||
}
|
||||
} catch (_error) {
|
||||
// Fallback to latest below
|
||||
}
|
||||
|
||||
return 'latest';
|
||||
}
|
||||
|
||||
/** Walk up from startDir until package.json exists. */
|
||||
function findProjectRoot(startDir: string = process.cwd()): string {
|
||||
let currentDir = resolve(startDir);
|
||||
const root = resolve('/');
|
||||
|
||||
while (currentDir !== root) {
|
||||
const packageJsonPath = join(currentDir, 'package.json');
|
||||
if (existsSync(packageJsonPath)) {
|
||||
return currentDir;
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
|
||||
return startDir;
|
||||
}
|
||||
|
||||
interface PackageJson {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
packageManager?: string;
|
||||
}
|
||||
|
||||
function getDependencyVersion(packageJson: PackageJson, depName: string): string | undefined {
|
||||
return packageJson.dependencies?.[depName] || packageJson.devDependencies?.[depName];
|
||||
}
|
||||
|
||||
/** package.json lists vite */
|
||||
function hasVite(packageJson: PackageJson): boolean {
|
||||
return !!packageJson.dependencies?.vite || !!packageJson.devDependencies?.vite;
|
||||
}
|
||||
|
||||
/** package.json lists react */
|
||||
function hasReact(packageJson: PackageJson): boolean {
|
||||
return !!packageJson.dependencies?.react || !!packageJson.devDependencies?.react;
|
||||
}
|
||||
|
||||
/** package.json lists vue */
|
||||
function hasVue(packageJson: PackageJson): boolean {
|
||||
return !!getDependencyVersion(packageJson, 'vue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false only when we can confidently identify a Vue 2 range.
|
||||
* Unknown or non-semver formats (workspace:, github:, file:) are treated as supported.
|
||||
*/
|
||||
function isVue3Version(version: string): boolean {
|
||||
const normalized = version.trim();
|
||||
if (!normalized) return true;
|
||||
|
||||
if (/(^|[^\d])2\./.test(normalized) || /(^|[^\d])\^?~?2(\D|$)/.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/(^|[^\d])3\./.test(normalized) || /(^|[^\d])\^?~?3(\D|$)/.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** PLUGIN_NAME present in deps */
|
||||
function isPluginInstalled(packageJson: PackageJson): boolean {
|
||||
return !!packageJson.dependencies?.[PLUGIN_NAME] || !!packageJson.devDependencies?.[PLUGIN_NAME];
|
||||
}
|
||||
|
||||
function addPluginToPackageJson(packageJson: PackageJson, version: string): PackageJson {
|
||||
if (!packageJson.devDependencies) {
|
||||
packageJson.devDependencies = {};
|
||||
}
|
||||
packageJson.devDependencies[PLUGIN_NAME] = `^${version}`;
|
||||
return packageJson;
|
||||
}
|
||||
|
||||
/** First existing vite.config.{ts,js,mjs} */
|
||||
function findViteConfig(projectRoot: string): string | null {
|
||||
for (const file of VITE_CONFIG_FILES) {
|
||||
const configPath = join(projectRoot, file);
|
||||
if (existsSync(configPath)) {
|
||||
return configPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasImport(content: string): boolean {
|
||||
const importPatterns = [
|
||||
/import\s+appdevDesignMode\s+from\s+['"]@xagi\/vite-plugin-design-mode['"]/,
|
||||
/import\s+\{\s*default\s+as\s+appdevDesignMode\s*\}\s+from\s+['"]@xagi\/vite-plugin-design-mode['"]/,
|
||||
];
|
||||
return importPatterns.some(pattern => pattern.test(content));
|
||||
}
|
||||
|
||||
function hasPluginConfig(content: string): boolean {
|
||||
const pluginPatterns = [/appdevDesignMode\s*\(/, /appdevDesignMode\s*\(\s*\{/];
|
||||
return pluginPatterns.some(pattern => pattern.test(content));
|
||||
}
|
||||
|
||||
function addImport(content: string): string {
|
||||
if (hasImport(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
const importRegex = /^import\s+.*?from\s+['"].*?['"];?$/gm;
|
||||
const imports = content.match(importRegex);
|
||||
|
||||
if (imports && imports.length > 0) {
|
||||
const lastImport = imports[imports.length - 1];
|
||||
const lastImportIndex = content.lastIndexOf(lastImport);
|
||||
const insertIndex = lastImportIndex + lastImport.length;
|
||||
const useSingleQuote = lastImport.includes("'");
|
||||
const quote = useSingleQuote ? "'" : '"';
|
||||
|
||||
const newImport = `\nimport appdevDesignMode from ${quote}@xagi/vite-plugin-design-mode${quote};`;
|
||||
return content.slice(0, insertIndex) + newImport + content.slice(insertIndex);
|
||||
}
|
||||
|
||||
const useSingleQuote = content.includes("'");
|
||||
const quote = useSingleQuote ? "'" : '"';
|
||||
return `import appdevDesignMode from ${quote}@xagi/vite-plugin-design-mode${quote};\n${content}`;
|
||||
}
|
||||
|
||||
function addPluginConfig(content: string): string {
|
||||
if (hasPluginConfig(content)) {
|
||||
return content.replace(/appdevDesignMode\s*\([^)]*\)/g, 'appdevDesignMode()');
|
||||
}
|
||||
|
||||
const pluginsArrayRegex = /plugins\s*:\s*\[/;
|
||||
const match = content.match(pluginsArrayRegex);
|
||||
|
||||
if (match) {
|
||||
const startIndex = match.index! + match[0].length;
|
||||
let depth = 1;
|
||||
let i = startIndex;
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let inTemplateString = false;
|
||||
|
||||
while (i < content.length && depth > 0) {
|
||||
const char = content[i];
|
||||
const prevChar = i > 0 ? content[i - 1] : '';
|
||||
|
||||
if ((char === '"' || char === "'") && prevChar !== '\\') {
|
||||
if (!inString && !inTemplateString) {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
} else if (inString && char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (char === '`' && prevChar !== '\\') {
|
||||
inTemplateString = !inTemplateString;
|
||||
}
|
||||
|
||||
if (!inString && !inTemplateString) {
|
||||
if (char === '[') {
|
||||
depth++;
|
||||
} else if (char === ']') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
const beforeClosing = content.substring(startIndex, i);
|
||||
const afterClosing = content.substring(i);
|
||||
|
||||
const cleanedBefore = beforeClosing
|
||||
.replace(/\/\/.*$/gm, '')
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.trim();
|
||||
|
||||
const hasOtherPlugins = cleanedBefore.length > 0;
|
||||
const beforePlugins = content.substring(0, match.index!);
|
||||
const lastNewlineIndex = beforePlugins.lastIndexOf('\n');
|
||||
const pluginsLine = beforePlugins.substring(lastNewlineIndex + 1);
|
||||
const indent = pluginsLine.match(/^(\s*)/)?.[1] || ' ';
|
||||
|
||||
if (hasOtherPlugins) {
|
||||
let lastNonWhitespace = beforeClosing.length - 1;
|
||||
while (lastNonWhitespace >= 0 && /\s/.test(beforeClosing[lastNonWhitespace])) {
|
||||
lastNonWhitespace--;
|
||||
}
|
||||
|
||||
const lastChar = lastNonWhitespace >= 0 ? beforeClosing[lastNonWhitespace] : '';
|
||||
const needsComma = lastChar !== ',';
|
||||
const insertText = (needsComma ? ',' : '') + '\n' + indent + ' appdevDesignMode()';
|
||||
|
||||
return (
|
||||
content.substring(0, startIndex + lastNonWhitespace + 1) +
|
||||
insertText +
|
||||
'\n' +
|
||||
indent +
|
||||
afterClosing
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
content.substring(0, startIndex) +
|
||||
'\n' +
|
||||
indent +
|
||||
' appdevDesignMode()\n' +
|
||||
indent +
|
||||
afterClosing
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
const defineConfigRegex = /defineConfig\s*\(\s*\{([\s\S]*?)\}\s*\)/;
|
||||
const configMatch = content.match(defineConfigRegex);
|
||||
|
||||
if (configMatch) {
|
||||
const configObject = configMatch[0];
|
||||
const pluginsConfig = `\n plugins: [\n appdevDesignMode()\n ],`;
|
||||
const newConfigObject = configObject.replace(/\}\s*\)$/, `${pluginsConfig}\n}`);
|
||||
return content.replace(defineConfigRegex, newConfigObject);
|
||||
}
|
||||
|
||||
return `${content}\n\n// appdevDesignMode plugin\nplugins: [appdevDesignMode()],`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('Installing @xagi/vite-plugin-design-mode...\n');
|
||||
const installerVersion = getPluginVersion();
|
||||
console.log(`[vite-plugin-design-mode] installer version: ${installerVersion}`);
|
||||
const projectRoot = findProjectRoot();
|
||||
console.log(`Project root: ${projectRoot}\n`);
|
||||
|
||||
const packageJsonPath = join(projectRoot, 'package.json');
|
||||
if (!existsSync(packageJsonPath)) {
|
||||
console.error('Error: package.json not found');
|
||||
console.error(`Directory: ${projectRoot}`);
|
||||
console.error('Run this command from your project root.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const packageJson: PackageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
||||
|
||||
const hasViteDep = hasVite(packageJson);
|
||||
const hasReactDep = hasReact(packageJson);
|
||||
const hasVueDep = hasVue(packageJson);
|
||||
const vueVersion = getDependencyVersion(packageJson, 'vue');
|
||||
const hasVue3Dep = hasVueDep && isVue3Version(vueVersion || '');
|
||||
|
||||
if (!hasViteDep) {
|
||||
console.error('Error: Vite not found in package.json');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!hasReactDep && !hasVueDep) {
|
||||
console.error('Error: neither React nor Vue found in package.json');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!hasReactDep && hasVueDep && !hasVue3Dep) {
|
||||
console.error(`Error: unsupported Vue version: ${vueVersion}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pluginVersion = installerVersion;
|
||||
const isInstalled = isPluginInstalled(packageJson);
|
||||
const updatedPackageJson = addPluginToPackageJson(packageJson, pluginVersion);
|
||||
const versionString = `^${pluginVersion}`;
|
||||
const currentVersion =
|
||||
packageJson.devDependencies?.[PLUGIN_NAME] || packageJson.dependencies?.[PLUGIN_NAME];
|
||||
|
||||
if (!isInstalled || currentVersion !== versionString) {
|
||||
writeFileSync(packageJsonPath, JSON.stringify(updatedPackageJson, null, 2) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
const viteConfigPath = findViteConfig(projectRoot);
|
||||
if (!viteConfigPath) {
|
||||
console.log('Done. Run your package manager install command.');
|
||||
return;
|
||||
}
|
||||
|
||||
let configContent = readFileSync(viteConfigPath, 'utf-8');
|
||||
const originalContent = configContent;
|
||||
configContent = addImport(configContent);
|
||||
configContent = addPluginConfig(configContent);
|
||||
|
||||
if (configContent !== originalContent) {
|
||||
writeFileSync(viteConfigPath, configContent, 'utf-8');
|
||||
}
|
||||
|
||||
console.log('Done.');
|
||||
}
|
||||
|
||||
export { main };
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Uninstall: remove devDependency and strip plugin from vite.config.
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { join, resolve, dirname } from 'path';
|
||||
|
||||
const PLUGIN_NAME = '@xagi/vite-plugin-design-mode';
|
||||
const VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];
|
||||
|
||||
/** Walk up from startDir until package.json exists. */
|
||||
function findProjectRoot(startDir: string = process.cwd()): string {
|
||||
let currentDir = resolve(startDir);
|
||||
const root = resolve('/');
|
||||
|
||||
while (currentDir !== root) {
|
||||
const packageJsonPath = join(currentDir, 'package.json');
|
||||
if (existsSync(packageJsonPath)) {
|
||||
return currentDir;
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
|
||||
return startDir;
|
||||
}
|
||||
|
||||
interface PackageJson {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
packageManager?: string;
|
||||
}
|
||||
|
||||
function isPluginInstalled(packageJson: PackageJson): boolean {
|
||||
return !!packageJson.dependencies?.[PLUGIN_NAME] || !!packageJson.devDependencies?.[PLUGIN_NAME];
|
||||
}
|
||||
|
||||
function removePluginFromPackageJson(packageJson: PackageJson): PackageJson {
|
||||
if (!isPluginInstalled(packageJson)) {
|
||||
return packageJson;
|
||||
}
|
||||
|
||||
if (packageJson.dependencies?.[PLUGIN_NAME]) {
|
||||
delete packageJson.dependencies[PLUGIN_NAME];
|
||||
if (Object.keys(packageJson.dependencies).length === 0) {
|
||||
delete packageJson.dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
if (packageJson.devDependencies?.[PLUGIN_NAME]) {
|
||||
delete packageJson.devDependencies[PLUGIN_NAME];
|
||||
if (Object.keys(packageJson.devDependencies).length === 0) {
|
||||
delete packageJson.devDependencies;
|
||||
}
|
||||
}
|
||||
|
||||
return packageJson;
|
||||
}
|
||||
|
||||
function findViteConfig(projectRoot: string): string | null {
|
||||
for (const file of VITE_CONFIG_FILES) {
|
||||
const configPath = join(projectRoot, file);
|
||||
if (existsSync(configPath)) {
|
||||
return configPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeImport(content: string): string {
|
||||
const importPatterns = [
|
||||
/import\s+appdevDesignMode\s+from\s+['"]@xagi\/vite-plugin-design-mode['"];?\s*\n?/g,
|
||||
/import\s+\{\s*default\s+as\s+appdevDesignMode\s*\}\s+from\s+['"]@xagi\/vite-plugin-design-mode['"];?\s*\n?/g,
|
||||
];
|
||||
|
||||
let result = content;
|
||||
for (const pattern of importPatterns) {
|
||||
result = result.replace(pattern, '');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function removePluginConfig(content: string): string {
|
||||
const pluginCallPattern = /appdevDesignMode\s*\([^)]*\)/g;
|
||||
let result = content.replace(pluginCallPattern, '');
|
||||
result = result.replace(/,\s*,/g, ',');
|
||||
result = result.replace(/,\s*\]/g, ']');
|
||||
result = result.replace(/\[\s*,/g, '[');
|
||||
result = result.replace(/,\s*}/g, '}');
|
||||
result = result.replace(/\/\/\s*appdevDesignMode.*?\n/g, '');
|
||||
result = result.replace(/\/\*\s*appdevDesignMode.*?\*\//g, '');
|
||||
result = result.replace(/,\s*plugins\s*:\s*\[\s*\]/g, '');
|
||||
result = result.replace(/plugins\s*:\s*\[\s*\]\s*,?/g, '');
|
||||
result = result.replace(/\n{3,}/g, '\n\n');
|
||||
result = result.replace(/[ \t]+$/gm, '');
|
||||
return result;
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('Removing @xagi/vite-plugin-design-mode...\n');
|
||||
const projectRoot = findProjectRoot();
|
||||
const packageJsonPath = join(projectRoot, 'package.json');
|
||||
if (!existsSync(packageJsonPath)) {
|
||||
console.error('Error: package.json not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const packageJson: PackageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
||||
if (!isPluginInstalled(packageJson)) {
|
||||
console.log('Plugin not listed in package.json, nothing to remove.');
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedPackageJson = removePluginFromPackageJson(packageJson);
|
||||
if (updatedPackageJson !== packageJson) {
|
||||
writeFileSync(packageJsonPath, JSON.stringify(updatedPackageJson, null, 2) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
const viteConfigPath = findViteConfig(projectRoot);
|
||||
if (!viteConfigPath) {
|
||||
console.log('Done.');
|
||||
return;
|
||||
}
|
||||
|
||||
let configContent = readFileSync(viteConfigPath, 'utf-8');
|
||||
const originalContent = configContent;
|
||||
configContent = removeImport(configContent);
|
||||
configContent = removePluginConfig(configContent);
|
||||
|
||||
if (configContent !== originalContent) {
|
||||
writeFileSync(viteConfigPath, configContent, 'utf-8');
|
||||
}
|
||||
|
||||
console.log('Done.');
|
||||
}
|
||||
|
||||
export { main };
|
||||
Reference in New Issue
Block a user