eslint-plugin-no-server-imports 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +397 -0
- package/dist/index.d.mts +139 -0
- package/dist/index.d.ts +139 -0
- package/dist/index.js +931 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +892 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# eslint-plugin-no-server-imports
|
|
2
|
+
|
|
3
|
+
Modern frameworks cram server and client files together. You flip between them fast, review a PR, feel confident…and then the bundle explodes because someone `import prisma from '@prisma/client'` in a component. Builds take minutes. CI nags later. Users see the error first. Meanwhile you're diffing stack traces wondering where your day went.
|
|
4
|
+
|
|
5
|
+
This plugin shortens the feedback loop to zero:
|
|
6
|
+
|
|
7
|
+
- **Write code → see error immediately** - ESLint surfaces violations as you type, not minutes later in a build
|
|
8
|
+
- **Stay in flow** - Fix happens in the same editor where you're working. No context switching, no waiting for bundlers
|
|
9
|
+
- **Builds become boring** - Errors are caught in dev mode, so `npm run build` confirms what you already know instead of surprising you
|
|
10
|
+
- **Productive dev loop** - Write, see feedback, fix, move on. The way it should be.
|
|
11
|
+
|
|
12
|
+
## TL;DR
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add -D eslint-plugin-no-server-imports @typescript-eslint/utils
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// eslint.config.mjs
|
|
20
|
+
import noServerImports from 'eslint-plugin-no-server-imports';
|
|
21
|
+
export default [noServerImports.configs['recommended-next']]; // or 'recommended-astro' / 'recommended-sveltekit'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**What it catches:**
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// ❌ This triggers an error in your editor:
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import { PrismaClient } from '@prisma/client';
|
|
30
|
+
import pino from 'pino';
|
|
31
|
+
|
|
32
|
+
// ✅ This is safe:
|
|
33
|
+
import type { User } from '@prisma/client';
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Done. Blocks `fs`, `prisma`, `pino`, and 100+ server-only modules in client code. Type-only imports are always safe. [Full config options →](#configuration)
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm add -D eslint-plugin-no-server-imports @typescript-eslint/utils
|
|
42
|
+
# npm/yarn/bun work too - ESLint 9+ is the only peer dependency.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// eslint.config.mjs
|
|
47
|
+
import tseslint from 'typescript-eslint';
|
|
48
|
+
import noServerImports from 'eslint-plugin-no-server-imports';
|
|
49
|
+
|
|
50
|
+
export default [
|
|
51
|
+
...tseslint.configs.recommended,
|
|
52
|
+
{
|
|
53
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
54
|
+
plugins: { 'no-server-imports': noServerImports },
|
|
55
|
+
rules: {
|
|
56
|
+
'no-server-imports/no-server-imports': 'error',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Using plain JS? Drop the `files` filter. Already on a flat ESLint config? Just keep the `plugins` + `rules` block.
|
|
63
|
+
|
|
64
|
+
### Framework presets (smarter defaults)
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import noServerImports from 'eslint-plugin-no-server-imports';
|
|
68
|
+
|
|
69
|
+
export default [
|
|
70
|
+
noServerImports.configs['recommended-next'],
|
|
71
|
+
// or: noServerImports.configs['recommended-astro']
|
|
72
|
+
// or: noServerImports.configs['recommended-sveltekit']
|
|
73
|
+
];
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each preset ships with tuned `clientFilePatterns` + `serverFilePatterns` for that framework. If you keep `app/` at the repo root in Next.js, add `'**/app/**'` yourself - the default intentionally sticks to `src/app/**` so it doesn't match `myapp/src/...` by accident.
|
|
77
|
+
|
|
78
|
+
## What actually gets flagged
|
|
79
|
+
|
|
80
|
+
### Server-only modules (100+ built-ins and packages)
|
|
81
|
+
|
|
82
|
+
Out of the box the rule blocks:
|
|
83
|
+
|
|
84
|
+
- **Node built-ins**: `fs`, `node:fs`, `path`, `crypto`, `child_process`, `worker_threads`, …
|
|
85
|
+
- **Databases**: `prisma`, `@prisma/client`, `drizzle-orm`, `pg`, `mysql2`, `mongodb`, `kysely`, …
|
|
86
|
+
- **Logging & monitoring**: `pino`, `winston`, `bunyan`, `@appsignal/nodejs`, `dd-trace`, …
|
|
87
|
+
- **Security**: `bcrypt`, `argon2`, `oslo`, `@node-rs/*`
|
|
88
|
+
- **Tooling**: `@swc/core`, `postcss`, `typescript`, `jest`, `playwright`, `sharp`, …
|
|
89
|
+
|
|
90
|
+
Need your own aliases like `@/lib/db` or `@company/logger`? Add them via `serverModules`. Full list lives in [the complete server-only modules list](#server-only-modules).
|
|
91
|
+
|
|
92
|
+
### File awareness (so only the right files are checked)
|
|
93
|
+
|
|
94
|
+
- **Client patterns (default)**: `**/routes/**`, `**/pages/**`, `**/components/**`, `**/islands/**`, `**/src/app/**`
|
|
95
|
+
- **Server patterns (always skipped)**: `**/*.server.ts`, `**/*.server.tsx`, `**/*.server.js`, `**/server/**`, `**/api/**`, `**/_server/**`
|
|
96
|
+
|
|
97
|
+
By default the rule runs in `client-only` mode, meaning "only check paths that look like client code." Flip `mode: 'all-non-server'` if you want every non-server file scanned. `ignoreFiles` beats both modes when you need to silence generated code or test fixtures.
|
|
98
|
+
|
|
99
|
+
### Imports, re-exports, requires… all of it
|
|
100
|
+
|
|
101
|
+
- Static `import` and `export { ... } from` usage of server modules ✅
|
|
102
|
+
- Side-effect imports like `import 'fs'` ✅
|
|
103
|
+
- CommonJS `require('pg')` ✅ (the rule even tracks destructuring)
|
|
104
|
+
- Re-exports (`export * from 'pino'`) ✅
|
|
105
|
+
- Dynamic imports (`await import('pg')`) stay untouched because they're runtime-only.
|
|
106
|
+
|
|
107
|
+
The rule also understands server function scopes. If every reference to a value import stays inside a callback passed to functions like `createServerFn`, `createIsomorphicFn`, `server$`, `action$`, or `loader$`, it's considered safe. Configure `serverFunctionNames` to teach it your own helpers (Nuxt's `defineEventHandler`, Remix loaders, etc.). Next.js Server Actions aren't special-cased - keep the imports inside the `'use server'` function via `await import(...)` and you're good.
|
|
108
|
+
|
|
109
|
+
### What remains allowed
|
|
110
|
+
|
|
111
|
+
| Pattern | Why it passes |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `import type { Logger } from 'pino'` | Type-only imports vanish during compilation. |
|
|
114
|
+
| `export { type Logger } from 'pino'` | Same deal - types don't hit bundles. |
|
|
115
|
+
| `import 'server-only';` | The "server-only" marker opts the whole file out (configurable via `checkServerOnlyMarker`). |
|
|
116
|
+
| Server function scopes | Imports pulled into callbacks from `createServerFn`/`server$`/`action$` stay server-side. |
|
|
117
|
+
| Dynamic imports inside functions | `const { PrismaClient } = await import('@prisma/client');` runs only when the server code executes. |
|
|
118
|
+
|
|
119
|
+
Quick fixes currently offer to insert `import 'server-only';` above your code when that's the right escape hatch. Prefer to keep unused imports warnings in one place? Set `reportUnusedImports: false` and let `no-unused-vars` handle it.
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
All options are optional and can be fully customized. Here's a complete example with all options:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
{
|
|
127
|
+
rules: {
|
|
128
|
+
'no-server-imports/no-server-imports': ['error', {
|
|
129
|
+
// Add custom server-only modules (merged with 100+ defaults)
|
|
130
|
+
serverModules: ['@/lib/logger', 'my-custom-db'],
|
|
131
|
+
|
|
132
|
+
// File patterns for server files (merged with defaults)
|
|
133
|
+
serverFilePatterns: ['**/backend/**', '**/server-utils/**'],
|
|
134
|
+
|
|
135
|
+
// File patterns for client files (replaces defaults if provided)
|
|
136
|
+
clientFilePatterns: ['**/app/**', '**/pages/**', '**/components/**'],
|
|
137
|
+
|
|
138
|
+
// Files to completely ignore
|
|
139
|
+
ignoreFiles: ['**/__tests__/**', '**/*.stories.tsx', '**/generated/**'],
|
|
140
|
+
|
|
141
|
+
// Detection behavior
|
|
142
|
+
checkServerOnlyMarker: true, // Respect 'server-only' import marker
|
|
143
|
+
checkServerFunctions: true, // Allow imports inside server function callbacks
|
|
144
|
+
serverFunctionNames: ['createServerFn', 'server$', 'action$', 'loader$', 'myServerFn'],
|
|
145
|
+
|
|
146
|
+
// Reporting behavior
|
|
147
|
+
reportUnusedImports: true, // Report unused server imports (default: true)
|
|
148
|
+
|
|
149
|
+
// File selection strategy
|
|
150
|
+
mode: 'client-only', // or 'all-non-server' to check all non-server files
|
|
151
|
+
|
|
152
|
+
// Next.js integration
|
|
153
|
+
serverExternalPackages: ['my-native-package'], // Sync with next.config.js
|
|
154
|
+
}],
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Minimal configuration
|
|
160
|
+
|
|
161
|
+
For most projects, the defaults work out of the box. You only need to configure options that differ from defaults:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
{
|
|
165
|
+
rules: {
|
|
166
|
+
'no-server-imports/no-server-imports': ['error', {
|
|
167
|
+
// Only configure what you need to change
|
|
168
|
+
clientFilePatterns: ['**/app/**', '**/pages/**'], // Next.js root app/
|
|
169
|
+
serverModules: ['@/lib/db'], // Your custom modules
|
|
170
|
+
}],
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Option reference
|
|
176
|
+
|
|
177
|
+
#### `serverModules` (optional)
|
|
178
|
+
|
|
179
|
+
- **Type**: `string[]`
|
|
180
|
+
- **Default**: 100+ built-in server-only modules (see list below)
|
|
181
|
+
- **What it does**: Adds additional server-only modules to the detection list. These modules will trigger errors when imported in client code.
|
|
182
|
+
- **Why it exists**: Your project might use custom server-only packages, internal modules with path aliases (like `@/lib/db`), or packages not in the default list. This option lets you extend the detection to cover your specific stack.
|
|
183
|
+
- **Example**: `serverModules: ['@/lib/logger', 'my-custom-db', '../server/utils']`
|
|
184
|
+
- **Note**: Supports path aliases, relative paths, and subpath imports (e.g., `'@/lib/db'` will also match `'@/lib/db/query'`)
|
|
185
|
+
|
|
186
|
+
#### `serverFilePatterns` (optional)
|
|
187
|
+
|
|
188
|
+
- **Type**: `string[]`
|
|
189
|
+
- **Default**: `['**/*.server.ts', '**/*.server.tsx', '**/*.server.js', '**/server/**', '**/api/**', '**/_server/**']`
|
|
190
|
+
- `**/*.server.*` - Files with `.server.` in the name (Astro convention)
|
|
191
|
+
- `**/server/**` - Files in `server/` directories
|
|
192
|
+
- `**/api/**` - API route directories (Next.js, SvelteKit, etc.)
|
|
193
|
+
- `**/_server/**` - Alternative server directory pattern
|
|
194
|
+
- **What it does**: Defines file path patterns that indicate server-only code. Files matching these patterns are completely ignored by the rule.
|
|
195
|
+
- **Why it exists**: Server files can safely import server-only modules, so we skip checking them entirely. This improves performance and prevents false positives.
|
|
196
|
+
- **Example**: `serverFilePatterns: ['**/backend/**', '**/server/**', '**/*.server.ts']`
|
|
197
|
+
- **Note**: Patterns are **merged** with defaults (not replaced). Use glob patterns compatible with `picomatch`.
|
|
198
|
+
|
|
199
|
+
#### `clientFilePatterns` (optional)
|
|
200
|
+
|
|
201
|
+
- **Type**: `string[]`
|
|
202
|
+
- **Default**: `['**/routes/**', '**/pages/**', '**/components/**', '**/islands/**', '**/src/app/**']`
|
|
203
|
+
- `**/routes/**` - Route files (SvelteKit, Remix)
|
|
204
|
+
- `**/pages/**` - Page files (Next.js pages router, Nuxt, Astro)
|
|
205
|
+
- `**/components/**` - Component files (generic)
|
|
206
|
+
- `**/islands/**` - Island components (Astro)
|
|
207
|
+
- `**/src/app/**` - Next.js app directory (when using `src/` folder)
|
|
208
|
+
- **What it does**: Defines file path patterns that indicate client code. Only files matching these patterns are checked for server-only imports (when `mode: 'client-only'`).
|
|
209
|
+
- **Why it exists**: Different frameworks organize client code differently. This lets you configure the rule to match your project structure.
|
|
210
|
+
- **Example**: `clientFilePatterns: ['**/app/**', '**/pages/**', '**/components/**']` for Next.js root-level `app/`
|
|
211
|
+
- **Note**: If provided, this **replaces** the defaults (doesn't merge). Include all patterns you need. For Next.js root-level `app/`, you must explicitly add `'**/app/**'` because the default only covers `'**/src/app/**'` to avoid false matches.
|
|
212
|
+
|
|
213
|
+
#### `ignoreFiles` (optional)
|
|
214
|
+
|
|
215
|
+
- **Type**: `string[]`
|
|
216
|
+
- **Default**: `[]`
|
|
217
|
+
- **What it does**: File patterns to completely ignore, regardless of whether they match client or server patterns.
|
|
218
|
+
- **Why it exists**: Some files like tests, stories, or generated code might need to import server modules for testing/mocking purposes. This provides a clean way to exclude them.
|
|
219
|
+
- **Example**: `ignoreFiles: ['**/__tests__/**', '**/*.test.ts', '**/*.stories.tsx', '**/generated/**']`
|
|
220
|
+
- **Note**: This takes precedence over both `clientFilePatterns` and `serverFilePatterns`.
|
|
221
|
+
|
|
222
|
+
#### `checkServerOnlyMarker` (optional)
|
|
223
|
+
|
|
224
|
+
- **Type**: `boolean`
|
|
225
|
+
- **Default**: `true`
|
|
226
|
+
- **What it does**: When enabled, if a file contains `import 'server-only'` or `require('server-only')`, the entire file is treated as server-only and all imports are allowed.
|
|
227
|
+
- **Why it exists**: The `server-only` package is a common runtime guard. This option respects that marker as an explicit opt-in to server-only behavior, providing an escape hatch for edge cases.
|
|
228
|
+
- **Example**: Set to `false` if you want stricter checking even with the marker, or if you don't use `server-only` at all.
|
|
229
|
+
|
|
230
|
+
#### `checkServerFunctions` (optional)
|
|
231
|
+
|
|
232
|
+
- **Type**: `boolean`
|
|
233
|
+
- **Default**: `true`
|
|
234
|
+
- **What it does**: When enabled, the rule detects server function calls (like `createServerFn()`, `server$()`, etc.) and allows server-only imports if they're **only** used inside the server function callbacks.
|
|
235
|
+
- **Why it exists**: Modern frameworks use server functions/actions that run server-side. Imports used exclusively inside these callbacks are safe because they never execute on the client. This enables the recommended pattern of importing server modules inside server functions.
|
|
236
|
+
- **Example**: Set to `false` if you want to disallow all server imports in client files, even inside server functions (stricter mode).
|
|
237
|
+
|
|
238
|
+
#### `serverFunctionNames` (optional)
|
|
239
|
+
|
|
240
|
+
- **Type**: `string[]`
|
|
241
|
+
- **Default**: `['createServerFn', 'createIsomorphicFn', 'server$', 'action$', 'loader$']`
|
|
242
|
+
- `createServerFn` - TanStack Start
|
|
243
|
+
- `createIsomorphicFn` - TanStack Start (isomorphic functions)
|
|
244
|
+
- `server$` - SolidStart
|
|
245
|
+
- `action$` - Remix
|
|
246
|
+
- `loader$` - Remix
|
|
247
|
+
- **What it does**: Function names that create server-side execution contexts. The rule tracks callbacks passed to these functions and allows server-only imports used exclusively within those callbacks.
|
|
248
|
+
- **Why it exists**: Different frameworks use different function names for server actions. This lets you configure the rule to recognize your framework's patterns (e.g., Nuxt's `defineEventHandler`, Remix's `action$`/`loader$`).
|
|
249
|
+
- **Example**: `serverFunctionNames: ['createServerFn', 'server$', 'defineEventHandler', 'myCustomServerFn']`
|
|
250
|
+
- **Note**: The rule detects both direct calls (`createServerFn()`) and chained calls (`createServerFn().handler()`). It tracks where imports are **used**, not just where they're declared. If you provide this option, it **replaces** the defaults (doesn't merge), so include all function names you need.
|
|
251
|
+
|
|
252
|
+
#### `reportUnusedImports` (optional)
|
|
253
|
+
|
|
254
|
+
- **Type**: `boolean`
|
|
255
|
+
- **Default**: `true`
|
|
256
|
+
- **What it does**: When `true`, reports server-only imports even if they're never used in the file. When `false`, only reports imports that are actually referenced.
|
|
257
|
+
- **Why it exists**: Some teams prefer to let ESLint's `no-unused-vars` rule handle unused imports. Setting this to `false` avoids duplicate warnings and lets you use `no-unused-vars` for all unused imports consistently.
|
|
258
|
+
- **Example**: Set to `false` if you want `no-unused-vars` to handle unused imports, or `true` if you want this rule to catch unused server imports specifically.
|
|
259
|
+
|
|
260
|
+
#### `mode` (optional)
|
|
261
|
+
|
|
262
|
+
- **Type**: `'client-only' | 'all-non-server'`
|
|
263
|
+
- **Default**: `'client-only'`
|
|
264
|
+
- **What it does**: Controls which files are checked:
|
|
265
|
+
- `'client-only'`: Only files matching `clientFilePatterns` are checked
|
|
266
|
+
- `'all-non-server'`: All files are checked except those matching `serverFilePatterns` or `ignoreFiles`
|
|
267
|
+
- **Why it exists**: Some projects have files that aren't explicitly client code but also shouldn't import server modules (e.g., shared utilities, config files). `'all-non-server'` mode catches server imports in these ambiguous files.
|
|
268
|
+
- **Example**: Use `'all-non-server'` if you want comprehensive checking across your entire codebase, not just known client files.
|
|
269
|
+
|
|
270
|
+
#### `serverExternalPackages` (optional)
|
|
271
|
+
|
|
272
|
+
- **Type**: `string[]`
|
|
273
|
+
- **Default**: `[]`
|
|
274
|
+
- **What it does**: Merges Next.js `serverExternalPackages` configuration into `serverModules`. Packages listed here are treated as server-only.
|
|
275
|
+
- **Why it exists**: Next.js has a `serverExternalPackages` config option that marks packages that shouldn't be bundled for the client. This option lets you sync that configuration with the ESLint rule, ensuring consistency between your Next.js config and linting.
|
|
276
|
+
- **Example**: `serverExternalPackages: ['my-native-package', '@my-org/server-lib']` (typically read from `next.config.js`)
|
|
277
|
+
- **Note**: This is merged with `serverModules`, so you can use both options together. Primarily useful for Next.js projects.
|
|
278
|
+
|
|
279
|
+
### Common tweaks
|
|
280
|
+
|
|
281
|
+
- **Next.js root `app/`**: `clientFilePatterns: ['**/app/**', '**/pages/**', '**/components/**']`
|
|
282
|
+
- **Next.js with serverExternalPackages**: Use `serverExternalPackages` option to sync with your `next.config.js`
|
|
283
|
+
- **Check all files**: `mode: 'all-non-server'` - checks every file except server patterns
|
|
284
|
+
- **Let `no-unused-vars` handle unused imports**: `reportUnusedImports: false`
|
|
285
|
+
- **Astro**: `clientFilePatterns: ['**/pages/**', '**/components/**', '**/islands/**']`, `serverFilePatterns: ['**/*.server.ts', '**/server/**']`
|
|
286
|
+
- **SvelteKit**: Check out the monorepo examples - we've got working configs for all three frameworks.
|
|
287
|
+
- **Custom infra**: add in-house modules to `serverModules` (supports aliases like `@/lib/db` and subpaths).
|
|
288
|
+
|
|
289
|
+
### Next.js integration
|
|
290
|
+
|
|
291
|
+
If you're using Next.js's `serverExternalPackages` in `next.config.js`, you can sync it with this rule:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
// eslint.config.mjs
|
|
295
|
+
import nextConfig from './next.config.js';
|
|
296
|
+
|
|
297
|
+
export default [
|
|
298
|
+
{
|
|
299
|
+
rules: {
|
|
300
|
+
'no-server-imports/no-server-imports': ['error', {
|
|
301
|
+
serverExternalPackages: nextConfig.serverExternalPackages || [],
|
|
302
|
+
clientFilePatterns: ['**/app/**', '**/pages/**', '**/components/**'],
|
|
303
|
+
}],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
];
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Server-only modules
|
|
310
|
+
|
|
311
|
+
Complete list of server-only modules detected by default:
|
|
312
|
+
|
|
313
|
+
- **Logging**: `pino`, `pino-pretty`, `pino-roll`, `winston`, `bunyan`
|
|
314
|
+
- **Databases**: `better-sqlite3`, `pg`, `mysql2`, `mongodb`, `mongoose`, `prisma`, `@prisma/client`, `drizzle-orm`, `kysely`, `@libsql/client`, `libsql`, `@mikro-orm/core`, `@mikro-orm/knex`, `sqlite3`, `ravendb`
|
|
315
|
+
- **Node.js built-ins**: `fs`, `node:fs`, `fs/promises`, `node:fs/promises`, `path`, `node:path`, `crypto`, `node:crypto`, `child_process`, `node:child_process`, `os`, `node:os`, `net`, `node:net`, `dns`, `node:dns`, `cluster`, `node:cluster`, `worker_threads`, `node:worker_threads`
|
|
316
|
+
- **Authentication & Security**: `argon2`, `@node-rs/argon2`, `bcrypt`, `@node-rs/bcrypt`, `oslo`
|
|
317
|
+
- **AWS SDK**: `@aws-sdk/client-s3`, `@aws-sdk/s3-presigned-post`, `aws-crt`
|
|
318
|
+
- **Monitoring & Observability**: `@appsignal/nodejs`, `@highlight-run/node`, `@sentry/profiling-node`, `dd-trace`, `newrelic`, `@statsig/statsig-node-core`
|
|
319
|
+
- **AI & ML**: `@huggingface/transformers`, `@xenova/transformers`, `chromadb-default-embed`, `onnxruntime-node`
|
|
320
|
+
- **Blockchain**: `@blockfrost/blockfrost-js`, `@jpg-store/lucid-cardano`
|
|
321
|
+
- **Build Tools & Compilers**: `@swc/core`, `autoprefixer`, `postcss`, `prettier`, `typescript`, `ts-node`, `ts-morph`, `webpack`, `eslint`
|
|
322
|
+
- **Testing**: `cypress`, `jest`, `playwright`, `playwright-core`, `puppeteer`, `puppeteer-core`
|
|
323
|
+
- **Browser Automation**: `@sparticuz/chromium`, `@sparticuz/chromium-min`
|
|
324
|
+
- **Content & Document Generation**: `@react-pdf/renderer`, `mdx-bundler`, `next-mdx-remote`, `next-seo`, `shiki`, `vscode-oniguruma`
|
|
325
|
+
- **Image Processing**: `canvas`, `sharp`
|
|
326
|
+
- **Utilities**: `config`, `keyv`, `node-cron`, `rimraf`, `thread-stream`
|
|
327
|
+
- **Runtime & Framework**: `@alinea/generated`, `@zenstackhq/runtime`, `express`, `firebase-admin`, `htmlrewriter`
|
|
328
|
+
- **Native Node.js addons**: `cpu-features`, `isolated-vm`, `node-pty`, `node-web-audio-api`, `websocket`, `zeromq`
|
|
329
|
+
- **Module System Patchers**: `import-in-the-middle`, `require-in-the-middle`
|
|
330
|
+
- **DOM & Browser APIs**: `jsdom`
|
|
331
|
+
|
|
332
|
+
The rule also detects subpath imports (e.g., `fs/promises` matches `fs`, `@prisma/client/query` matches `@prisma/client`).
|
|
333
|
+
|
|
334
|
+
## Behavioral summary
|
|
335
|
+
|
|
336
|
+
Quick reference for what triggers what:
|
|
337
|
+
|
|
338
|
+
| Import style | Bundled? | Rule result |
|
|
339
|
+
| --- | --- | --- |
|
|
340
|
+
| `import pino from 'pino'` | Yes | ❌ error|
|
|
341
|
+
| `const fs = require('fs')` | Yes | ❌ error |
|
|
342
|
+
| `export { default } from 'pino'` | Yes | ❌ error |
|
|
343
|
+
| `import type { Logger } from 'pino'` | No | ✅ allowed |
|
|
344
|
+
| `import { type Logger } from 'pino'` | No | ✅ allowed |
|
|
345
|
+
| `export { type Logger } from 'pino'` | No | ✅ allowed |
|
|
346
|
+
| `await import('pino')` inside function | Lazy | ✅ allowed |
|
|
347
|
+
|
|
348
|
+
## Quick fix suggestions
|
|
349
|
+
|
|
350
|
+
When the rule detects a violation, it can offer to insert `import 'server-only';` at the top of the file. That keeps the current file marked as server-only without touching any other logic. If you'd rather restructure things (move the code, switch to a server action, or wrap it in an `await import(...)`), decline the suggestion and do it manually - ESLint simply points to the exact import and lets you pick the right pattern.
|
|
351
|
+
|
|
352
|
+
> **Note**: Suggestions rely on ESLint's `hasSuggestions` API, so they're opt-in. Nothing changes until you accept the fix.
|
|
353
|
+
|
|
354
|
+
## VS Code extension
|
|
355
|
+
|
|
356
|
+
For an enhanced experience, install the companion VS Code extension:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
code --install-extension jagreehal.vscode-no-server-imports
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The extension provides:
|
|
363
|
+
|
|
364
|
+
- **Status bar indicator** - Shows detected framework (Next.js, Astro, SvelteKit) with icon
|
|
365
|
+
- **Show Status command** - Displays framework info and quick links to config/docs
|
|
366
|
+
- **Detect Framework command** - Re-scans the workspace for framework detection
|
|
367
|
+
- **Open Documentation command** - Opens framework-specific documentation
|
|
368
|
+
|
|
369
|
+
The extension works alongside the [ESLint VS Code extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), which handles the actual linting.
|
|
370
|
+
|
|
371
|
+
## FAQ
|
|
372
|
+
|
|
373
|
+
**Does this replace the `server-only` runtime guard?**
|
|
374
|
+
|
|
375
|
+
No - keep it for defense in depth. This rule stops mistakes earlier; the runtime guard still protects you if someone bypasses linting. Think of it like wearing both a seatbelt and having airbags.
|
|
376
|
+
|
|
377
|
+
**Will it slow down ESLint?**
|
|
378
|
+
|
|
379
|
+
Nope. The rule only inspects files whose path matches the client patterns, and the AST work is limited to imports/exports/server-function calls. Expect negligible overhead compared to `typescript-eslint` itself. We're pretty efficient about this.
|
|
380
|
+
|
|
381
|
+
**How do I allow a specific file?**
|
|
382
|
+
|
|
383
|
+
Use `ignoreFiles` with any glob, or drop a `/* eslint-disable no-server-imports/no-server-imports */` pragma if you absolutely must. But really, try to fix the underlying issue first - that's usually the better path.
|
|
384
|
+
|
|
385
|
+
**What if my framework isn't supported?**
|
|
386
|
+
|
|
387
|
+
We've got defaults for Next.js, Astro, SvelteKit, TanStack Start, Remix, and SolidStart. If your framework uses different patterns, just configure `clientFilePatterns` and `serverFilePatterns` to match your setup. It's pretty flexible.
|
|
388
|
+
|
|
389
|
+
## Contributing & support
|
|
390
|
+
|
|
391
|
+
Issues and feature requests live at [GitHub Issues](https://github.com/jagreehal/eslint-plugin-no-server-imports/issues). Feel free to open a PR if your framework uses a different server function name - we're happy to add sensible defaults.
|
|
392
|
+
|
|
393
|
+
Want to see it in action? Check out the [monorepo examples](../../README.md) with working Next.js, Astro, and SvelteKit setups.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
Stop finding server imports during deploys. Catch them in the editor instead.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Framework Auto-Detection
|
|
5
|
+
* ========================
|
|
6
|
+
* Automatically detects the frontend framework being used and provides
|
|
7
|
+
* optimal default configurations for the no-server-imports rule.
|
|
8
|
+
*/
|
|
9
|
+
type DetectedFramework = 'next' | 'astro' | 'sveltekit' | 'unknown';
|
|
10
|
+
interface FrameworkDefaults {
|
|
11
|
+
clientFilePatterns: string[];
|
|
12
|
+
serverFilePatterns: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Framework-specific default configurations
|
|
16
|
+
*/
|
|
17
|
+
declare const FRAMEWORK_DEFAULTS: Record<DetectedFramework, FrameworkDefaults>;
|
|
18
|
+
/**
|
|
19
|
+
* Detects the framework being used in the project.
|
|
20
|
+
*
|
|
21
|
+
* Detection priority:
|
|
22
|
+
* 1. Config files (most reliable): next.config.*, astro.config.*, svelte.config.*
|
|
23
|
+
* 2. Package.json dependencies: "next", "astro", "@sveltejs/kit"
|
|
24
|
+
*
|
|
25
|
+
* @param filePath - Path to a file or directory in the project (used to find project root)
|
|
26
|
+
* @returns The detected framework or 'unknown'
|
|
27
|
+
*/
|
|
28
|
+
declare function detectFramework(filePath?: string): DetectedFramework;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the default configuration for the detected framework.
|
|
31
|
+
*
|
|
32
|
+
* @param filePath - Path to a file in the project (used for detection)
|
|
33
|
+
* @returns Framework-specific defaults
|
|
34
|
+
*/
|
|
35
|
+
declare function getFrameworkDefaults(filePath?: string): FrameworkDefaults;
|
|
36
|
+
/**
|
|
37
|
+
* Clears the cached framework detection (useful for testing)
|
|
38
|
+
*/
|
|
39
|
+
declare function clearFrameworkCache(): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ESLint Plugin: eslint-plugin-no-server-imports
|
|
43
|
+
* ===============================================
|
|
44
|
+
* Prevents server-only module imports in client code.
|
|
45
|
+
* Catches bundling issues in your editor instead of at build time.
|
|
46
|
+
*
|
|
47
|
+
* @author Jag Reehal [@jagreehal] <jag@jagreehal.com>
|
|
48
|
+
* @license MIT
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** Configuration options for the no-server-imports rule */
|
|
52
|
+
interface RuleOptions {
|
|
53
|
+
/** Additional server-only modules to check (merged with defaults) */
|
|
54
|
+
serverModules?: string[];
|
|
55
|
+
/** Additional server-only file patterns to check (merged with defaults) */
|
|
56
|
+
serverFilePatterns?: string[];
|
|
57
|
+
/** File patterns that indicate client code (overrides defaults if provided) */
|
|
58
|
+
clientFilePatterns?: string[];
|
|
59
|
+
/** File patterns to completely ignore */
|
|
60
|
+
ignoreFiles?: string[];
|
|
61
|
+
/** Whether to check for 'server-only' import marker */
|
|
62
|
+
checkServerOnlyMarker?: boolean;
|
|
63
|
+
/** Whether to check for server function usage (createServerFn, etc.) */
|
|
64
|
+
checkServerFunctions?: boolean;
|
|
65
|
+
/** Server function names to check for */
|
|
66
|
+
serverFunctionNames?: string[];
|
|
67
|
+
/** Whether to report unused server-only imports (default: true) */
|
|
68
|
+
reportUnusedImports?: boolean;
|
|
69
|
+
/** File selection mode: 'client-only' checks only clientFilePatterns, 'all-non-server' checks all except server files */
|
|
70
|
+
mode?: 'client-only' | 'all-non-server';
|
|
71
|
+
/** Next.js serverExternalPackages - merged into serverModules (for Next.js projects) */
|
|
72
|
+
serverExternalPackages?: string[];
|
|
73
|
+
}
|
|
74
|
+
type MessageIds = 'serverOnlyImport' | 'serverOnlyRequire' | 'suggestServerOnlyMarker';
|
|
75
|
+
type Options = [RuleOptions?];
|
|
76
|
+
declare const rule: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
77
|
+
|
|
78
|
+
/** Plugin configuration */
|
|
79
|
+
declare const plugin: {
|
|
80
|
+
readonly rules: {
|
|
81
|
+
readonly 'no-server-imports': ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
82
|
+
};
|
|
83
|
+
readonly configs: {
|
|
84
|
+
/**
|
|
85
|
+
* Recommended config - uses sensible defaults that work across frameworks.
|
|
86
|
+
* For framework-specific optimizations, use one of the framework presets.
|
|
87
|
+
*/
|
|
88
|
+
readonly recommended: {
|
|
89
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
90
|
+
readonly rules: {
|
|
91
|
+
readonly 'no-server-imports/no-server-imports': "error";
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Next.js optimized configuration.
|
|
96
|
+
* Best for Next.js App Router and Pages Router projects.
|
|
97
|
+
*/
|
|
98
|
+
readonly 'recommended-next': {
|
|
99
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
100
|
+
readonly rules: {
|
|
101
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
102
|
+
readonly clientFilePatterns: string[];
|
|
103
|
+
readonly serverFilePatterns: string[];
|
|
104
|
+
}];
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Astro optimized configuration.
|
|
109
|
+
* Best for Astro projects with islands architecture.
|
|
110
|
+
*/
|
|
111
|
+
readonly 'recommended-astro': {
|
|
112
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
113
|
+
readonly rules: {
|
|
114
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
115
|
+
readonly clientFilePatterns: string[];
|
|
116
|
+
readonly serverFilePatterns: string[];
|
|
117
|
+
}];
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* SvelteKit optimized configuration.
|
|
122
|
+
* Best for SvelteKit projects.
|
|
123
|
+
*/
|
|
124
|
+
readonly 'recommended-sveltekit': {
|
|
125
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
126
|
+
readonly rules: {
|
|
127
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
128
|
+
readonly clientFilePatterns: string[];
|
|
129
|
+
readonly serverFilePatterns: string[];
|
|
130
|
+
}];
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
readonly meta: {
|
|
135
|
+
readonly name: "eslint-plugin-no-server-imports";
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export { type DetectedFramework, FRAMEWORK_DEFAULTS, type FrameworkDefaults, type RuleOptions, clearFrameworkCache, plugin as default, detectFramework, getFrameworkDefaults, rule };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Framework Auto-Detection
|
|
5
|
+
* ========================
|
|
6
|
+
* Automatically detects the frontend framework being used and provides
|
|
7
|
+
* optimal default configurations for the no-server-imports rule.
|
|
8
|
+
*/
|
|
9
|
+
type DetectedFramework = 'next' | 'astro' | 'sveltekit' | 'unknown';
|
|
10
|
+
interface FrameworkDefaults {
|
|
11
|
+
clientFilePatterns: string[];
|
|
12
|
+
serverFilePatterns: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Framework-specific default configurations
|
|
16
|
+
*/
|
|
17
|
+
declare const FRAMEWORK_DEFAULTS: Record<DetectedFramework, FrameworkDefaults>;
|
|
18
|
+
/**
|
|
19
|
+
* Detects the framework being used in the project.
|
|
20
|
+
*
|
|
21
|
+
* Detection priority:
|
|
22
|
+
* 1. Config files (most reliable): next.config.*, astro.config.*, svelte.config.*
|
|
23
|
+
* 2. Package.json dependencies: "next", "astro", "@sveltejs/kit"
|
|
24
|
+
*
|
|
25
|
+
* @param filePath - Path to a file or directory in the project (used to find project root)
|
|
26
|
+
* @returns The detected framework or 'unknown'
|
|
27
|
+
*/
|
|
28
|
+
declare function detectFramework(filePath?: string): DetectedFramework;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the default configuration for the detected framework.
|
|
31
|
+
*
|
|
32
|
+
* @param filePath - Path to a file in the project (used for detection)
|
|
33
|
+
* @returns Framework-specific defaults
|
|
34
|
+
*/
|
|
35
|
+
declare function getFrameworkDefaults(filePath?: string): FrameworkDefaults;
|
|
36
|
+
/**
|
|
37
|
+
* Clears the cached framework detection (useful for testing)
|
|
38
|
+
*/
|
|
39
|
+
declare function clearFrameworkCache(): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ESLint Plugin: eslint-plugin-no-server-imports
|
|
43
|
+
* ===============================================
|
|
44
|
+
* Prevents server-only module imports in client code.
|
|
45
|
+
* Catches bundling issues in your editor instead of at build time.
|
|
46
|
+
*
|
|
47
|
+
* @author Jag Reehal [@jagreehal] <jag@jagreehal.com>
|
|
48
|
+
* @license MIT
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** Configuration options for the no-server-imports rule */
|
|
52
|
+
interface RuleOptions {
|
|
53
|
+
/** Additional server-only modules to check (merged with defaults) */
|
|
54
|
+
serverModules?: string[];
|
|
55
|
+
/** Additional server-only file patterns to check (merged with defaults) */
|
|
56
|
+
serverFilePatterns?: string[];
|
|
57
|
+
/** File patterns that indicate client code (overrides defaults if provided) */
|
|
58
|
+
clientFilePatterns?: string[];
|
|
59
|
+
/** File patterns to completely ignore */
|
|
60
|
+
ignoreFiles?: string[];
|
|
61
|
+
/** Whether to check for 'server-only' import marker */
|
|
62
|
+
checkServerOnlyMarker?: boolean;
|
|
63
|
+
/** Whether to check for server function usage (createServerFn, etc.) */
|
|
64
|
+
checkServerFunctions?: boolean;
|
|
65
|
+
/** Server function names to check for */
|
|
66
|
+
serverFunctionNames?: string[];
|
|
67
|
+
/** Whether to report unused server-only imports (default: true) */
|
|
68
|
+
reportUnusedImports?: boolean;
|
|
69
|
+
/** File selection mode: 'client-only' checks only clientFilePatterns, 'all-non-server' checks all except server files */
|
|
70
|
+
mode?: 'client-only' | 'all-non-server';
|
|
71
|
+
/** Next.js serverExternalPackages - merged into serverModules (for Next.js projects) */
|
|
72
|
+
serverExternalPackages?: string[];
|
|
73
|
+
}
|
|
74
|
+
type MessageIds = 'serverOnlyImport' | 'serverOnlyRequire' | 'suggestServerOnlyMarker';
|
|
75
|
+
type Options = [RuleOptions?];
|
|
76
|
+
declare const rule: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
77
|
+
|
|
78
|
+
/** Plugin configuration */
|
|
79
|
+
declare const plugin: {
|
|
80
|
+
readonly rules: {
|
|
81
|
+
readonly 'no-server-imports': ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
82
|
+
};
|
|
83
|
+
readonly configs: {
|
|
84
|
+
/**
|
|
85
|
+
* Recommended config - uses sensible defaults that work across frameworks.
|
|
86
|
+
* For framework-specific optimizations, use one of the framework presets.
|
|
87
|
+
*/
|
|
88
|
+
readonly recommended: {
|
|
89
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
90
|
+
readonly rules: {
|
|
91
|
+
readonly 'no-server-imports/no-server-imports': "error";
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Next.js optimized configuration.
|
|
96
|
+
* Best for Next.js App Router and Pages Router projects.
|
|
97
|
+
*/
|
|
98
|
+
readonly 'recommended-next': {
|
|
99
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
100
|
+
readonly rules: {
|
|
101
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
102
|
+
readonly clientFilePatterns: string[];
|
|
103
|
+
readonly serverFilePatterns: string[];
|
|
104
|
+
}];
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Astro optimized configuration.
|
|
109
|
+
* Best for Astro projects with islands architecture.
|
|
110
|
+
*/
|
|
111
|
+
readonly 'recommended-astro': {
|
|
112
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
113
|
+
readonly rules: {
|
|
114
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
115
|
+
readonly clientFilePatterns: string[];
|
|
116
|
+
readonly serverFilePatterns: string[];
|
|
117
|
+
}];
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* SvelteKit optimized configuration.
|
|
122
|
+
* Best for SvelteKit projects.
|
|
123
|
+
*/
|
|
124
|
+
readonly 'recommended-sveltekit': {
|
|
125
|
+
readonly plugins: readonly ["no-server-imports"];
|
|
126
|
+
readonly rules: {
|
|
127
|
+
readonly 'no-server-imports/no-server-imports': readonly ["error", {
|
|
128
|
+
readonly clientFilePatterns: string[];
|
|
129
|
+
readonly serverFilePatterns: string[];
|
|
130
|
+
}];
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
readonly meta: {
|
|
135
|
+
readonly name: "eslint-plugin-no-server-imports";
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export { type DetectedFramework, FRAMEWORK_DEFAULTS, type FrameworkDefaults, type RuleOptions, clearFrameworkCache, plugin as default, detectFramework, getFrameworkDefaults, rule };
|