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/dist/index.js ADDED
@@ -0,0 +1,931 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ FRAMEWORK_DEFAULTS: () => FRAMEWORK_DEFAULTS,
34
+ clearFrameworkCache: () => clearFrameworkCache,
35
+ default: () => index_default,
36
+ detectFramework: () => detectFramework,
37
+ getFrameworkDefaults: () => getFrameworkDefaults,
38
+ rule: () => rule
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+ var import_utils = require("@typescript-eslint/utils");
42
+ var import_picomatch = __toESM(require("picomatch"));
43
+
44
+ // src/framework-detection.ts
45
+ var import_node_fs = require("fs");
46
+ var import_node_path = __toESM(require("path"));
47
+ var FRAMEWORK_DEFAULTS = {
48
+ next: {
49
+ clientFilePatterns: [
50
+ "**/app/**",
51
+ "**/src/app/**",
52
+ "**/pages/**",
53
+ "**/components/**"
54
+ ],
55
+ serverFilePatterns: [
56
+ "**/*.server.ts",
57
+ "**/*.server.tsx",
58
+ "**/api/**",
59
+ "**/server/**",
60
+ "**/actions/**"
61
+ ]
62
+ },
63
+ astro: {
64
+ clientFilePatterns: [
65
+ "**/src/pages/**",
66
+ "**/src/components/**",
67
+ "**/src/islands/**"
68
+ ],
69
+ serverFilePatterns: [
70
+ "**/*.server.ts",
71
+ "**/*.server.tsx",
72
+ "**/server/**",
73
+ "**/api/**/*.ts"
74
+ ]
75
+ },
76
+ sveltekit: {
77
+ clientFilePatterns: [
78
+ "**/src/routes/**",
79
+ "**/src/lib/components/**"
80
+ ],
81
+ serverFilePatterns: [
82
+ "**/*.server.ts",
83
+ "**/*.server.js",
84
+ "**/+server.ts",
85
+ "**/+server.js",
86
+ "**/+page.server.ts",
87
+ "**/+layout.server.ts"
88
+ ]
89
+ },
90
+ unknown: {
91
+ clientFilePatterns: [
92
+ "**/routes/**",
93
+ "**/pages/**",
94
+ "**/components/**",
95
+ "**/islands/**",
96
+ "**/src/app/**"
97
+ ],
98
+ serverFilePatterns: [
99
+ "**/*.server.ts",
100
+ "**/*.server.tsx",
101
+ "**/*.server.js",
102
+ "**/server/**",
103
+ "**/api/**",
104
+ "**/_server/**"
105
+ ]
106
+ }
107
+ };
108
+ function findProjectRoot(startDir) {
109
+ let current = startDir;
110
+ while (current !== import_node_path.default.dirname(current)) {
111
+ if ((0, import_node_fs.existsSync)(import_node_path.default.join(current, "package.json"))) {
112
+ return current;
113
+ }
114
+ current = import_node_path.default.dirname(current);
115
+ }
116
+ return null;
117
+ }
118
+ function configExists(dir, baseName) {
119
+ const extensions = [".js", ".mjs", ".cjs", ".ts", ".mts"];
120
+ return extensions.some((ext) => (0, import_node_fs.existsSync)(import_node_path.default.join(dir, `${baseName}${ext}`)));
121
+ }
122
+ function checkPackageJsonDependencies(projectRoot) {
123
+ const packageJsonPath = import_node_path.default.join(projectRoot, "package.json");
124
+ if (!(0, import_node_fs.existsSync)(packageJsonPath)) {
125
+ return "unknown";
126
+ }
127
+ try {
128
+ const content = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
129
+ const pkg = JSON.parse(content);
130
+ const allDeps = {
131
+ ...pkg.dependencies,
132
+ ...pkg.devDependencies
133
+ };
134
+ if (allDeps["next"]) {
135
+ return "next";
136
+ }
137
+ if (allDeps["@sveltejs/kit"]) {
138
+ return "sveltekit";
139
+ }
140
+ if (allDeps["astro"]) {
141
+ return "astro";
142
+ }
143
+ } catch {
144
+ }
145
+ return "unknown";
146
+ }
147
+ function checkConfigFiles(projectRoot) {
148
+ if (configExists(projectRoot, "next.config")) {
149
+ return "next";
150
+ }
151
+ if (configExists(projectRoot, "svelte.config")) {
152
+ return "sveltekit";
153
+ }
154
+ if (configExists(projectRoot, "astro.config")) {
155
+ return "astro";
156
+ }
157
+ return "unknown";
158
+ }
159
+ var cachedFramework = null;
160
+ var cachedProjectRoot = null;
161
+ function isDirectory(filePath) {
162
+ try {
163
+ return (0, import_node_fs.existsSync)(filePath) && (0, import_node_fs.statSync)(filePath).isDirectory();
164
+ } catch {
165
+ return false;
166
+ }
167
+ }
168
+ function detectFramework(filePath) {
169
+ let startDir;
170
+ if (!filePath) {
171
+ startDir = process.cwd();
172
+ } else if (isDirectory(filePath)) {
173
+ startDir = filePath;
174
+ } else {
175
+ startDir = import_node_path.default.dirname(filePath);
176
+ }
177
+ const projectRoot = findProjectRoot(startDir) || process.cwd();
178
+ if (cachedProjectRoot === projectRoot && cachedFramework !== null) {
179
+ return cachedFramework;
180
+ }
181
+ let framework = checkConfigFiles(projectRoot);
182
+ if (framework === "unknown") {
183
+ framework = checkPackageJsonDependencies(projectRoot);
184
+ }
185
+ cachedProjectRoot = projectRoot;
186
+ cachedFramework = framework;
187
+ return framework;
188
+ }
189
+ function getFrameworkDefaults(filePath) {
190
+ const framework = detectFramework(filePath);
191
+ return FRAMEWORK_DEFAULTS[framework];
192
+ }
193
+ function clearFrameworkCache() {
194
+ cachedFramework = null;
195
+ cachedProjectRoot = null;
196
+ }
197
+
198
+ // src/index.ts
199
+ var DEFAULT_SERVER_MODULES = [
200
+ // Logging
201
+ "pino",
202
+ "pino-pretty",
203
+ "pino-roll",
204
+ "winston",
205
+ "bunyan",
206
+ // Database
207
+ "better-sqlite3",
208
+ "pg",
209
+ "mysql2",
210
+ "mongodb",
211
+ "mongoose",
212
+ "prisma",
213
+ "@prisma/client",
214
+ "drizzle-orm",
215
+ "kysely",
216
+ "@libsql/client",
217
+ "libsql",
218
+ "@mikro-orm/core",
219
+ "@mikro-orm/knex",
220
+ "sqlite3",
221
+ // Authentication & Security
222
+ "argon2",
223
+ "@node-rs/argon2",
224
+ "bcrypt",
225
+ "@node-rs/bcrypt",
226
+ "oslo",
227
+ // AWS SDK
228
+ "@aws-sdk/client-s3",
229
+ "@aws-sdk/s3-presigned-post",
230
+ "aws-crt",
231
+ // Monitoring & Observability
232
+ "@appsignal/nodejs",
233
+ "@highlight-run/node",
234
+ "@sentry/profiling-node",
235
+ "dd-trace",
236
+ "newrelic",
237
+ "@statsig/statsig-node-core",
238
+ // AI & ML
239
+ "@huggingface/transformers",
240
+ "@xenova/transformers",
241
+ "chromadb-default-embed",
242
+ "onnxruntime-node",
243
+ // Blockchain
244
+ "@blockfrost/blockfrost-js",
245
+ "@jpg-store/lucid-cardano",
246
+ // Build Tools & Compilers
247
+ "@swc/core",
248
+ "autoprefixer",
249
+ "postcss",
250
+ "prettier",
251
+ "typescript",
252
+ "ts-node",
253
+ "ts-morph",
254
+ "webpack",
255
+ "eslint",
256
+ // Testing
257
+ "cypress",
258
+ "jest",
259
+ "playwright",
260
+ "playwright-core",
261
+ "puppeteer",
262
+ "puppeteer-core",
263
+ // Browser Automation
264
+ "@sparticuz/chromium",
265
+ "@sparticuz/chromium-min",
266
+ // Content & Document Generation
267
+ "@react-pdf/renderer",
268
+ "mdx-bundler",
269
+ "next-mdx-remote",
270
+ "next-seo",
271
+ "shiki",
272
+ "vscode-oniguruma",
273
+ // Image Processing
274
+ "canvas",
275
+ "sharp",
276
+ // Utilities
277
+ "config",
278
+ "keyv",
279
+ "node-cron",
280
+ "rimraf",
281
+ "thread-stream",
282
+ // Runtime & Framework
283
+ "@alinea/generated",
284
+ "@zenstackhq/runtime",
285
+ "express",
286
+ "firebase-admin",
287
+ "htmlrewriter",
288
+ // Native Node.js addons
289
+ "cpu-features",
290
+ "isolated-vm",
291
+ "node-pty",
292
+ "node-web-audio-api",
293
+ "websocket",
294
+ "zeromq",
295
+ // Module System Patchers
296
+ "import-in-the-middle",
297
+ "require-in-the-middle",
298
+ // DOM & Browser APIs
299
+ "jsdom",
300
+ // Database (additional)
301
+ "ravendb",
302
+ // Node.js built-ins
303
+ "fs",
304
+ "node:fs",
305
+ "fs/promises",
306
+ "node:fs/promises",
307
+ "path",
308
+ "node:path",
309
+ "crypto",
310
+ "node:crypto",
311
+ "child_process",
312
+ "node:child_process",
313
+ "os",
314
+ "node:os",
315
+ "net",
316
+ "node:net",
317
+ "dns",
318
+ "node:dns",
319
+ "cluster",
320
+ "node:cluster",
321
+ "worker_threads",
322
+ "node:worker_threads"
323
+ ];
324
+ var DEFAULT_SERVER_FILE_PATTERNS = [
325
+ "**/*.server.ts",
326
+ "**/*.server.tsx",
327
+ "**/*.server.js",
328
+ "**/server/**",
329
+ "**/api/**",
330
+ "**/_server/**"
331
+ ];
332
+ var DEFAULT_CLIENT_FILE_PATTERNS = [
333
+ "**/routes/**",
334
+ "**/pages/**",
335
+ "**/components/**",
336
+ "**/islands/**",
337
+ "**/src/app/**"
338
+ // Next.js app directory (in src)
339
+ // Note: For Next.js root-level app/, users should configure clientFilePatterns
340
+ // We can't use '**/app/**' as it's too broad and matches paths like '/myapp/src/...'
341
+ ];
342
+ var createRule = import_utils.ESLintUtils.RuleCreator(
343
+ () => "https://github.com/jagreehal/eslint-plugin-no-server-imports#readme"
344
+ );
345
+ function hasValueImportSpecifiers(node) {
346
+ if (node.importKind === "type") {
347
+ return false;
348
+ }
349
+ if (node.specifiers.length === 0) {
350
+ return true;
351
+ }
352
+ return node.specifiers.some((specifier) => {
353
+ if (specifier.type === import_utils.AST_NODE_TYPES.ImportDefaultSpecifier) {
354
+ return true;
355
+ }
356
+ if (specifier.type === import_utils.AST_NODE_TYPES.ImportNamespaceSpecifier) {
357
+ return true;
358
+ }
359
+ if (specifier.type === import_utils.AST_NODE_TYPES.ImportSpecifier) {
360
+ return specifier.importKind !== "type";
361
+ }
362
+ return true;
363
+ });
364
+ }
365
+ function hasValueExportSpecifiers(node) {
366
+ if (node.exportKind === "type") {
367
+ return false;
368
+ }
369
+ if (node.specifiers.length === 0) {
370
+ return true;
371
+ }
372
+ return node.specifiers.some((specifier) => {
373
+ return specifier.exportKind !== "type";
374
+ });
375
+ }
376
+ function getValueImportNames(node) {
377
+ if (node.importKind === "type") {
378
+ return [];
379
+ }
380
+ const names = [];
381
+ for (const specifier of node.specifiers) {
382
+ switch (specifier.type) {
383
+ case import_utils.AST_NODE_TYPES.ImportDefaultSpecifier:
384
+ case import_utils.AST_NODE_TYPES.ImportNamespaceSpecifier: {
385
+ names.push(specifier.local.name);
386
+ break;
387
+ }
388
+ case import_utils.AST_NODE_TYPES.ImportSpecifier: {
389
+ if (specifier.importKind !== "type") {
390
+ names.push(specifier.local.name);
391
+ }
392
+ break;
393
+ }
394
+ }
395
+ }
396
+ return names;
397
+ }
398
+ function isNodeInsideScope(node, scopeNode) {
399
+ let current = node;
400
+ while (current) {
401
+ if (current === scopeNode) {
402
+ return true;
403
+ }
404
+ current = current.parent;
405
+ }
406
+ return false;
407
+ }
408
+ function isDirectivePrologue(node) {
409
+ return node.type === import_utils.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils.AST_NODE_TYPES.Literal && typeof node.expression.value === "string";
410
+ }
411
+ function hasUseClientDirective(sourceCode) {
412
+ const ast = sourceCode.ast;
413
+ if (!ast.body || ast.body.length === 0) return false;
414
+ for (const node of ast.body) {
415
+ if (!isDirectivePrologue(node)) break;
416
+ const expr = node.expression;
417
+ if (expr.value === "use client") return true;
418
+ }
419
+ return false;
420
+ }
421
+ function findServerOnlyMarkerInsertNode(sourceCode) {
422
+ const ast = sourceCode.ast;
423
+ if (!ast.body || ast.body.length === 0) {
424
+ return null;
425
+ }
426
+ let insertIndex = 0;
427
+ for (const node of ast.body) {
428
+ if (isDirectivePrologue(node)) {
429
+ insertIndex++;
430
+ } else {
431
+ break;
432
+ }
433
+ }
434
+ if (insertIndex >= ast.body.length) {
435
+ return null;
436
+ }
437
+ return ast.body[insertIndex];
438
+ }
439
+ function createServerOnlyMarkerSuggestion(sourceCode) {
440
+ if (hasUseClientDirective(sourceCode)) {
441
+ return [];
442
+ }
443
+ const text = sourceCode.getText();
444
+ const hasMarker = /^import\s+['"]server-only['"]/m.test(text);
445
+ return [
446
+ {
447
+ messageId: "suggestServerOnlyMarker",
448
+ data: {},
449
+ fix: (fixer) => {
450
+ if (hasMarker) {
451
+ return fixer.insertTextBeforeRange([0, 0], "");
452
+ }
453
+ const insertNode = findServerOnlyMarkerInsertNode(sourceCode);
454
+ if (insertNode) {
455
+ return fixer.insertTextBefore(insertNode, "import 'server-only';\n");
456
+ }
457
+ return fixer.insertTextBeforeRange([0, 0], "import 'server-only';\n");
458
+ }
459
+ }
460
+ ];
461
+ }
462
+ function createImportSuggestions(sourceCode) {
463
+ return createServerOnlyMarkerSuggestion(sourceCode);
464
+ }
465
+ function createSideEffectImportSuggestions(_moduleName, sourceCode) {
466
+ return createServerOnlyMarkerSuggestion(sourceCode);
467
+ }
468
+ function createRequireSuggestions(sourceCode) {
469
+ return createServerOnlyMarkerSuggestion(sourceCode);
470
+ }
471
+ function createReexportSuggestions(sourceCode) {
472
+ return createServerOnlyMarkerSuggestion(sourceCode);
473
+ }
474
+ var rule = createRule({
475
+ name: "no-server-imports",
476
+ meta: {
477
+ type: "problem",
478
+ docs: {
479
+ description: "Prevent server-only module imports in client code"
480
+ },
481
+ schema: [
482
+ {
483
+ type: "object",
484
+ properties: {
485
+ serverModules: {
486
+ type: "array",
487
+ items: { type: "string" },
488
+ description: "Additional server-only modules to check"
489
+ },
490
+ serverFilePatterns: {
491
+ type: "array",
492
+ items: { type: "string" },
493
+ description: "Additional server-only file patterns"
494
+ },
495
+ clientFilePatterns: {
496
+ type: "array",
497
+ items: { type: "string" },
498
+ description: "File patterns that indicate client code"
499
+ },
500
+ ignoreFiles: {
501
+ type: "array",
502
+ items: { type: "string" },
503
+ description: "File patterns to ignore"
504
+ },
505
+ checkServerOnlyMarker: {
506
+ type: "boolean",
507
+ description: "Check for 'server-only' import marker"
508
+ },
509
+ checkServerFunctions: {
510
+ type: "boolean",
511
+ description: "Check for server function usage"
512
+ },
513
+ serverFunctionNames: {
514
+ type: "array",
515
+ items: { type: "string" },
516
+ description: "Server function names to check for"
517
+ },
518
+ reportUnusedImports: {
519
+ type: "boolean",
520
+ description: "Whether to report unused server-only imports (default: true)"
521
+ },
522
+ mode: {
523
+ type: "string",
524
+ enum: ["client-only", "all-non-server"],
525
+ description: "File selection mode"
526
+ },
527
+ serverExternalPackages: {
528
+ type: "array",
529
+ items: { type: "string" },
530
+ description: "Next.js serverExternalPackages to treat as server-only"
531
+ }
532
+ },
533
+ additionalProperties: false
534
+ }
535
+ ],
536
+ hasSuggestions: true,
537
+ messages: {
538
+ serverOnlyImport: 'Server-only module "{{module}}" imported in client code. Use dynamic import in a server function: const mod = await import("{{module}}")',
539
+ serverOnlyRequire: 'Server-only module "{{module}}" required in client code. Use dynamic import in a server function instead.',
540
+ suggestServerOnlyMarker: "Add import 'server-only' to mark this file as server-only"
541
+ }
542
+ },
543
+ defaultOptions: [{}],
544
+ create(context, [options = {}]) {
545
+ const serverModules = [
546
+ ...DEFAULT_SERVER_MODULES,
547
+ ...options.serverModules || [],
548
+ ...options.serverExternalPackages || []
549
+ // Next.js integration
550
+ ];
551
+ const serverFilePatterns = [
552
+ ...DEFAULT_SERVER_FILE_PATTERNS,
553
+ ...options.serverFilePatterns || []
554
+ ];
555
+ const clientFilePatterns = options.clientFilePatterns || DEFAULT_CLIENT_FILE_PATTERNS;
556
+ const ignoreFiles = options.ignoreFiles || [];
557
+ const checkServerOnlyMarker = options.checkServerOnlyMarker ?? true;
558
+ const checkServerFunctions = options.checkServerFunctions ?? true;
559
+ const serverFunctionNames = options.serverFunctionNames || [
560
+ "createServerFn",
561
+ "createIsomorphicFn",
562
+ "server$",
563
+ "action$",
564
+ "loader$"
565
+ ];
566
+ const reportUnusedImports = options.reportUnusedImports ?? true;
567
+ const mode = options.mode || "client-only";
568
+ const serverModuleSet = new Set(serverModules);
569
+ const rawFilename = context.getFilename();
570
+ const filename = rawFilename.replaceAll("\\", "/");
571
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
572
+ let hasServerOnlyImport = false;
573
+ const serverOnlyImports = [];
574
+ const serverFunctionScopes = /* @__PURE__ */ new Set();
575
+ function shouldIgnoreFile() {
576
+ if (ignoreFiles.length > 0) {
577
+ const isMatch = (0, import_picomatch.default)(ignoreFiles);
578
+ if (isMatch(filename)) {
579
+ return true;
580
+ }
581
+ }
582
+ return false;
583
+ }
584
+ function isServerFile() {
585
+ const isMatch = (0, import_picomatch.default)(serverFilePatterns);
586
+ return isMatch(filename);
587
+ }
588
+ function isClientFile() {
589
+ const isMatch = (0, import_picomatch.default)(clientFilePatterns);
590
+ return isMatch(filename);
591
+ }
592
+ function isServerOnlyModule(importSource) {
593
+ if (serverModuleSet.has(importSource)) {
594
+ return true;
595
+ }
596
+ return serverModules.some(
597
+ (mod) => importSource.startsWith(`${mod}/`)
598
+ );
599
+ }
600
+ function findServerFunctionCall(node) {
601
+ if (node.callee.type === import_utils.AST_NODE_TYPES.Identifier && serverFunctionNames.includes(node.callee.name)) {
602
+ return node;
603
+ }
604
+ if (node.callee.type === import_utils.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils.AST_NODE_TYPES.CallExpression) {
605
+ return findServerFunctionCall(node.callee.object);
606
+ }
607
+ return null;
608
+ }
609
+ function extractCallbacksFromChain(node) {
610
+ const callbacks = [];
611
+ let current = node;
612
+ while (current.type === import_utils.AST_NODE_TYPES.CallExpression) {
613
+ for (const arg of current.arguments) {
614
+ if (arg.type === import_utils.AST_NODE_TYPES.ArrowFunctionExpression || arg.type === import_utils.AST_NODE_TYPES.FunctionExpression) {
615
+ callbacks.push(arg);
616
+ }
617
+ }
618
+ if (current.parent?.type === import_utils.AST_NODE_TYPES.MemberExpression && current.parent.parent?.type === import_utils.AST_NODE_TYPES.CallExpression) {
619
+ current = current.parent.parent;
620
+ } else {
621
+ break;
622
+ }
623
+ }
624
+ return callbacks;
625
+ }
626
+ function getRequireSource(node) {
627
+ if (node.callee.type === import_utils.AST_NODE_TYPES.Identifier && node.callee.name === "require" && node.arguments.length > 0) {
628
+ const arg = node.arguments[0];
629
+ if (arg.type === import_utils.AST_NODE_TYPES.Literal && typeof arg.value === "string") {
630
+ return arg.value;
631
+ }
632
+ }
633
+ return null;
634
+ }
635
+ if (shouldIgnoreFile()) {
636
+ return {};
637
+ }
638
+ if (isServerFile()) {
639
+ return {};
640
+ }
641
+ if (mode === "client-only" && // Only check files matching clientFilePatterns
642
+ !isClientFile()) {
643
+ return {};
644
+ }
645
+ const serverOnlyRequires = [];
646
+ const bareRequireViolations = [];
647
+ const sideEffectImportViolations = [];
648
+ const reexportViolations = [];
649
+ function isInsideServerScope(node) {
650
+ for (const scope of serverFunctionScopes) {
651
+ if (isNodeInsideScope(node, scope)) {
652
+ return true;
653
+ }
654
+ }
655
+ return false;
656
+ }
657
+ return {
658
+ // Collect server-only imports and their local names
659
+ ImportDeclaration(node) {
660
+ const source = node.source.value;
661
+ if (checkServerOnlyMarker && source === "server-only") {
662
+ hasServerOnlyImport = true;
663
+ return;
664
+ }
665
+ if (typeof source !== "string" || !isServerOnlyModule(source)) {
666
+ return;
667
+ }
668
+ if (!hasValueImportSpecifiers(node)) {
669
+ return;
670
+ }
671
+ if (node.specifiers.length === 0) {
672
+ sideEffectImportViolations.push({ node, module: source });
673
+ return;
674
+ }
675
+ const valueNames = new Set(getValueImportNames(node));
676
+ const variables = sourceCode.getDeclaredVariables(node).filter((variable) => valueNames.has(variable.name));
677
+ if (variables.length > 0) {
678
+ serverOnlyImports.push({
679
+ module: source,
680
+ node,
681
+ variables
682
+ });
683
+ }
684
+ },
685
+ CallExpression(node) {
686
+ const requireSource = getRequireSource(node);
687
+ if (requireSource) {
688
+ if (checkServerOnlyMarker && requireSource === "server-only") {
689
+ hasServerOnlyImport = true;
690
+ return;
691
+ }
692
+ if (isServerOnlyModule(requireSource)) {
693
+ const parent = node.parent;
694
+ if (parent?.type === import_utils.AST_NODE_TYPES.VariableDeclarator) {
695
+ const varDecl = parent.parent;
696
+ if (varDecl?.type === import_utils.AST_NODE_TYPES.VariableDeclaration) {
697
+ const variables = sourceCode.getDeclaredVariables(varDecl);
698
+ if (variables.length > 0) {
699
+ serverOnlyRequires.push({
700
+ module: requireSource,
701
+ node,
702
+ variables
703
+ });
704
+ return;
705
+ }
706
+ }
707
+ }
708
+ bareRequireViolations.push({
709
+ node: node.arguments[0],
710
+ module: requireSource
711
+ });
712
+ }
713
+ return;
714
+ }
715
+ if (!checkServerFunctions) {
716
+ return;
717
+ }
718
+ const serverFnCall = findServerFunctionCall(node);
719
+ if (serverFnCall) {
720
+ const callbacks = extractCallbacksFromChain(node);
721
+ for (const callback of callbacks) {
722
+ serverFunctionScopes.add(callback);
723
+ }
724
+ }
725
+ },
726
+ // Check export declarations - collect for reporting in Program:exit
727
+ ExportAllDeclaration(node) {
728
+ const source = node.source.value;
729
+ if (node.exportKind === "type") {
730
+ return;
731
+ }
732
+ if (typeof source === "string" && isServerOnlyModule(source)) {
733
+ reexportViolations.push({ node: node.source, module: source });
734
+ }
735
+ },
736
+ ExportNamedDeclaration(node) {
737
+ if (!node.source) return;
738
+ const source = node.source.value;
739
+ if (typeof source !== "string" || !isServerOnlyModule(source)) {
740
+ return;
741
+ }
742
+ if (!hasValueExportSpecifiers(node)) {
743
+ return;
744
+ }
745
+ reexportViolations.push({ node: node.source, module: source });
746
+ },
747
+ // Final analysis at end of file
748
+ "Program:exit"() {
749
+ if (hasServerOnlyImport) {
750
+ return;
751
+ }
752
+ for (const { node, module: module2 } of sideEffectImportViolations) {
753
+ context.report({
754
+ node,
755
+ messageId: "serverOnlyImport",
756
+ data: { module: module2 },
757
+ suggest: createSideEffectImportSuggestions(module2, sourceCode)
758
+ });
759
+ }
760
+ for (const { node, module: module2 } of reexportViolations) {
761
+ context.report({
762
+ node,
763
+ messageId: "serverOnlyImport",
764
+ data: { module: module2 },
765
+ suggest: createReexportSuggestions(sourceCode)
766
+ });
767
+ }
768
+ for (const violation of bareRequireViolations) {
769
+ if (!isInsideServerScope(violation.node)) {
770
+ context.report({
771
+ node: violation.node,
772
+ messageId: "serverOnlyRequire",
773
+ data: { module: violation.module },
774
+ suggest: createRequireSuggestions(sourceCode)
775
+ });
776
+ }
777
+ }
778
+ for (const { module: module2, node, variables } of serverOnlyRequires) {
779
+ if (isInsideServerScope(node)) {
780
+ continue;
781
+ }
782
+ let hasViolation = false;
783
+ for (const variable of variables) {
784
+ const valueReferences = variable.references.filter(
785
+ (reference) => reference.isRead()
786
+ );
787
+ if (valueReferences.length === 0) {
788
+ if (reportUnusedImports) {
789
+ hasViolation = true;
790
+ break;
791
+ }
792
+ continue;
793
+ }
794
+ const hasClientUsage = valueReferences.some((reference) => {
795
+ return !isInsideServerScope(reference.identifier);
796
+ });
797
+ if (hasClientUsage) {
798
+ hasViolation = true;
799
+ break;
800
+ }
801
+ }
802
+ if (hasViolation) {
803
+ context.report({
804
+ node: node.arguments[0],
805
+ messageId: "serverOnlyRequire",
806
+ data: { module: module2 },
807
+ suggest: createRequireSuggestions(sourceCode)
808
+ });
809
+ }
810
+ }
811
+ for (const { module: module2, node, variables } of serverOnlyImports) {
812
+ let hasViolation = false;
813
+ for (const variable of variables) {
814
+ const valueReferences = variable.references.filter(
815
+ (reference) => reference.isRead()
816
+ );
817
+ if (valueReferences.length === 0) {
818
+ if (reportUnusedImports) {
819
+ hasViolation = true;
820
+ break;
821
+ }
822
+ continue;
823
+ }
824
+ const hasClientUsage = valueReferences.some((reference) => {
825
+ return !isInsideServerScope(reference.identifier);
826
+ });
827
+ if (hasClientUsage) {
828
+ hasViolation = true;
829
+ break;
830
+ }
831
+ }
832
+ if (hasViolation) {
833
+ context.report({
834
+ node: node.source,
835
+ messageId: "serverOnlyImport",
836
+ data: { module: module2 },
837
+ suggest: createImportSuggestions(sourceCode)
838
+ });
839
+ }
840
+ }
841
+ }
842
+ };
843
+ }
844
+ });
845
+ var plugin = {
846
+ rules: {
847
+ "no-server-imports": rule
848
+ },
849
+ configs: {
850
+ /**
851
+ * Recommended config - uses sensible defaults that work across frameworks.
852
+ * For framework-specific optimizations, use one of the framework presets.
853
+ */
854
+ recommended: {
855
+ plugins: ["no-server-imports"],
856
+ rules: {
857
+ "no-server-imports/no-server-imports": "error"
858
+ }
859
+ },
860
+ /**
861
+ * Next.js optimized configuration.
862
+ * Best for Next.js App Router and Pages Router projects.
863
+ */
864
+ "recommended-next": {
865
+ plugins: ["no-server-imports"],
866
+ rules: {
867
+ "no-server-imports/no-server-imports": [
868
+ "error",
869
+ {
870
+ clientFilePatterns: FRAMEWORK_DEFAULTS.next.clientFilePatterns,
871
+ serverFilePatterns: FRAMEWORK_DEFAULTS.next.serverFilePatterns
872
+ }
873
+ ]
874
+ }
875
+ },
876
+ /**
877
+ * Astro optimized configuration.
878
+ * Best for Astro projects with islands architecture.
879
+ */
880
+ "recommended-astro": {
881
+ plugins: ["no-server-imports"],
882
+ rules: {
883
+ "no-server-imports/no-server-imports": [
884
+ "error",
885
+ {
886
+ clientFilePatterns: FRAMEWORK_DEFAULTS.astro.clientFilePatterns,
887
+ serverFilePatterns: FRAMEWORK_DEFAULTS.astro.serverFilePatterns
888
+ }
889
+ ]
890
+ }
891
+ },
892
+ /**
893
+ * SvelteKit optimized configuration.
894
+ * Best for SvelteKit projects.
895
+ */
896
+ "recommended-sveltekit": {
897
+ plugins: ["no-server-imports"],
898
+ rules: {
899
+ "no-server-imports/no-server-imports": [
900
+ "error",
901
+ {
902
+ clientFilePatterns: FRAMEWORK_DEFAULTS.sveltekit.clientFilePatterns,
903
+ serverFilePatterns: FRAMEWORK_DEFAULTS.sveltekit.serverFilePatterns
904
+ }
905
+ ]
906
+ }
907
+ }
908
+ },
909
+ meta: {
910
+ name: "eslint-plugin-no-server-imports"
911
+ }
912
+ };
913
+ var index_default = plugin;
914
+ // Annotate the CommonJS export names for ESM import in node:
915
+ 0 && (module.exports = {
916
+ FRAMEWORK_DEFAULTS,
917
+ clearFrameworkCache,
918
+ detectFramework,
919
+ getFrameworkDefaults,
920
+ rule
921
+ });
922
+ /**
923
+ * ESLint Plugin: eslint-plugin-no-server-imports
924
+ * ===============================================
925
+ * Prevents server-only module imports in client code.
926
+ * Catches bundling issues in your editor instead of at build time.
927
+ *
928
+ * @author Jag Reehal [@jagreehal] <jag@jagreehal.com>
929
+ * @license MIT
930
+ */
931
+ //# sourceMappingURL=index.js.map