125k

パッケージインポート

package.json インポートを使用して shadcn/ui を構成します。

shadcn CLI は パッケージのインポート をサポートしています。コンポーネントのインストール、インポートの書き換え、サードパーティレジストリの解決に使用されます。

パッケージのインポートにより、tsconfig.json の compilerOptions.paths の代わりに package.json でプライベートな #... インポートエイリアスを使用できます。

例

package.json で imports を構成します。

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  }
}

次に、#... 指定子を使用して、生成されたコンポーネントをインポートします。

import { Button } from "#components/ui/button"
import { cn } from "#lib/utils"

アプリ

Next.js、Vite、TanStack Start のようなアプリでは、コンポーネントを同じワークスペースにインストールして追加します。

package.json を構成する

shadcn/ui のインストール先に対応するインポートを追加します。

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  }
}

アプリが src ディレクトリを使用しない場合は、ターゲットから src/ を削除します。たとえば:

package.json
{
  "imports": {
    "#components/*": "./components/*.tsx",
    "#lib/*": "./lib/*.ts",
    "#hooks/*": "./hooks/*.ts"
  }
}

TypeScript を設定する

パッケージのインポート解決を有効にします。

tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "resolvePackageJsonImports": true
  }
}

これらのエイリアスには compilerOptions.paths は必要ありません。

components.json を構成する

components.json で同じ #... ルートを使用します。

components.json
{
  "aliases": {
    "components": "#components",
    "ui": "#components/ui",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "#lib/utils"
  }
}

ui エイリアスは #components/ui を使用しますが、これは package.json の #components/* インポートでまだカバーされています。

utils エイリアスは #lib/utils を使用します。これは #lib/* でカバーされているため、別途 #utils インポートを追加する必要はありません。

モノレポ

モノレポでは、各パッケージ内のファイルにはパッケージインポートを使用し、ワークスペース間で共有するファイルにはエクスポートを使用します。

アプリのワークスペースの場合:

apps/web/package.json
{
  "name": "web",
  "private": true,
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  },
  "dependencies": {
    "@workspace/ui": "workspace:*"
  }
}
apps/web/components.json
{
  "aliases": {
    "components": "#components",
    "ui": "@workspace/ui/components",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "@workspace/ui/lib/utils"
  }
}

共有 UI パッケージの場合:

packages/ui/package.json
{
  "name": "@workspace/ui",
  "private": true,
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  },
  "exports": {
    "./globals.css": "./src/styles/globals.css",
    "./components/*": "./src/components/*.tsx",
    "./lib/*": "./src/lib/*.ts",
    "./hooks/*": "./src/hooks/*.ts"
  }
}
packages/ui/components.json
{
  "aliases": {
    "components": "#components",
    "ui": "#components",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "#lib/utils"
  }
}

apps/web から add を実行すると、アプリのローカルファイルは #... インポートを使用し、 共有 UI ファイルは @workspace/ui からインポートされます。

import { Button } from "@workspace/ui/components/button"
import { LoginForm } from "#components/login-form"

ファイル拡張子

package.json#imports のターゲットパターンは、生成されるインポートにファイル拡張子を含めるかどうかを制御します。

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx"
  }
}

これにより、拡張子なしのインポートが生成されます。

import { Button } from "#components/ui/button"

拡張子なしでターゲットを使用する場合:

package.json
{
  "imports": {
    "#components/*": "./src/components/*"
  }
}

生成されたインポートでは、ソース拡張子が保持されます。

import { Button } from "#components/ui/button.tsx"

ほとんどのアプリでは、ターゲットパターンで拡張子を使用します。

トラブルシューティング

TypeScript が #... インポートを解決できない場合は、次のことを確認してください。

  • 指定子は # で始まります
  • imports エントリは最も近い package.json にあります
  • moduleResolution は bundler に設定されます
  • resolvePackageJsonImports が有効になっています
  • コンポーネントの追加後に一致するターゲットが存在する

コンポーネントをインストールしたにもかかわらず、インポートが依然として @/... を指している場合は、components.json がパッケージのインポートと同じ #... エイリアスを使用しているか確認してください。