Skip to content

[web-framework] collectDependencyVersions에서 svelte export condition을 가진 패키지 resolve 실패 #169

@clomia

Description

@clomia

요약

@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 init

2. svelte export condition만 있는 패키지 설치

npm install svelte-easy-crop

3. 해당 패키지를 컴포넌트에서 사용

<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-croppackage.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"
    }
  }
}

exportssvelte condition만 정의되어 있고 default가 없습니다.

2. @apps-in-toss/pluginscollectDependencyVersions 함수

// @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의 모든 dependenciesdevDependencies를 esbuild로 resolve하여 버전 정보를 수집합니다.

3. 문제 발생 지점

esbuild는 기본적으로 svelte export condition을 인식하지 못합니다. Vite/SvelteKit은 svelte condition을 지원하므로 빌드가 성공하지만, collectDependencyVersions에서 사용하는 esbuild는 이를 처리하지 못해 resolve 실패가 발생합니다.


영향 범위

다음 조건을 만족하는 모든 패키지에서 동일한 문제가 발생합니다:

  1. exports 필드에 svelte condition만 정의
  2. default condition 없음
  3. 프로젝트의 dependencies 또는 devDependencies에 포함

Svelte 생태계의 많은 패키지들이 이 패턴을 따르고 있어, SvelteKit 기반 앱인토스 앱에서 광범위하게 영향을 받을 수 있습니다.

영향받는 패키지 예시:

  • svelte-easy-crop
  • 기타 svelte condition만 사용하는 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 함수)

참고 링크

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions