From 71f614d54f2f554c7122e7f3028c01399e8df6c5 Mon Sep 17 00:00:00 2001 From: lete114 Date: Mon, 8 Jun 2026 22:28:40 +0800 Subject: [PATCH] refactor: move config from mcbe.config.json to package.json mcbe field --- README.md | 5 +- src/commands/build.ts | 7 +- src/commands/dev.ts | 9 +- src/commands/info.ts | 7 +- src/commands/init.ts | 141 ++++---- src/commands/manifest.ts | 27 +- src/commands/sync.ts | 5 - .../__tests__/manifest.integration.test.ts | 315 ++++++++++++++++++ src/core/__tests__/manifest.test.ts | 41 ++- .../__tests__/project.integration.test.ts | 268 +++++++++++++++ src/core/manifest.ts | 98 ++++-- src/core/project.ts | 98 ++++-- src/types.ts | 18 +- src/utils/mcpath.ts | 54 +-- 14 files changed, 863 insertions(+), 230 deletions(-) create mode 100644 src/core/__tests__/manifest.integration.test.ts create mode 100644 src/core/__tests__/project.integration.test.ts diff --git a/README.md b/README.md index 6830490..7447e14 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Produces a `.mcaddon` file in the `pack/` directory. ```bash mcbe-create sync # Manual one-shot sync dist/ to Minecraft -mcbe-create manifest # Regenerate manifest.json from config +mcbe-create manifest # Patch manifest.json from project config mcbe-create info # Show project information ``` @@ -98,8 +98,7 @@ my-addon/ │ ├── behavior_pack/ │ └── resource_pack/ ├── pack/ # .mcaddon output -├── mcbe.config.json # Project configuration -├── package.json +├── package.json # Contains "mcbe" field for configuration └── tsconfig.json # Only for TypeScript projects ``` diff --git a/src/commands/build.ts b/src/commands/build.ts index ac434e3..b7a0d09 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -3,6 +3,7 @@ import { join } from 'node:path' import process from 'node:process' import pc from 'picocolors' import { resolveEntry, runBuild } from '../core/builder.js' +import { patchManifest } from '../core/manifest.js' import { createMcAddon } from '../core/packager.js' import { getProjectConfig, getProjectDir } from '../core/project.js' import { spinnerFail, spinnerStart, spinnerSucceed } from '../utils/logger.js' @@ -11,11 +12,7 @@ const TS_FILTER = (src: string) => !src.endsWith('.ts') export async function buildCommand(options: { package?: boolean }) { const projectDir = getProjectDir() - if (!projectDir) { - console.error(pc.red(' ✗ No mcbe.config.json found')) - process.exit(1) - } - + patchManifest(projectDir) const config = getProjectConfig(projectDir) const distDir = join(projectDir, 'dist') const bpOut = join(distDir, 'behavior_pack') diff --git a/src/commands/dev.ts b/src/commands/dev.ts index 829cd7e..e28c5f8 100644 --- a/src/commands/dev.ts +++ b/src/commands/dev.ts @@ -3,6 +3,7 @@ import { join } from 'node:path' import process from 'node:process' import pc from 'picocolors' import { resolveEntry, runWatchBuild } from '../core/builder.js' +import { patchManifest } from '../core/manifest.js' import { getProjectConfig, getProjectDir } from '../core/project.js' import { startSync } from '../core/syncer.js' import { spinnerFail, spinnerStart, spinnerSucceed } from '../utils/logger.js' @@ -23,11 +24,7 @@ function syncDir(src: string, dest: string, label: string) { export async function devCommand(options: DevOptions = {}) { const projectDir = getProjectDir() - if (!projectDir) { - console.error(pc.red(' ✗ No mcbe.config.json found')) - process.exit(1) - } - + patchManifest(projectDir) const config = getProjectConfig(projectDir) const distDir = join(projectDir, 'dist') @@ -82,7 +79,7 @@ export async function devCommand(options: DevOptions = {}) { if (options.sync) { const mcPaths = getMcPaths(projectDir) if (!mcPaths) { - console.error(pc.red(' ✗ Minecraft path not found. Set MC_DEV_PATH')) + console.error(pc.red(' ✗ Minecraft path not found. Set MCBE_DEV_PATH')) process.exit(1) } diff --git a/src/commands/info.ts b/src/commands/info.ts index 33c8046..bb2dfa4 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -6,11 +6,6 @@ import { getMcPaths, getMcProjectDir } from '../utils/mcpath.js' export async function infoCommand() { const projectDir = getProjectDir() - if (!projectDir) { - console.log(pc.red('Not in a project directory')) - process.exit(1) - } - const config = getProjectConfig(projectDir) const mcPaths = getMcPaths(projectDir) const distDir = join(projectDir, 'dist') @@ -36,7 +31,7 @@ export async function infoCommand() { console.log(` ${pc.bold('Version:')} ${config.version}`) if (config.author) { console.log(` ${pc.bold('Author:')} ${config.author}`) } if (config.description) { console.log(` ${pc.bold('Desc:')} ${config.description}`) } - console.log(` ${pc.bold('Template:')} ${config.template}`) + console.log(` ${pc.bold('License:')} ${config.license || '-'}`) console.log(` ${pc.bold('Scripts:')} ${config.hasScripts ? '✓' : '✗'} ${config.language || '-'}`) console.log(` ${pc.bold('Resource:')} ${hasRp ? '✓' : '✗'}`) console.log(` ${pc.bold('Minecraft:')} ${mcConnected ? '✓' : '✗'} ${mcInfo}`) diff --git a/src/commands/init.ts b/src/commands/init.ts index 8f74aa4..db0b583 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -1,13 +1,14 @@ -import type { PackageManager, PackType, QuickStartConfig, ScriptLanguage, TemplateName } from '../types.js' +import type { McbeConfig, PackageManager, PackType, ScriptLanguage, TemplateName } from '../types.js' import { execSync } from 'node:child_process' import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, join } from 'node:path' import process from 'node:process' import { fileURLToPath } from 'node:url' import * as p from '@clack/prompts' +import { isCancel } from '@clack/prompts' import pc from 'picocolors' import { writeManifest } from '../core/manifest.js' -import { createProjectConfig, getProjectConfig, getProjectDir, updateProjectConfig } from '../core/project.js' +import { getProjectConfig, getProjectDir, writeMcbeConfig } from '../core/project.js' import { generateUUIDs } from '../utils/uuid.js' import { validateProjectName } from '../utils/validation.js' @@ -41,6 +42,11 @@ function detectPackageManager(): PackageManager { } } +function onCancel() { + p.cancel('Cancelled') + process.exit(0) +} + function copyTemplateScript(templateDir: string, destDir: string, language: ScriptLanguage) { const srcPath = join(templateDir, 'src', 'main.ts') if (!existsSync(srcPath)) { return } @@ -81,12 +87,14 @@ async function createNewProject(name: string, options: InitOptions) { { value: 'resource_pack', label: 'Resource Pack', hint: 'Textures, sounds, models' }, ], }) as PackType[] + if (isCancel(packs)) { onCancel() } if (packs.includes('behavior_pack')) { hasScripts = await p.confirm({ message: 'Use Minecraft Script API?', initialValue: true, }) as boolean + if (isCancel(hasScripts)) { onCancel() } if (hasScripts) { language = await p.select({ @@ -96,6 +104,7 @@ async function createNewProject(name: string, options: InitOptions) { { value: 'javascript', label: 'JavaScript', hint: 'No compilation needed' }, ], }) as ScriptLanguage + if (isCancel(language)) { onCancel() } template = await p.select({ message: 'Select a template:', @@ -105,12 +114,14 @@ async function createNewProject(name: string, options: InitOptions) { { value: 'explosive-bow', label: 'Explosive Bow', hint: 'Bow that spawns exploding crystals' }, ], }) as TemplateName + if (isCancel(template)) { onCancel() } mcVersion = await p.text({ message: '@minecraft/server version:', placeholder: mcVersion, initialValue: mcVersion, }) as string + if (isCancel(mcVersion)) { onCancel() } } } @@ -123,16 +134,19 @@ async function createNewProject(name: string, options: InitOptions) { { value: 'yarn', label: 'yarn' }, ], }) as PackageManager + if (isCancel(pm)) { onCancel() } author = await p.text({ message: 'Author:', placeholder: 'Your name', }) as string + if (isCancel(author)) { onCancel() } description = await p.text({ message: 'Description:', placeholder: 'My Minecraft BE addon', }) as string + if (isCancel(description)) { onCancel() } p.outro('Creating project...') } @@ -142,28 +156,46 @@ async function createNewProject(name: string, options: InitOptions) { template = template || 'default' - const uuids = generateUUIDs() - if (!packs.includes('resource_pack')) { - uuids.resourcePack = undefined + const newUuids = generateUUIDs() + const mcbeConfig: McbeConfig = { + uuids: { + behaviorPack: newUuids.behaviorPack, + resourcePack: packs.includes('resource_pack') ? newUuids.resourcePack : undefined, + module: (packs.includes('behavior_pack') && hasScripts) ? newUuids.module : undefined, + }, + minEngineVersion: [1, 21, 0], } - const config: QuickStartConfig = { + const pkg = { name, + type: 'module', version: '0.1.0', + private: true, + license: 'MIT', author: author || undefined, description: description || undefined, - template, - uuids, - hasScripts: packs.includes('behavior_pack') ? hasScripts : undefined, - language: packs.includes('behavior_pack') && hasScripts ? language : undefined, - minecraftServerVersion: hasScripts ? mcVersion : undefined, - minEngineVersion: [1, 21, 0], + scripts: { + 'dev': 'mcbe-create dev', + 'dev:sync': 'mcbe-create dev --sync', + 'build': 'mcbe-create build', + }, + devDependencies: { + '@mcbe-mods/create': `^${CLI_VERSION}`, + }, + dependencies: (packs.includes('behavior_pack') && hasScripts) ? { '@minecraft/server': mcVersion } : {}, + mcbe: mcbeConfig, } + mkdirSync(projectDir, { recursive: true }) + writeFileSync(join(projectDir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`) + + writeFileSync(join(projectDir, '.gitignore'), ['node_modules', 'dist', 'pack', '*.local'].join('\n')) + + const config = getProjectConfig(projectDir) if (packs.includes('behavior_pack')) { const bpDir = join(projectDir, 'src', 'behavior_pack') mkdirSync(bpDir, { recursive: true }) - writeManifest(bpDir, 'behavior_pack', config) + writeManifest(bpDir, 'behavior_pack', config, mcVersion) writeLang(bpDir, name, description) if (hasScripts) { @@ -197,31 +229,9 @@ async function createNewProject(name: string, options: InitOptions) { writeLang(rpDir, name, description) } - const pkg = { - name, - type: 'module', - version: '0.1.0', - private: true, - license: 'MIT', - scripts: { - dev: 'mcbe-create dev', - devsync: 'mcbe-create dev --sync', - build: 'mcbe-create build', - }, - devDependencies: { - '@mcbe-mods/create': `^${CLI_VERSION}`, - }, - dependencies: hasScripts ? { '@minecraft/server': mcVersion } : {}, - } - writeFileSync(join(projectDir, 'package.json'), JSON.stringify(pkg, null, 2)) - - writeFileSync(join(projectDir, '.gitignore'), ['node_modules', 'dist', 'pack', '*.local'].join('\n')) - - createProjectConfig(projectDir, config) - if (options.install !== false) { console.log(` Installing dependencies with ${pc.cyan(pm)}...`) - const installCmd = pm === 'npm' ? 'npm install' : `${pm} install` + const installCmd = `${pm} install` try { execSync(installCmd, { cwd: projectDir, stdio: 'inherit' }) } @@ -233,11 +243,13 @@ async function createNewProject(name: string, options: InitOptions) { console.log() console.log(pc.green(` ✓ Project "${name}" created!`)) console.log() + + const runCmd = pm === 'npm' ? 'npm run' : pm console.log(` ${pc.dim('Next steps:')}`) console.log(` ${pc.cyan(`cd ${name}`)}`) - console.log(` ${pc.cyan('mcbe-create dev')} ${pc.dim('Start development mode')}`) - console.log(` ${pc.cyan('mcbe-create dev --sync')} ${pc.dim('Start dev with auto MC sync')}`) - console.log(` ${pc.cyan('mcbe-create build')} ${pc.dim('Build and package')}`) + console.log(` ${pc.cyan(`${runCmd} dev`)} ${pc.dim('Start development mode')}`) + console.log(` ${pc.cyan(`${runCmd} dev:sync`)} ${pc.dim('Start dev with auto MC sync')}`) + console.log(` ${pc.cyan(`${runCmd} build`)} ${pc.dim('Build and package')}`) console.log() } @@ -256,19 +268,26 @@ async function reinitProject(projectDir: string) { console.log() const additions: string[] = [] + const uuids = config.uuids if (!hasBp) { const addBp = await p.confirm({ message: 'Add Behavior Pack?', initialValue: true, }) + if (isCancel(addBp)) { onCancel() } if (addBp) { additions.push('behavior_pack') + const newUuids = generateUUIDs() + writeMcbeConfig(projectDir, { + uuids: { behaviorPack: newUuids.behaviorPack, module: newUuids.module }, + }) const addScripts = await p.confirm({ message: 'Use Minecraft Script API?', initialValue: true, }) + if (isCancel(addScripts)) { onCancel() } if (addScripts) { const lang = await p.select({ message: 'Script language:', @@ -277,20 +296,14 @@ async function reinitProject(projectDir: string) { { value: 'javascript', label: 'JavaScript' }, ], }) as ScriptLanguage - - const uuids = generateUUIDs() - const updated = updateProjectConfig(projectDir, { - uuids: { ...config.uuids, behaviorPack: uuids.behaviorPack, module: uuids.module }, - hasScripts: true, - language: lang, - }) + if (isCancel(lang)) { onCancel() } const bpDir = join(projectDir, 'src', 'behavior_pack') mkdirSync(bpDir, { recursive: true }) - writeManifest(bpDir, 'behavior_pack', updated) + writeManifest(bpDir, 'behavior_pack', getProjectConfig(projectDir)) writeLang(bpDir, config.name, config.description || '') - const templateDir = join(TEMPLATES_DIR, config.template) + const templateDir = join(TEMPLATES_DIR, 'default') if (existsSync(templateDir)) { copyTemplateScript(templateDir, bpDir, lang) } @@ -311,28 +324,20 @@ async function reinitProject(projectDir: string) { writeFileSync(join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2)) } } - else { - updateProjectConfig(projectDir, { - hasScripts: false, - language: undefined, - }) - } } } - if (!hasRp && config.uuids.resourcePack) { + if (!hasRp) { const addRp = await p.confirm({ message: 'Add Resource Pack?', initialValue: true, }) + if (isCancel(addRp)) { onCancel() } if (addRp) { additions.push('resource_pack') - - if (!hasBp) { - const uuids = generateUUIDs() - updateProjectConfig(projectDir, { - uuids: { ...config.uuids, resourcePack: uuids.resourcePack }, - }) + if (!uuids.resourcePack) { + const newUuids = generateUUIDs() + writeMcbeConfig(projectDir, { uuids: { resourcePack: newUuids.resourcePack } }) } const rpDir = join(projectDir, 'src', 'resource_pack') @@ -351,11 +356,16 @@ async function reinitProject(projectDir: string) { } export async function initCommand(name: string | undefined, options: InitOptions) { - const existingProjectDir = getProjectDir() + try { + const existingProjectDir = getProjectDir() - if (existingProjectDir) { - await reinitProject(existingProjectDir) - return + if (existingProjectDir) { + await reinitProject(existingProjectDir) + return + } + } + catch { + // Not in an existing project, proceed with creating new one } if (!name) { @@ -364,6 +374,7 @@ export async function initCommand(name: string | undefined, options: InitOptions placeholder: 'my-addon', validate: input => validateProjectName(input) || undefined, }) as string + if (isCancel(name)) { onCancel() } } const nameError = validateProjectName(name) diff --git a/src/commands/manifest.ts b/src/commands/manifest.ts index 30ab196..bdca281 100644 --- a/src/commands/manifest.ts +++ b/src/commands/manifest.ts @@ -1,26 +1,17 @@ +import { existsSync } from 'node:fs' import { join } from 'node:path' import pc from 'picocolors' -import { writeManifest } from '../core/manifest.js' -import { getProjectConfig, getProjectDir } from '../core/project.js' +import { patchManifest } from '../core/manifest.js' +import { getProjectDir } from '../core/project.js' export async function manifestCommand() { const projectDir = getProjectDir() - if (!projectDir) { - console.error(pc.red(' ✗ No mcbe.config.json found')) - process.exit(1) - } - - const config = getProjectConfig(projectDir) - const bpDir = join(projectDir, 'src', 'behavior_pack') - const rpDir = config.uuids.resourcePack - ? join(projectDir, 'src', 'resource_pack') - : undefined - - writeManifest(bpDir, 'behavior_pack', config) - console.log(pc.green(` ✓ Manifest generated: ${join(bpDir, 'manifest.json')}`)) + patchManifest(projectDir) - if (rpDir) { - writeManifest(rpDir, 'resource_pack', config) - console.log(pc.green(` ✓ Manifest generated: ${join(rpDir, 'manifest.json')}`)) + for (const packType of ['behavior_pack', 'resource_pack'] as const) { + const manifestPath = join(projectDir, 'src', packType, 'manifest.json') + if (existsSync(manifestPath)) { + console.log(pc.green(` ✓ Manifest patched: ${manifestPath}`)) + } } } diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 7b6ab36..8fb5fcc 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -6,11 +6,6 @@ import { getMcPaths, getMcProjectDir } from '../utils/mcpath.js' export async function syncCommand() { const projectDir = getProjectDir() - if (!projectDir) { - console.error(pc.red(' ✗ No mcbe.config.json found')) - process.exit(1) - } - const config = getProjectConfig(projectDir) const mcPaths = getMcPaths(projectDir) if (!mcPaths) { diff --git a/src/core/__tests__/manifest.integration.test.ts b/src/core/__tests__/manifest.integration.test.ts new file mode 100644 index 0000000..c4dd620 --- /dev/null +++ b/src/core/__tests__/manifest.integration.test.ts @@ -0,0 +1,315 @@ +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { patchManifest } from '../manifest.js' + +function createProject(): string { + const dir = mkdtempSync(join(tmpdir(), 'mcbe-test-')) + const pkg = { + name: 'test-addon', + version: '1.0.0', + author: 'Test Author', + license: 'MIT', + homepage: 'https://example.com', + dependencies: { + '@minecraft/server': '^1.18.0', + '@minecraft/server-ui': '~1.3.0', + }, + mcbe: { + uuids: { + behaviorPack: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + resourcePack: 'bbbbbbbb-cccc-dddd-eeee-ffffffffffff', + module: 'cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa', + }, + minEngineVersion: [1, 21, 80], + }, + } + writeFileSync(join(dir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`) + return dir +} + +function addManifest(dir: string, manifest: any) { + const bpDir = join(dir, 'src', 'behavior_pack') + mkdirSync(bpDir, { recursive: true }) + writeFileSync(join(bpDir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`) +} + +function readManifest(dir: string): any { + return JSON.parse(readFileSync(join(dir, 'src', 'behavior_pack', 'manifest.json'), 'utf-8')) +} + +describe('patchManifest', () => { + it('should update header uuid, version and min_engine_version', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'old-uuid', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [], + }) + + patchManifest(dir) + const result = readManifest(dir) + const header = result.header + + expect(header.uuid).toBe('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee') + expect(header.version).toEqual([1, 0, 0]) + expect(header.min_engine_version).toEqual([1, 21, 80]) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should update script module uuid and version', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'old-uuid', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [ + { + type: 'script', + language: 'javascript', + uuid: 'old-module-uuid', + entry: 'scripts/main.js', + version: [0, 0, 1], + }, + ], + dependencies: [], + }) + + patchManifest(dir) + const result = readManifest(dir) + const modules = result.modules + + expect(modules[0].uuid).toBe('cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa') + expect(modules[0].version).toEqual([1, 0, 0]) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should update @minecraft/* dependency versions from package.json', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [ + { module_name: '@minecraft/server', version: '0.0.0' }, + { module_name: '@minecraft/server-ui', version: '0.0.0' }, + ], + }) + + patchManifest(dir) + const result = readManifest(dir) + const deps = result.dependencies as Array> + + expect(deps).toContainEqual({ module_name: '@minecraft/server', version: '1.18.0' }) + expect(deps).toContainEqual({ module_name: '@minecraft/server-ui', version: '1.3.0' }) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should update metadata from package.json', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [], + }) + + patchManifest(dir) + const result = readManifest(dir) + + expect(result.metadata).toEqual({ + authors: ['Test Author'], + license: 'MIT', + url: 'https://example.com', + }) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should preserve user-added fields (product_type, extra modules, extra dependencies)', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'old-uuid', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [ + { + type: 'script', + language: 'javascript', + uuid: 'old-module-uuid', + entry: 'scripts/main.js', + version: [0, 0, 1], + }, + { + type: 'custom-type', + uuid: 'custom-module-uuid', + version: [9, 9, 9], + }, + ], + dependencies: [ + { module_name: '@minecraft/server', version: '0.0.0' }, + { uuid: 'custom-dep-uuid', version: [9, 9, 9] }, + ], + product_type: 'addon', + customRootField: 'should survive', + }) + + patchManifest(dir) + const result = readManifest(dir) + + expect(result.product_type).toBe('addon') + expect(result.customRootField).toBe('should survive') + + const modules = result.modules + const extraModule = modules.find((m: any) => m.type === 'custom-type') + expect(extraModule).toBeDefined() + expect(extraModule!.uuid).toBe('custom-module-uuid') + + const deps = result.dependencies + expect(deps).toContainEqual({ uuid: 'custom-dep-uuid', version: [9, 9, 9] }) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should not crash when no manifest exists', () => { + const dir = createProject() + try { + expect(() => patchManifest(dir)).not.toThrow() + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should add metadata if manifest has none', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [], + }) + + patchManifest(dir) + const result = readManifest(dir) + + expect(result.metadata).toBeDefined() + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should update resource pack uuid when resourcePack exists', () => { + const dir = createProject() + try { + const rpDir = join(dir, 'src', 'resource_pack') + mkdirSync(rpDir, { recursive: true }) + writeFileSync(join(rpDir, 'manifest.json'), `${JSON.stringify({ + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'old-rp-uuid', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [], + }, null, 2)}\n`) + + patchManifest(dir) + const rpManifest = JSON.parse(readFileSync(join(rpDir, 'manifest.json'), 'utf-8')) + expect(rpManifest.header.uuid).toBe('bbbbbbbb-cccc-dddd-eeee-ffffffffffff') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should update cross-pack uuid dependency version', () => { + const dir = createProject() + try { + addManifest(dir, { + format_version: 2, + header: { + description: 'pack.description', + name: 'pack.name', + uuid: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + version: [0, 0, 1], + min_engine_version: [1, 20, 0], + }, + modules: [], + dependencies: [ + { uuid: 'bbbbbbbb-cccc-dddd-eeee-ffffffffffff', version: [9, 9, 9] }, + ], + }) + + patchManifest(dir) + const result = readManifest(dir) + + expect(result.dependencies).toContainEqual({ + uuid: 'bbbbbbbb-cccc-dddd-eeee-ffffffffffff', + version: [1, 0, 0], + }) + } + finally { + rmSync(dir, { recursive: true }) + } + }) +}) diff --git a/src/core/__tests__/manifest.test.ts b/src/core/__tests__/manifest.test.ts index 084efee..c363f1c 100644 --- a/src/core/__tests__/manifest.test.ts +++ b/src/core/__tests__/manifest.test.ts @@ -5,12 +5,12 @@ import { generateBpManifest, generateRpManifest } from '../manifest.js' const baseConfig: QuickStartConfig = { name: 'test-pack', version: '1.0.0', - template: 'default', uuids: { behaviorPack: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', resourcePack: 'ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj', module: 'kkkkkkkk-llll-mmmm-nnnn-oooooooooooo', }, + hasScripts: true, minEngineVersion: [1, 21, 0], } @@ -26,8 +26,11 @@ describe('generateBpManifest', () => { version: [1, 0, 0], min_engine_version: [1, 21, 0], }, - modules: [], dependencies: [ + { + module_name: '@minecraft/server', + version: '1.18.0', + }, { uuid: baseConfig.uuids.resourcePack, version: [1, 0, 0], @@ -36,29 +39,32 @@ describe('generateBpManifest', () => { }) }) - it('should include script module when hasScripts is true', () => { - const config = { ...baseConfig, hasScripts: true } - const manifest = generateBpManifest(config) as Record + it('should include script module when module UUID exists', () => { + const manifest = generateBpManifest(baseConfig) as Record expect(manifest.modules).toEqual([ { type: 'script', language: 'javascript', - uuid: config.uuids.module, + uuid: baseConfig.uuids.module, entry: 'scripts/main.js', version: [1, 0, 0], }, ]) - expect(manifest.dependencies).toContainEqual({ - module_name: '@minecraft/server', - version: '1.18.0', - }) }) - it('should include resource pack dependency when resourcePack UUID exists', () => { - const config = { ...baseConfig, uuids: baseConfig.uuids } + it('should skip script module when module UUID is missing', () => { + const config = { + ...baseConfig, + uuids: { behaviorPack: 'aaa', resourcePack: 'bbb' }, + } const manifest = generateBpManifest(config) as Record + expect(manifest.modules).toEqual([]) + }) + + it('should include resource pack dependency when resourcePack UUID exists', () => { + const manifest = generateBpManifest(baseConfig) as Record expect(manifest.dependencies).toContainEqual({ - uuid: config.uuids.resourcePack, + uuid: baseConfig.uuids.resourcePack, version: [1, 0, 0], }) }) @@ -73,6 +79,15 @@ describe('generateBpManifest', () => { }) }) + it('should include homepage in metadata when provided', () => { + const config = { ...baseConfig, homepage: 'https://example.com' } + const manifest = generateBpManifest(config) as Record + expect(manifest.metadata).toEqual({ + license: 'MIT', + url: 'https://example.com', + }) + }) + it('should include license and url in metadata when no author', () => { const manifest = generateBpManifest(baseConfig) as Record expect(manifest.metadata).toEqual({ diff --git a/src/core/__tests__/project.integration.test.ts b/src/core/__tests__/project.integration.test.ts new file mode 100644 index 0000000..eee6ba6 --- /dev/null +++ b/src/core/__tests__/project.integration.test.ts @@ -0,0 +1,268 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { getMcDependencyVersion, getProjectConfig, writeMcbeConfig } from '../project.js' + +function createProject(fields?: Record): string { + const dir = mkdtempSync(join(tmpdir(), 'mcbe-test-')) + const pkg: Record = { + name: 'test-addon', + version: '1.2.3', + author: 'Test Author', + license: 'MIT', + homepage: 'https://example.com', + mcbe: { + uuids: { + behaviorPack: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + module: 'ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj', + }, + minEngineVersion: [1, 21, 80], + }, + ...fields, + } + writeFileSync(join(dir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`) + return dir +} + +function addBpManifest(dir: string, hasScriptModule: boolean, extraModules: unknown[] = []) { + const bpDir = join(dir, 'src', 'behavior_pack') + mkdirSync(bpDir, { recursive: true }) + const modules = extraModules.slice() + if (hasScriptModule) { + modules.unshift({ + type: 'script', + language: 'javascript', + uuid: 'script-uuid-0000-0000-000000000000', + entry: 'scripts/main.js', + version: [1, 0, 0], + }) + } + writeFileSync(join(bpDir, 'manifest.json'), `${JSON.stringify({ format_version: 2, modules }, null, 2)}\n`) +} + +describe('getProjectConfig', () => { + it('should read full config from a valid project', () => { + const dir = createProject() + try { + const config = getProjectConfig(dir) + expect(config.name).toBe('test-addon') + expect(config.version).toBe('1.2.3') + expect(config.author).toBe('Test Author') + expect(config.license).toBe('MIT') + expect(config.homepage).toBe('https://example.com') + expect(config.uuids.behaviorPack).toBe('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee') + expect(config.uuids.module).toBe('ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj') + expect(config.minEngineVersion).toEqual([1, 21, 80]) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should handle missing optional fields', () => { + const dir = createProject({ author: undefined, license: undefined, homepage: undefined }) + try { + const config = getProjectConfig(dir) + expect(config.name).toBe('test-addon') + expect(config.author).toBeUndefined() + expect(config.license).toBeUndefined() + expect(config.homepage).toBeUndefined() + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should throw when mcbe field is missing', () => { + const dir = createProject({ mcbe: undefined }) + try { + expect(() => getProjectConfig(dir)).toThrow('"mcbe" field') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should detect hasScripts=true when manifest has script module', () => { + const dir = createProject() + try { + addBpManifest(dir, true) + const config = getProjectConfig(dir) + expect(config.hasScripts).toBe(true) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should detect hasScripts=false when manifest has no script module', () => { + const dir = createProject() + try { + addBpManifest(dir, false) + const config = getProjectConfig(dir) + expect(config.hasScripts).toBe(false) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should detect hasScripts=false when no manifest exists', () => { + const dir = createProject() + try { + const config = getProjectConfig(dir) + expect(config.hasScripts).toBe(false) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should infer typescript language when tsconfig.json exists', () => { + const dir = createProject() + try { + addBpManifest(dir, true) + writeFileSync(join(dir, 'tsconfig.json'), '{}') + const config = getProjectConfig(dir) + expect(config.language).toBe('typescript') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should infer javascript language when no tsconfig.json exists', () => { + const dir = createProject() + try { + addBpManifest(dir, true) + const config = getProjectConfig(dir) + expect(config.language).toBe('javascript') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should return language=undefined when no script module', () => { + const dir = createProject() + try { + const config = getProjectConfig(dir) + expect(config.language).toBeUndefined() + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should handle empty uuids', () => { + const dir = createProject({ mcbe: { uuids: {}, minEngineVersion: [1, 21, 0] } }) + try { + const config = getProjectConfig(dir) + expect(config.uuids).toEqual({}) + } + finally { + rmSync(dir, { recursive: true }) + } + }) +}) + +describe('getMcDependencyVersion', () => { + it('should read version from dependencies', () => { + const dir = createProject({ dependencies: { '@minecraft/server': '^1.18.0' } }) + try { + const version = getMcDependencyVersion(dir, '@minecraft/server') + expect(version).toBe('1.18.0') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should read version from devDependencies', () => { + const dir = createProject({ devDependencies: { '@minecraft/server': '~1.15.0' } }) + try { + const version = getMcDependencyVersion(dir, '@minecraft/server') + expect(version).toBe('1.15.0') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should prefer dependencies over devDependencies', () => { + const dir = createProject({ + dependencies: { '@minecraft/server': '1.18.0' }, + devDependencies: { '@minecraft/server': '1.15.0' }, + }) + try { + const version = getMcDependencyVersion(dir, '@minecraft/server') + expect(version).toBe('1.18.0') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should strip ^ ~ >= < range prefixes', () => { + const dir = createProject({ dependencies: { '@minecraft/server-ui': '>=1.3.0' } }) + try { + const version = getMcDependencyVersion(dir, '@minecraft/server-ui') + expect(version).toBe('1.3.0') + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should return undefined for non-existent package', () => { + const dir = createProject() + try { + const version = getMcDependencyVersion(dir, '@minecraft/nonexistent') + expect(version).toBeUndefined() + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should handle semver with spaces after prefix', () => { + const dir = createProject({ dependencies: { '@minecraft/server': '^ 1.18.0' } }) + try { + const version = getMcDependencyVersion(dir, '@minecraft/server') + expect(version).toBe('1.18.0') + } + finally { + rmSync(dir, { recursive: true }) + } + }) +}) + +describe('writeMcbeConfig', () => { + it('should update mcbe field in package.json', () => { + const dir = createProject() + try { + writeMcbeConfig(dir, { minEngineVersion: [1, 21, 0] }) + const config = getProjectConfig(dir) + expect(config.minEngineVersion).toEqual([1, 21, 0]) + } + finally { + rmSync(dir, { recursive: true }) + } + }) + + it('should merge partial uuid updates', () => { + const dir = createProject() + try { + writeMcbeConfig(dir, { + uuids: { resourcePack: 'new-resource-uuid-000000000000' }, + }) + const config = getProjectConfig(dir) + expect(config.uuids.resourcePack).toBe('new-resource-uuid-000000000000') + expect(config.uuids.behaviorPack).toBe('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee') + } + finally { + rmSync(dir, { recursive: true }) + } + }) +}) diff --git a/src/core/manifest.ts b/src/core/manifest.ts index 8d14aa5..2ae2921 100644 --- a/src/core/manifest.ts +++ b/src/core/manifest.ts @@ -1,10 +1,11 @@ import type { QuickStartConfig } from '../types.js' -import { mkdirSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, join } from 'node:path' -import { parseVersion } from './project.js' +import { getMcDependencyVersion, getProjectConfig, parseVersion } from './project.js' -export function generateBpManifest(config: QuickStartConfig): object { - const scriptModule = config.hasScripts +export function generateBpManifest(config: QuickStartConfig, minecraftServerVersion = '1.18.0'): object { + const hasScripts = config.hasScripts && config.uuids.module != null + const scriptModule = hasScripts ? { type: 'script' as const, language: 'javascript' as const, @@ -30,7 +31,7 @@ export function generateBpManifest(config: QuickStartConfig): object { if (scriptModule) { (manifest.dependencies as Array>).push({ module_name: '@minecraft/server', - version: config.minecraftServerVersion || '1.18.0', + version: minecraftServerVersion, }) } @@ -41,13 +42,7 @@ export function generateBpManifest(config: QuickStartConfig): object { }) } - manifest.metadata = { - license: 'MIT', - url: 'https://github.com/mcbe-mods', - } - if (config.author) { - (manifest.metadata as Record).authors = [config.author] - } + manifest.metadata = buildMetadata(config) return manifest } @@ -70,7 +65,7 @@ export function generateRpManifest(config: QuickStartConfig): object { { type: 'resources', version: parseVersion(config.version), - uuid: config.uuids.module, + uuid: config.uuids.module || config.uuids.resourcePack, }, ], dependencies: [], @@ -83,23 +78,82 @@ export function generateRpManifest(config: QuickStartConfig): object { }) } - manifest.metadata = { - license: 'MIT', - url: 'https://github.com/mcbe-mods', - } - if (config.author) { - (manifest.metadata as Record).authors = [config.author] - } + manifest.metadata = buildMetadata(config) return manifest } -export function writeManifest(dir: string, type: 'behavior_pack' | 'resource_pack', config: QuickStartConfig): void { +function buildMetadata(config: QuickStartConfig): Record { + const metadata: Record = {} + if (config.author) { metadata.authors = [config.author] } + if (config.license) { metadata.license = config.license } + else { metadata.license = 'MIT' } + if (config.homepage) { metadata.url = config.homepage } + else { metadata.url = 'https://github.com/mcbe-mods' } + return metadata +} + +export function writeManifest( + dir: string, + type: 'behavior_pack' | 'resource_pack', + config: QuickStartConfig, + minecraftServerVersion?: string, +): void { const manifest = type === 'resource_pack' ? generateRpManifest(config) - : generateBpManifest(config) + : generateBpManifest(config, minecraftServerVersion) const manifestPath = join(dir, 'manifest.json') mkdirSync(dirname(manifestPath), { recursive: true }) writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) } + +export function patchManifest(projectDir: string): void { + const config = getProjectConfig(projectDir) + const version = parseVersion(config.version) + + for (const packType of ['behavior_pack', 'resource_pack'] as const) { + const dir = join(projectDir, 'src', packType) + const manifestPath = join(dir, 'manifest.json') + if (!existsSync(manifestPath)) { continue } + + const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) + + const packKey = packType === 'behavior_pack' ? 'behaviorPack' : 'resourcePack' + if (config.uuids[packKey]) { + manifest.header.uuid = config.uuids[packKey] + } + manifest.header.version = version + manifest.header.min_engine_version = config.minEngineVersion + + if (packType === 'behavior_pack' && config.uuids.module && config.hasScripts) { + const scriptModule = manifest.modules?.find((m: { type: string }) => m.type === 'script') + if (scriptModule) { + scriptModule.uuid = config.uuids.module + scriptModule.version = version + } + } + + if (manifest.dependencies) { + for (const dep of manifest.dependencies) { + if (dep.module_name?.startsWith('@minecraft/')) { + const pkgVersion = getMcDependencyVersion(projectDir, dep.module_name) + if (pkgVersion) { + dep.version = pkgVersion + } + } + else if (dep.uuid === config.uuids.behaviorPack || dep.uuid === config.uuids.resourcePack) { + dep.version = version + } + } + } + + const pkg = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf-8')) + if (!manifest.metadata) { manifest.metadata = {} } + if (pkg.author) { manifest.metadata.authors = [pkg.author] } + manifest.metadata.license = pkg.license || 'MIT' + manifest.metadata.url = pkg.homepage || 'https://github.com/mcbe-mods' + + writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) + } +} diff --git a/src/core/project.ts b/src/core/project.ts index 422d9ed..a52913f 100644 --- a/src/core/project.ts +++ b/src/core/project.ts @@ -1,53 +1,71 @@ -import type { QuickStartConfig } from '../types.js' +import type { McbeConfig, QuickStartConfig, ScriptLanguage } from '../types.js' import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { join } from 'node:path' +import process from 'node:process' -const CONFIG_FILE = 'mcbe.config.json' +const CONFIG_KEY = 'mcbe' -export function getProjectDir(): string | null { - let dir = process.cwd() - while (true) { - if (existsSync(join(dir, CONFIG_FILE))) { return dir } - const parent = join(dir, '..') - if (parent === dir) { return null } - dir = parent - } +function getRootPkg(dir: string): Record { + const pkgPath = join(dir, 'package.json') + if (!existsSync(pkgPath)) { throw new Error(`No package.json found in ${dir}`) } + return JSON.parse(readFileSync(pkgPath, 'utf-8')) } -export function getProjectConfig(projectDir?: string): QuickStartConfig { - const dir = projectDir || getProjectDir() - if (!dir) { throw new Error('No mcbe.config.json found. Are you in a project directory?') } +export function getProjectDir(): string { + const dir = process.cwd() + const pkg = getRootPkg(dir) + if (!pkg[CONFIG_KEY]) { throw new Error('Not in a project directory: package.json is missing "mcbe" field') } + return dir +} - const configPath = join(dir, CONFIG_FILE) - if (!existsSync(configPath)) { throw new Error(`Config file not found: ${configPath}`) } +function inferHasScripts(dir: string): boolean { + const manifestPath = join(dir, 'src', 'behavior_pack', 'manifest.json') + if (!existsSync(manifestPath)) { return false } + try { + const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) + return manifest.modules?.some((m: { type: string }) => m.type === 'script') ?? false + } + catch { return false } +} - return JSON.parse(readFileSync(configPath, 'utf-8')) +function inferLanguage(dir: string): ScriptLanguage | undefined { + if (!inferHasScripts(dir)) { return undefined } + return existsSync(join(dir, 'tsconfig.json')) ? 'typescript' : 'javascript' } -export function writeProjectConfig(config: QuickStartConfig, projectDir?: string): void { +export function getProjectConfig(projectDir?: string): QuickStartConfig { const dir = projectDir || getProjectDir() - if (!dir) { throw new Error('No project directory found') } + const pkg = getRootPkg(dir) + const mcbe = pkg[CONFIG_KEY] as McbeConfig | undefined + if (!mcbe) { throw new Error('package.json is missing "mcbe" field') } - const configPath = join(dir, CONFIG_FILE) - writeFileSync(configPath, JSON.stringify(config, null, 2)) -} - -export function createProjectConfig( - projectDir: string, - config: QuickStartConfig, -): void { - const configPath = join(projectDir, CONFIG_FILE) - writeFileSync(configPath, JSON.stringify(config, null, 2)) + return { + name: pkg.name as string || '', + version: pkg.version as string || '0.1.0', + author: pkg.author as string | undefined, + description: pkg.description as string | undefined, + license: pkg.license as string | undefined, + homepage: pkg.homepage as string | undefined, + uuids: mcbe.uuids ?? {}, + hasScripts: inferHasScripts(dir), + language: inferLanguage(dir), + minEngineVersion: mcbe.minEngineVersion, + } } -export function updateProjectConfig( - projectDir: string, - partial: Partial, -): QuickStartConfig { - const config = getProjectConfig(projectDir) - const updated = { ...config, ...partial } - writeProjectConfig(updated, projectDir) - return updated +export function writeMcbeConfig(projectDir: string, config: Partial): void { + const pkgPath = join(projectDir, 'package.json') + const pkg = getRootPkg(projectDir) + const existing = (pkg[CONFIG_KEY] as Record) || {} + const merged: Record = { ...existing } + if (config.uuids) { + merged.uuids = { ...(existing.uuids as Record || {}), ...config.uuids } + } + if (config.minEngineVersion) { + merged.minEngineVersion = config.minEngineVersion + } + pkg[CONFIG_KEY] = merged + writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`) } export function parseVersion(version: string): [number, number, number] { @@ -58,3 +76,11 @@ export function parseVersion(version: string): [number, number, number] { parts[2] !== undefined ? Number(parts[2]) : 0, ] } + +export function getMcDependencyVersion(projectDir: string, moduleName: string): string | undefined { + const pkg = getRootPkg(projectDir) + const allDeps = { ...(pkg.devDependencies as Record || {}), ...(pkg.dependencies as Record || {}) } + const version = allDeps[moduleName] + if (!version) { return undefined } + return version.replace(/^[~^>=<]+\s*/, '') +} diff --git a/src/types.ts b/src/types.ts index abe14c5..411f796 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,20 +4,24 @@ export interface UUIDs { module: string } +// Config stored in package.json.mcbe +export interface McbeConfig { + uuids?: Partial + minEngineVersion: [number, number, number] +} + +// Full project config assembled at runtime from multiple sources export interface QuickStartConfig { name: string version: string author?: string description?: string - template: string - uuids: UUIDs - hasScripts?: boolean + license?: string + homepage?: string + uuids: Partial + hasScripts: boolean language?: ScriptLanguage minEngineVersion: [number, number, number] - minecraftServerVersion?: string - minecraft?: { - developmentPath?: string - } } export type ScriptLanguage = 'typescript' | 'javascript' diff --git a/src/utils/mcpath.ts b/src/utils/mcpath.ts index 347d86b..46b3ca1 100644 --- a/src/utils/mcpath.ts +++ b/src/utils/mcpath.ts @@ -1,45 +1,27 @@ -import type { McPaths, QuickStartConfig } from '../types.js' -import { existsSync, readFileSync } from 'node:fs' +import type { McPaths } from '../types.js' +import { existsSync } from 'node:fs' import { join } from 'node:path' import process from 'node:process' const WIN_STORE_PACKAGE = 'Microsoft.MinecraftUWP_8wekyb3d8bbwe' -const WIN_GAMEPASS_PACKAGE = 'Microsoft.4297127D64EC1_8wekyb3d8bbwe' - -function getWinPackageName(): string | null { - const candidates = [WIN_STORE_PACKAGE, WIN_GAMEPASS_PACKAGE] - const localAppData = process.env.LOCALAPPDATA - if (!localAppData) { return null } - - for (const pkg of candidates) { - const basePath = join(localAppData, 'Packages', pkg, 'LocalState', 'games', 'com.mojang') - if (existsSync(join(basePath, 'development_behavior_packs'))) { - return pkg - } - if (existsSync(basePath)) { - return pkg - } - } - return null -} function getDefaultMcPaths(): McPaths | null { if (process.platform !== 'win32') { return null } - const pkg = getWinPackageName() - if (!pkg) { return null } + const localAppData = process.env.LOCALAPPDATA + if (!localAppData) { return null } - const localAppData = process.env.LOCALAPPDATA! - const comMojang = join(localAppData, 'Packages', pkg, 'LocalState', 'games', 'com.mojang') + const basePath = join(localAppData, 'Packages', WIN_STORE_PACKAGE, 'LocalState', 'games', 'com.mojang') + if (!existsSync(basePath)) { return null } return { - developmentBehaviorPacks: join(comMojang, 'development_behavior_packs'), - developmentResourcePacks: join(comMojang, 'development_resource_packs'), + developmentBehaviorPacks: join(basePath, 'development_behavior_packs'), + developmentResourcePacks: join(basePath, 'development_resource_packs'), } } -export function getMcPaths(projectDir?: string): McPaths | null { - const envPath = process.env.MC_DEV_PATH +export function getMcPaths(_projectDir?: string): McPaths | null { + const envPath = process.env.MCBE_DEV_PATH if (envPath) { return { developmentBehaviorPacks: join(envPath, 'development_behavior_packs'), @@ -47,22 +29,6 @@ export function getMcPaths(projectDir?: string): McPaths | null { } } - if (projectDir) { - const configPath = join(projectDir, 'mcbe.config.json') - if (existsSync(configPath)) { - try { - const config: QuickStartConfig = JSON.parse(readFileSync(configPath, 'utf-8')) - if (config.minecraft?.developmentPath) { - return { - developmentBehaviorPacks: join(config.minecraft.developmentPath, 'development_behavior_packs'), - developmentResourcePacks: join(config.minecraft.developmentPath, 'development_resource_packs'), - } - } - } - catch {} - } - } - return getDefaultMcPaths() }