oxlintでimportして良いパッケージを制限する

oxlintでimportして良いパッケージを制限する

Cloudflare WorkersとWebフロントエンドを同一プロジェクトで管理する際に、誤ったライブラリのimportを防ぐ方法をご紹介します。oxlintの設定を活用して、各環境で使用可能なパッケージを制限し、開発時のミスを未然に防ぎましょう。
2026.09.09

こんばんは、情報システム室の夏目です。

Cloudflare WorkersでWebフロントとAPIを組んでいるのですが、Workerでしか使わないライブラリ、Webフロントでしか使わないライブラリ、これらがpackage.jsonのdependenciesにあります。

今回は間違えて使わないようにoxlintを使って間違えてimportしたらlintでエラーが出るようにします。

1. 準備

まずある程度のコードを準備します。
結論だけ見たい場合はスキップして 2. まで進んでください。

1-1. プロジェクトの雛型を作る

まずViteでReactのプロジェクトを作成します。

$ npm create vite@latest
Need to install the following packages:
create-vite@9.2.0
Ok to proceed? (y) y
npm notice run npx
npm notice run 'create-vite'

  Project name:
  trial-import-linting

  Select a framework:
  React

  Select a variant:
  TypeScript + React Compiler

  Which linter to use?
  Oxlint

  Install with npm and start now?
  No

  Scaffolding project in /Users/natsume.yuta/spaces/work/blog/017_import_limiting/trial-import-linting...

  Done. Now run:

  cd trial-import-linting
  npm install
  npm run dev

作成されたファイルは次のようになっています。

$ cd trial-import-linting
$ eza -la --time-style=long-iso -F
.rw-r--r--@  253 natsume.yuta 2026-09-09 13:57 .gitignore
.rw-r--r--@  245 natsume.yuta 2026-09-09 13:57 .oxlintrc.json
.rw-r--r--@  372 natsume.yuta 2026-09-09 13:57 index.html
.rw-r--r--@  692 natsume.yuta 2026-09-09 13:57 package.json
drwxr-xr-x@    - natsume.yuta 2026-09-09 13:57 public/
.rw-r--r--@ 1.5k natsume.yuta 2026-09-09 13:57 README.md
drwxr-xr-x@    - natsume.yuta 2026-09-09 13:57 src/
.rw-r--r--@  655 natsume.yuta 2026-09-09 13:57 tsconfig.app.json
.rw-r--r--@  119 natsume.yuta 2026-09-09 13:57 tsconfig.json
.rw-r--r--@  558 natsume.yuta 2026-09-09 13:57 tsconfig.node.json
.rw-r--r--@  286 natsume.yuta 2026-09-09 13:57 vite.config.ts

依存ライブラリをインストールします。
またCloudflare Workers用のViteプラグインもインストールします。

$ mkdir worker
$ npm install

added 72 packages, and audited 73 packages in 18s

13 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   fsevents@2.3.3 (install: node-gyp rebuild)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

$ npm install -D @cloudflare/vite-plugin

added 33 packages, and audited 106 packages in 6s

20 packages are looking for funding
  run `npm fund` for details

4 high severity vulnerabilities

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
npm warn install-scripts 3 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   fsevents@2.3.3 (install: (install scripts present))
npm warn install-scripts   workerd@1.20260908.1 (postinstall: node install.js)
npm warn install-scripts   esbuild@0.28.1 (postinstall: node install.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow

次に、Webフロント用のコードにpathsを設定します。

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "es2023",
    "lib": ["ES2023", "DOM"],
    "module": "esnext",
    "types": ["vite/client"],
    "allowArbitraryExtensions": true,
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true,

    /* Custom */
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src"]
}

vite.config.tsにCloudflare Workers用のpluginを追加し、pathsを使用できるようにします。

vite.config.ts
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
import { defineConfig } from 'vite'
import { cloudflare } from '@cloudflare/vite-plugin'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react(),
    babel({ presets: [reactCompilerPreset()] }),
    cloudflare()
  ],
  resolve: {
    tsconfigPaths: true
  }
})

1-2. Worker用のコードを書く

Worker用のコードを置くディレクトリを作成します。
さらにWorkerのルーティング用にHonoを、Worker用の型ファイル生成用にwranglerをインストールします。

$ mkdir -p worker/routes
$ npm install hono

added 1 package, and audited 74 packages in 5s

13 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   fsevents@2.3.3 (install: (install scripts present))
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

$ npm install -D wrangler

added 32 packages, and audited 106 packages in 8s

20 packages are looking for funding
  run `npm fund` for details

3 high severity vulnerabilities

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
npm warn install-scripts 3 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   fsevents@2.3.3 (install: (install scripts present))
npm warn install-scripts   esbuild@0.28.1 (postinstall: node install.js)
npm warn install-scripts   workerd@1.20260908.1 (postinstall: node install.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

次にwrangler.jsoncを作成します。

wrangler.jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "trial-import-linting",
  "compatibility_date": "2026-09-07",
  "main": "worker/index.ts",
  "assets": {
    "not_found_handling": "single-page-application",
    "run_worker_first": ["/api/*"]
  },
  "observability": {
    "enabled": true
  }
}

次にWorker用の型定義ファイルを生成します。

$ npx wrangler types
npm notice run trial-import-linting@0.0.0 npx
npm notice run 'wrangler' types

 wrangler 4.130.0
────────────────────
Generating project types...

interface __BaseEnv_Env {
}
declare namespace Cloudflare {
        interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}

Generating runtime types...

Runtime types generated.

────────────────────────────────────────────────────────────
 Types written to worker-configuration.d.ts

📖 Read about runtime types
https://developers.cloudflare.com/workers/languages/typescript/#generate-types
📣 Remember to rerun 'wrangler types' after you change your wrangler.jsonc file.

次にWorker用のtsconfig、 tsconfig.worker.json を作成します。

tsconfig.worker.json
{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.worker.tsbuildinfo",
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "esnext",
    "moduleResolution": "bundler",
    "types": ["./worker-configuration.d.ts"],
    "noEmit": true,
    "strict": true,
    "skipLibCheck": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "paths": {
      "@worker/*": ["./worker/*"]
    }
  },
  "include": ["worker-configuration.d.ts", "worker"]
}

作成した tsconfig.worker.jsontsconfig.json に追記します。

tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" },
    { "path": "./tsconfig.worker.json" }
  ]
}

次にWorker用のコードを作成します。

worker/app-env.ts
export type AppEnv = {
  Bindings: Env;
}
worker/routes/health.ts
import { Hono } from "hono";

import type { AppEnv } from "@worker/app-env"

export const routeHealth = new Hono<AppEnv>();

routeHealth.get("/", (c) => c.json({ ok: true }));
worker/routes/index.ts
import { Hono } from "hono";

import type { AppEnv } from "@worker/app-env";

import { routeHealth } from "./health";

export const routeApi = new Hono<AppEnv>();

routeApi.route("/health", routeHealth);
worker/index.ts
import { Hono } from "hono";
import { logger } from "hono/logger";

import type { AppEnv } from "./app-env"
import { routeApi } from "./routes";

const app = new Hono<AppEnv>();

app.use("*", logger());

app.route("/api", routeApi);

app.notFound((c) => c.json({ message: "Not found" }, 404));

app.onError((err, c) => {
  console.error(err);

  return c.json({ message: "Internal Server Error" }, 500);
});

export default app;

2. oxlintでimportの設定をする

Viteでは雛型の段階でOxlintは次のような設定になっています。

.oxlintrc.json
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "oxc"],
  "rules": {
    "react/rules-of-hooks": "error",
    "react/only-export-components": ["warn", { "allowConstantExport": true }]
  }
}

2-1. oxlintの設定ファイルを.oxlintrc.jsoncにする

oxlintはJSONにコメントがあっても動くのですが、エディターなどのために拡張子を .jsonc にします。

$ mv .oxlintrc.json .oxlintrc.jsonc

2-2. Webフロント用のコードでimportを制限する

Webフロントで使用するコードでimportを制限すると次のようになります。

.oxlintrc.jsonc
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "oxc"],
  "rules": {
    "react/rules-of-hooks": "error",
    "react/only-export-components": ["warn", { "allowConstantExport": true }]
  },
  "overrides": [
    {
      // Browser Code
      "files": ["src/**/*.{ts,tsx}"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "patterns": [
              {
                "message": "This dependency is not allowed in browser code.",
                "group": [
                  // 一旦全てを禁止する
                  "*",

                  // ローカルファイルは許可
                  "!./**",
                  "!@/**",

                  // Browser側で使用可能なnpm package
                  "!react",
                  "!react-dom/**"
                ]
              }
            ]
          }
        ]
      }
    }
  ]
}

まず、特定のファイルに対して制限をかけるために overrides を使用します。
"files": ["src/**/*.{ts,tsx}"] とすることでViteにおけるWebフロントのコードに対してlintの制限をかけます。

今回はimportの制限なので no-restricted-imports というルールを使用します。
error とすることでlintに引っかかったら終了コードが 1 になるようにしています。

import { useState } from "react"; と書いたときのfromで指定できなくしたいものを groups に書いていきます (この例だと react になる)。
groups に書くとき頭に ! をつけると逆に指定することを許可することになります。

そのため、①一旦全てのimportを禁止する、②その上で許可したいものを列挙する、という書き方をしています。

この groups では !react-dom と書いたとき、

  • import {} from 'react-dom' は許可される
  • import {} from 'react-dom/client' は禁止のまま

ということになります。
そのため groups!react-dom/** と記載しています。

使用するnpm packageが増えたら書き足していきます。

2-3. Worker用のコードでimportを制限する

同様に、Worker用のコードでimportを制限すると次のようになります。
(overrides で指定するものだけを記載します)

{
  // Worker Code
  "files": ["worker/**/*.ts"],
  "rules": {
    "no-restricted-imports": [
      "error",
      {
        "patterns": [
          {
            "message": "This dependency is not allowed in Cloudflare Workers code.",
            "group": [
              // 一旦全てを禁止する
              "*",

              // ローカルファイルは許可
              "!./**",
              "!@worker/**",

              // Browser側で使用可能なnpm package
              "!hono",
              "!hono/**"
            ]
          }
        ]
      }
    ]
  }
}

2-4. .oxlintrc.jsonc の完成形

.oxlintrc.jsonc
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "oxc"],
  "rules": {
    "react/rules-of-hooks": "error",
    "react/only-export-components": ["warn", { "allowConstantExport": true }]
  },
  "overrides": [
    {
      // Browser Code
      "files": ["src/**/*.{ts,tsx}"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "patterns": [
              {
                "message": "This dependency is not allowed in browser code.",
                "group": [
                  // 一旦全てを禁止する
                  "*",

                  // ローカルファイルは許可
                  "!./**",
                  "!@/**",

                  // Browser側で使用可能なnpm package
                  "!react",
                  "!react-dom/**"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      // Worker Code
      "files": ["worker/**/*.ts"],
      "rules": {
        "no-restricted-imports": [
          "error",
          {
            "patterns": [
              {
                "message": "This dependency is not allowed in Cloudflare Workers code.",
                "group": [
                  // 一旦全てを禁止する
                  "*",

                  // ローカルファイルは許可
                  "!./**",
                  "!@worker/**",

                  // Browser側で使用可能なnpm package
                  "!hono",
                  "!hono/**"
                ]
              }
            ]
          }
        ]
      }
    }
  ]
}

まとめ

以上、oxlintを使ってWebフロント用のコードとWorker用コードでimportして良いパッケージを制限する話でした。

oxlintでlintをするときにエラーになるだけでビルドそのものは通ってしまいますが、間違いを減らすことはできると思います。

何かのお役に立てたら幸いです。

この記事をシェアする

関連記事