fix: publish game engine runtime subpaths
This commit is contained in:
@@ -5,9 +5,95 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./auction/finalizer.js": {
|
||||||
|
"types": "./dist/auction/finalizer.d.ts",
|
||||||
|
"default": "./dist/auction/finalizer.js"
|
||||||
|
},
|
||||||
|
"./scenario/mapLoader.js": {
|
||||||
|
"types": "./dist/scenario/mapLoader.d.ts",
|
||||||
|
"default": "./dist/scenario/mapLoader.js"
|
||||||
|
},
|
||||||
|
"./scenario/scenarioComposition.js": {
|
||||||
|
"types": "./dist/scenario/scenarioComposition.d.ts",
|
||||||
|
"default": "./dist/scenario/scenarioComposition.js"
|
||||||
|
},
|
||||||
|
"./scenario/scenarioLoader.js": {
|
||||||
|
"types": "./dist/scenario/scenarioLoader.d.ts",
|
||||||
|
"default": "./dist/scenario/scenarioLoader.js"
|
||||||
|
},
|
||||||
|
"./scenario/scenarioSeeder.js": {
|
||||||
|
"types": "./dist/scenario/scenarioSeeder.d.ts",
|
||||||
|
"default": "./dist/scenario/scenarioSeeder.js"
|
||||||
|
},
|
||||||
|
"./scenario/unitSetLoader.js": {
|
||||||
|
"types": "./dist/scenario/unitSetLoader.d.ts",
|
||||||
|
"default": "./dist/scenario/unitSetLoader.js"
|
||||||
|
},
|
||||||
|
"./turn/databaseHooks.js": {
|
||||||
|
"types": "./dist/turn/databaseHooks.d.ts",
|
||||||
|
"default": "./dist/turn/databaseHooks.js"
|
||||||
|
},
|
||||||
|
"./turn/inMemoryWorld.js": {
|
||||||
|
"types": "./dist/turn/inMemoryWorld.d.ts",
|
||||||
|
"default": "./dist/turn/inMemoryWorld.js"
|
||||||
|
},
|
||||||
|
"./turn/monthlyDisasterAction.js": {
|
||||||
|
"types": "./dist/turn/monthlyDisasterAction.d.ts",
|
||||||
|
"default": "./dist/turn/monthlyDisasterAction.js"
|
||||||
|
},
|
||||||
|
"./turn/monthlyEventHandler.js": {
|
||||||
|
"types": "./dist/turn/monthlyEventHandler.d.ts",
|
||||||
|
"default": "./dist/turn/monthlyEventHandler.js"
|
||||||
|
},
|
||||||
|
"./turn/monthlyNationBettingAction.js": {
|
||||||
|
"types": "./dist/turn/monthlyNationBettingAction.d.ts",
|
||||||
|
"default": "./dist/turn/monthlyNationBettingAction.js"
|
||||||
|
},
|
||||||
|
"./turn/npcPossessionService.js": {
|
||||||
|
"types": "./dist/turn/npcPossessionService.d.ts",
|
||||||
|
"default": "./dist/turn/npcPossessionService.js"
|
||||||
|
},
|
||||||
|
"./turn/rankData.js": {
|
||||||
|
"types": "./dist/turn/rankData.d.ts",
|
||||||
|
"default": "./dist/turn/rankData.js"
|
||||||
|
},
|
||||||
|
"./turn/reservedTurnHandler.js": {
|
||||||
|
"types": "./dist/turn/reservedTurnHandler.d.ts",
|
||||||
|
"default": "./dist/turn/reservedTurnHandler.js"
|
||||||
|
},
|
||||||
|
"./turn/reservedTurnStore.js": {
|
||||||
|
"types": "./dist/turn/reservedTurnStore.d.ts",
|
||||||
|
"default": "./dist/turn/reservedTurnStore.js"
|
||||||
|
},
|
||||||
|
"./turn/selectPoolService.js": {
|
||||||
|
"types": "./dist/turn/selectPoolService.d.ts",
|
||||||
|
"default": "./dist/turn/selectPoolService.js"
|
||||||
|
},
|
||||||
|
"./turn/turnCommandProfile.js": {
|
||||||
|
"types": "./dist/turn/turnCommandProfile.d.ts",
|
||||||
|
"default": "./dist/turn/turnCommandProfile.js"
|
||||||
|
},
|
||||||
|
"./turn/types.js": {
|
||||||
|
"types": "./dist/turn/types.d.ts",
|
||||||
|
"default": "./dist/turn/types.js"
|
||||||
|
},
|
||||||
|
"./turn/worldCommandHandler.js": {
|
||||||
|
"types": "./dist/turn/worldCommandHandler.d.ts",
|
||||||
|
"default": "./dist/turn/worldCommandHandler.js"
|
||||||
|
},
|
||||||
|
"./turn/worldLoader.js": {
|
||||||
|
"types": "./dist/turn/worldLoader.d.ts",
|
||||||
|
"default": "./dist/turn/worldLoader.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-engine",
|
"build": "tsdown -c ./tsdown.config.ts && node ../../tools/check-package-exports.mjs .",
|
||||||
"dev": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-engine --watch",
|
"dev": "tsdown -c ./tsdown.config.ts --watch",
|
||||||
"start": "pnpm run build && GAME_ENGINE_ROLE=turn-daemon node dist/index.js",
|
"start": "pnpm run build && GAME_ENGINE_ROLE=turn-daemon node dist/index.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { defineConfig } from 'tsdown';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: {
|
||||||
|
index: 'src/index.ts',
|
||||||
|
'auction/finalizer': 'src/auction/finalizer.ts',
|
||||||
|
'scenario/mapLoader': 'src/scenario/mapLoader.ts',
|
||||||
|
'scenario/scenarioComposition': 'src/scenario/scenarioComposition.ts',
|
||||||
|
'scenario/scenarioLoader': 'src/scenario/scenarioLoader.ts',
|
||||||
|
'scenario/scenarioSeeder': 'src/scenario/scenarioSeeder.ts',
|
||||||
|
'scenario/unitSetLoader': 'src/scenario/unitSetLoader.ts',
|
||||||
|
'turn/databaseHooks': 'src/turn/databaseHooks.ts',
|
||||||
|
'turn/inMemoryWorld': 'src/turn/inMemoryWorld.ts',
|
||||||
|
'turn/monthlyDisasterAction': 'src/turn/monthlyDisasterAction.ts',
|
||||||
|
'turn/monthlyEventHandler': 'src/turn/monthlyEventHandler.ts',
|
||||||
|
'turn/monthlyNationBettingAction': 'src/turn/monthlyNationBettingAction.ts',
|
||||||
|
'turn/npcPossessionService': 'src/turn/npcPossessionService.ts',
|
||||||
|
'turn/rankData': 'src/turn/rankData.ts',
|
||||||
|
'turn/reservedTurnHandler': 'src/turn/reservedTurnHandler.ts',
|
||||||
|
'turn/reservedTurnStore': 'src/turn/reservedTurnStore.ts',
|
||||||
|
'turn/selectPoolService': 'src/turn/selectPoolService.ts',
|
||||||
|
'turn/turnCommandProfile': 'src/turn/turnCommandProfile.ts',
|
||||||
|
'turn/types': 'src/turn/types.ts',
|
||||||
|
'turn/worldCommandHandler': 'src/turn/worldCommandHandler.ts',
|
||||||
|
'turn/worldLoader': 'src/turn/worldLoader.ts',
|
||||||
|
},
|
||||||
|
format: 'es',
|
||||||
|
outDir: 'dist',
|
||||||
|
dts: {
|
||||||
|
build: true,
|
||||||
|
},
|
||||||
|
sourcemap: true,
|
||||||
|
target: 'node22',
|
||||||
|
platform: 'node',
|
||||||
|
fixedExtension: false,
|
||||||
|
hash: false,
|
||||||
|
});
|
||||||
@@ -40,6 +40,12 @@ Resource를 읽는 `scenarioLoader`, `mapLoader`, `unitSetLoader`,
|
|||||||
사용합니다. `@sammo-ts/game-engine` 루트는 daemon process entrypoint이므로
|
사용합니다. `@sammo-ts/game-engine` 루트는 daemon process entrypoint이므로
|
||||||
API 제품 소스에서 library처럼 import하지 않습니다.
|
API 제품 소스에서 library처럼 import하지 않습니다.
|
||||||
|
|
||||||
|
game-engine의 공개 subpath는 `app/game-engine/package.json`의 `exports`와
|
||||||
|
`app/game-engine/tsdown.config.ts`의 entry에 함께 선언합니다. `pnpm --filter
|
||||||
|
@sammo-ts/game-engine build`는 각 JS·type export target이 실제 생성됐는지
|
||||||
|
검사하므로, typecheck에서는 보이지만 production Node에서만 실패하는 subpath
|
||||||
|
누락을 배포 전에 차단합니다.
|
||||||
|
|
||||||
Frontend가 tRPC router shape를 참조할 때는 `import type`만 사용하고 backend
|
Frontend가 tRPC router shape를 참조할 때는 `import type`만 사용하고 backend
|
||||||
package를 `devDependencies`에 둡니다. 브라우저에서 실제 실행하는 공유 값만
|
package를 `devDependencies`에 둡니다. 브라우저에서 실제 실행하는 공유 값만
|
||||||
`common` 또는 `logic`의 browser-safe export에서 가져옵니다.
|
`common` 또는 `logic`의 browser-safe export에서 가져옵니다.
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { access, readFile } from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const packageRoot = path.resolve(process.argv[2] ?? '');
|
||||||
|
if (!process.argv[2]) {
|
||||||
|
throw new Error('Usage: node tools/check-package-exports.mjs <package-directory>');
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
|
||||||
|
const targets = new Set();
|
||||||
|
for (const descriptor of Object.values(manifest.exports ?? {})) {
|
||||||
|
if (typeof descriptor === 'string') {
|
||||||
|
targets.add(descriptor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (descriptor && typeof descriptor === 'object') {
|
||||||
|
for (const target of Object.values(descriptor)) {
|
||||||
|
if (typeof target === 'string') targets.add(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
const absolute = path.resolve(packageRoot, target);
|
||||||
|
if (absolute !== packageRoot && !absolute.startsWith(`${packageRoot}${path.sep}`)) {
|
||||||
|
throw new Error(`Package export escapes its package directory: ${target}`);
|
||||||
|
}
|
||||||
|
await access(absolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(`Validated ${targets.size} package export targets for ${manifest.name}.\n`);
|
||||||
Reference in New Issue
Block a user