-
Notifications
You must be signed in to change notification settings - Fork 46
Description
요약
@apps-in-toss/web-framework를 사용하여 SvelteKit 기반 웹 앱을 빌드할 때, svelte export condition만 정의된 패키지(예: svelte-easy-crop)가 있으면 npx granite build 패키징 단계에서 실패합니다.
Vite 빌드 자체는 성공하지만, .ait 파일 생성을 위한 의존성 버전 수집 단계에서 esbuild가 해당 패키지를 resolve하지 못합니다.
환경
| 항목 | 버전 |
|---|---|
@apps-in-toss/web-framework |
1.5.2 |
@apps-in-toss/plugins |
(web-framework 내장) |
| Node.js | 22.x |
| SvelteKit | 2.49.2 |
| Svelte | 5.45.8 |
| Vite | 7.2.7 |
| OS | macOS (Darwin 25.1.0) |
재현 방법
1. SvelteKit 프로젝트에 앱인토스 설정
npm install @apps-in-toss/web-framework
npx ait init2. svelte export condition만 있는 패키지 설치
npm install svelte-easy-crop3. 해당 패키지를 컴포넌트에서 사용
<script>
import Cropper from 'svelte-easy-crop';
</script>
<Cropper image={imageUrl} bind:crop bind:zoom aspect={5/8} />4. 빌드 실행
npx granite build에러 메시지
> app@0.0.1 build:toss
> BUILD_TARGET=toss VITE_BUILD_TARGET=toss vite build
vite v7.2.7 building for production...
✓ 6428 modules transformed.
✓ built in 15.05s
> Using @sveltejs/adapter-static
Wrote site to "build"
✔ done
◇ Building ios
◇ Building android
Internal Error: Build failed with 1 error:
<stdin>:10:7: ERROR: [plugin: collect-package-version] Could not resolve "svelte-easy-crop"
at failureErrorWithLog (/path/to/node_modules/@apps-in-toss/plugins/node_modules/esbuild/lib/main.js:1463:15)
...
원인 분석
1. svelte-easy-crop의 package.json exports 필드
{
"name": "svelte-easy-crop",
"version": "5.0.0",
"type": "module",
"svelte": "./dist/index.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
}
}exports에 svelte condition만 정의되어 있고 default가 없습니다.
2. @apps-in-toss/plugins의 collectDependencyVersions 함수
// @apps-in-toss/plugins/dist/index.js
async function collectDependencyVersions(rootDir) {
const packageJsonPath = path.join(rootDir, "package.json");
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
const [dependencies, devDependencies] = await Promise.all([
resolvePackageVersions(rootDir, Object.keys(packageJson.dependencies)),
resolvePackageVersions(rootDir, Object.keys(packageJson.devDependencies)),
]);
return { dependencies, devDependencies };
}
async function resolvePackageVersions(rootDir, packageNames) {
const results = {};
await esbuild.build({
stdin: { contents: createVirtualEntry(packageNames) },
bundle: true,
write: false,
logLevel: "silent",
plugins: [
{
name: "collect-package-version",
setup(build) {
// ... resolve 로직
},
},
],
});
return results;
}이 함수는 package.json의 모든 dependencies와 devDependencies를 esbuild로 resolve하여 버전 정보를 수집합니다.
3. 문제 발생 지점
esbuild는 기본적으로 svelte export condition을 인식하지 못합니다. Vite/SvelteKit은 svelte condition을 지원하므로 빌드가 성공하지만, collectDependencyVersions에서 사용하는 esbuild는 이를 처리하지 못해 resolve 실패가 발생합니다.
영향 범위
다음 조건을 만족하는 모든 패키지에서 동일한 문제가 발생합니다:
exports필드에sveltecondition만 정의defaultcondition 없음- 프로젝트의
dependencies또는devDependencies에 포함
Svelte 생태계의 많은 패키지들이 이 패턴을 따르고 있어, SvelteKit 기반 앱인토스 앱에서 광범위하게 영향을 받을 수 있습니다.
영향받는 패키지 예시:
svelte-easy-crop- 기타
sveltecondition만 사용하는 Svelte 컴포넌트 라이브러리들
제안하는 해결 방법
방법 1: esbuild conditions 옵션 추가 (권장)
resolvePackageVersions 함수의 esbuild 설정에 conditions 옵션을 추가합니다:
await esbuild.build({
stdin: { contents: createVirtualEntry(packageNames) },
bundle: true,
write: false,
logLevel: "silent",
conditions: ['svelte', 'node', 'import', 'default'], // 추가
plugins: [...]
});방법 2: resolve 실패 시 graceful fallback
패키지 resolve가 실패해도 빌드를 중단하지 않고 경고만 출력하는 방식:
build.onResolve({ filter: /.*/ }, async (args) => {
// ...
if (result.errors.length) {
console.warn(
`Warning: Could not resolve ${args.path}, skipping version collection`
);
return { path: args.path, external: true }; // 외부 모듈로 처리
}
// ...
});방법 3: 프레임워크별 conditions 자동 감지
프로젝트의 package.json이나 설정 파일을 분석하여 Svelte/Vue/React 등 프레임워크를 감지하고, 해당 프레임워크의 export condition을 자동으로 추가하는 방식.
현재 워크어라운드
1. postinstall 스크립트로 패키지 패치
// scripts/patch-svelte-easy-crop.js
const fs = require("fs");
const path = require("path");
const pkgPath = path.join(
__dirname,
"../node_modules/svelte-easy-crop/package.json"
);
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
pkg.exports["."].default = pkg.exports["."].svelte;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));// package.json
{
"scripts": {
"postinstall": "node scripts/patch-svelte-easy-crop.js"
}
}2. 수동 .ait 패키징
Vite 빌드가 성공하므로 build/ 디렉토리를 수동으로 패키징하는 방법도 가능하나, 메타데이터 구조를 정확히 맞춰야 하므로 권장하지 않습니다.
추가 정보
성공하는 단계
npm run build:toss(Vite 빌드): ✅ 성공npx granite dev(개발 서버): ✅ 성공- Static adapter로
build/디렉토리 생성: ✅ 성공 - iOS/Android 번들 생성 (
lebit.ios.js,lebit.android.js): ✅ 성공
실패하는 단계
.ait파일 생성을 위한collectDependencyVersions: ❌ 실패
빌드 결과물 구조 (실패 전)
build/
├── lebit.android.js
├── lebit.android.js.map
├── lebit.ios.js
├── lebit.ios.js.map
└── web/
├── _app/
├── index.html
└── ... (static assets)
번들 자체는 정상적으로 생성되었으며, 의존성 버전 수집 단계에서만 실패합니다.
관련 코드 위치
@apps-in-toss/plugins/src/utils/collectDependencyVersions.ts@apps-in-toss/web-framework/src/cli/index.ts(appsInTossCreateArtifact함수)