Compare commits
2 Commits
@esengine/
...
@esengine/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac4bc19c5 | ||
|
|
aed91dbe45 |
@@ -1,5 +1,16 @@
|
|||||||
# @esengine/cli
|
# @esengine/cli
|
||||||
|
|
||||||
|
## 1.5.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#359](https://github.com/esengine/esengine/pull/359) [`aed91db`](https://github.com/esengine/esengine/commit/aed91dbe4507459f8ac0563ee933e44fd4c9fea6) Thanks [@esengine](https://github.com/esengine)! - feat(cli): 添加 update 命令用于更新 ESEngine 包
|
||||||
|
- 新增 `esengine update` 命令检查并更新 @esengine/\* 包到最新版本
|
||||||
|
- 支持 `--check` 参数仅检查可用更新而不安装
|
||||||
|
- 支持 `--yes` 参数跳过确认提示
|
||||||
|
- 显示包更新状态,对比当前版本与最新版本
|
||||||
|
- 更新时保留版本前缀(^ 或 ~)
|
||||||
|
|
||||||
## 1.3.0
|
## 1.3.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esengine/cli",
|
"name": "@esengine/cli",
|
||||||
"version": "1.3.0",
|
"version": "1.5.0",
|
||||||
"description": "CLI tool for adding ESEngine ECS framework to existing projects",
|
"description": "CLI tool for adding ESEngine ECS framework to existing projects",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { getPlatformChoices, getPlatforms, getAdapter } from './adapters/index.j
|
|||||||
import type { PlatformType, ProjectConfig } from './adapters/types.js';
|
import type { PlatformType, ProjectConfig } from './adapters/types.js';
|
||||||
import { AVAILABLE_MODULES, getModuleById, getAllModuleIds, type ModuleInfo } from './modules.js';
|
import { AVAILABLE_MODULES, getModuleById, getAllModuleIds, type ModuleInfo } from './modules.js';
|
||||||
|
|
||||||
const VERSION = '1.1.0';
|
const VERSION = '1.4.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @zh 打印 Logo
|
* @zh 打印 Logo
|
||||||
@@ -585,6 +585,182 @@ async function removeCommand(moduleIds: string[], options: { yes?: boolean }): P
|
|||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @zh 获取 npm 包的最新版本
|
||||||
|
* @en Get latest version of npm package
|
||||||
|
*/
|
||||||
|
function getLatestVersion(packageName: string): string | null {
|
||||||
|
try {
|
||||||
|
const result = execSync(`npm view ${packageName} version`, {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe']
|
||||||
|
}).trim();
|
||||||
|
return result || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @zh 比较版本号,返回是否有更新
|
||||||
|
* @en Compare versions, return true if newer version available
|
||||||
|
*/
|
||||||
|
function isNewerVersion(current: string, latest: string): boolean {
|
||||||
|
const cleanCurrent = current.replace(/^\^|~/, '');
|
||||||
|
|
||||||
|
// "latest" 标签视为需要更新(固定到具体版本)
|
||||||
|
if (cleanCurrent === 'latest' || cleanCurrent === '*') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentParts = cleanCurrent.split('.').map(Number);
|
||||||
|
const latestParts = latest.split('.').map(Number);
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const c = currentParts[i] || 0;
|
||||||
|
const l = latestParts[i] || 0;
|
||||||
|
if (l > c) return true;
|
||||||
|
if (l < c) return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @zh 更新项目中的 ESEngine 模块
|
||||||
|
* @en Update ESEngine modules in project
|
||||||
|
*/
|
||||||
|
async function updateCommand(moduleIds: string[], options: { yes?: boolean; check?: boolean }): Promise<void> {
|
||||||
|
printLogo();
|
||||||
|
|
||||||
|
const cwd = process.cwd();
|
||||||
|
const packageJsonPath = path.join(cwd, 'package.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(packageJsonPath)) {
|
||||||
|
console.log(chalk.red(' ✗ No package.json found.'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||||
|
const deps = pkg.dependencies || {};
|
||||||
|
|
||||||
|
// Find installed @esengine packages
|
||||||
|
const esenginePackages: { name: string; current: string; latest: string | null }[] = [];
|
||||||
|
|
||||||
|
console.log(chalk.gray(' Checking for updates...\n'));
|
||||||
|
|
||||||
|
for (const [name, version] of Object.entries(deps)) {
|
||||||
|
if (name.startsWith('@esengine/')) {
|
||||||
|
// If specific modules provided, filter
|
||||||
|
if (moduleIds.length > 0) {
|
||||||
|
const mod = getModuleById(moduleIds.find(id => {
|
||||||
|
const m = getModuleById(id);
|
||||||
|
return m?.package === name;
|
||||||
|
}) || '');
|
||||||
|
if (!mod || mod.package !== name) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latest = getLatestVersion(name);
|
||||||
|
esenginePackages.push({
|
||||||
|
name,
|
||||||
|
current: version as string,
|
||||||
|
latest
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esenginePackages.length === 0) {
|
||||||
|
console.log(chalk.yellow(' No ESEngine packages found in dependencies.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display update status
|
||||||
|
const updatable: { name: string; current: string; latest: string }[] = [];
|
||||||
|
|
||||||
|
console.log(chalk.bold(' Package Status:\n'));
|
||||||
|
for (const pkg of esenginePackages) {
|
||||||
|
const currentClean = pkg.current.replace(/^\^|~/, '');
|
||||||
|
const isLatestTag = currentClean === 'latest' || currentClean === '*';
|
||||||
|
|
||||||
|
if (pkg.latest === null) {
|
||||||
|
console.log(` ${chalk.gray(pkg.name)}`);
|
||||||
|
console.log(` ${chalk.red('✗')} Unable to fetch latest version`);
|
||||||
|
} else if (isNewerVersion(pkg.current, pkg.latest)) {
|
||||||
|
console.log(` ${chalk.cyan(pkg.name)}`);
|
||||||
|
if (isLatestTag) {
|
||||||
|
console.log(` ${chalk.yellow(currentClean)} → ${chalk.green(`^${pkg.latest}`)} ${chalk.gray('(pin version)')}`);
|
||||||
|
} else {
|
||||||
|
console.log(` ${chalk.yellow(currentClean)} → ${chalk.green(pkg.latest)}`);
|
||||||
|
}
|
||||||
|
updatable.push({ name: pkg.name, current: pkg.current, latest: pkg.latest });
|
||||||
|
} else {
|
||||||
|
console.log(` ${chalk.gray(pkg.name)}`);
|
||||||
|
console.log(` ${chalk.green('✓')} ${currentClean} (up to date)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatable.length === 0) {
|
||||||
|
console.log(chalk.bold('\n All packages are up to date!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check-only mode
|
||||||
|
if (options.check) {
|
||||||
|
console.log(chalk.bold(`\n ${updatable.length} package(s) can be updated.`));
|
||||||
|
console.log(chalk.gray(' Run `esengine update` to update.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm update
|
||||||
|
if (!options.yes) {
|
||||||
|
console.log();
|
||||||
|
const confirm = await prompts({
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'proceed',
|
||||||
|
message: `Update ${updatable.length} package(s)?`,
|
||||||
|
initial: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirm.proceed) {
|
||||||
|
console.log(chalk.yellow('\n Cancelled.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update package.json (re-read to minimize race condition)
|
||||||
|
console.log(chalk.bold('\n Updating packages...\n'));
|
||||||
|
|
||||||
|
const updates: Record<string, string> = {};
|
||||||
|
for (const upd of updatable) {
|
||||||
|
const cleanCurrent = upd.current.replace(/^\^|~/, '');
|
||||||
|
const isLatestTag = cleanCurrent === 'latest' || cleanCurrent === '*';
|
||||||
|
const prefix = isLatestTag ? '^' : (upd.current.startsWith('^') ? '^' : upd.current.startsWith('~') ? '~' : '');
|
||||||
|
updates[upd.name] = `${prefix}${upd.latest}`;
|
||||||
|
console.log(` ${chalk.green('↑')} ${upd.name} → ${prefix}${upd.latest}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomic update: write to temp file then rename
|
||||||
|
const tempPath = `${packageJsonPath}.tmp`;
|
||||||
|
const freshPkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||||
|
freshPkg.dependencies = { ...freshPkg.dependencies, ...updates };
|
||||||
|
fs.writeFileSync(tempPath, JSON.stringify(freshPkg, null, 2), 'utf-8');
|
||||||
|
fs.renameSync(tempPath, packageJsonPath);
|
||||||
|
|
||||||
|
// Run install
|
||||||
|
const pm = detectPackageManager(cwd);
|
||||||
|
const installCmd = pm === 'pnpm' ? 'pnpm install' : pm === 'yarn' ? 'yarn' : 'npm install';
|
||||||
|
|
||||||
|
console.log(chalk.gray(`\n Running ${installCmd}...`));
|
||||||
|
|
||||||
|
try {
|
||||||
|
execSync(installCmd, { cwd, stdio: 'inherit' });
|
||||||
|
console.log(chalk.bold('\n Done! All packages updated.'));
|
||||||
|
} catch {
|
||||||
|
console.log(chalk.yellow(`\n ⚠ Failed to run install. package.json has been updated.`));
|
||||||
|
console.log(chalk.gray(` Run \`${installCmd}\` manually.`));
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// CLI Setup
|
// CLI Setup
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -623,6 +799,14 @@ program
|
|||||||
.option('-y, --yes', 'Skip confirmation')
|
.option('-y, --yes', 'Skip confirmation')
|
||||||
.action(removeCommand);
|
.action(removeCommand);
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('update [modules...]')
|
||||||
|
.alias('up')
|
||||||
|
.description('Update ESEngine packages to latest versions')
|
||||||
|
.option('-y, --yes', 'Skip confirmation')
|
||||||
|
.option('-c, --check', 'Only check for updates, do not install')
|
||||||
|
.action(updateCommand);
|
||||||
|
|
||||||
// Default command: show help
|
// Default command: show help
|
||||||
program
|
program
|
||||||
.action(() => {
|
.action(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user