109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
/**
|
|
* ESLint rule to forbid Next.js imports in lib/ directory
|
|
*
|
|
* The lib/ directory should be framework-agnostic.
|
|
* Next.js imports (redirect, cookies, headers, etc.) should only be in app/ directory.
|
|
*/
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Forbid Next.js imports in lib/ directory',
|
|
category: 'Architecture',
|
|
recommended: true,
|
|
},
|
|
fixable: null,
|
|
schema: [],
|
|
messages: {
|
|
noNextImports: 'Next.js imports are forbidden in lib/ directory. Found: {{import}} from "{{source}}"',
|
|
noNextRedirect: 'redirect() must be used in app/ directory, not lib/ directory',
|
|
noNextCookies: 'cookies() must be used in app/ directory, not lib/ directory',
|
|
noNextHeaders: 'headers() must be used in app/ directory, not lib/ directory',
|
|
},
|
|
},
|
|
|
|
create(context) {
|
|
const filename = context.getFilename();
|
|
const isInLib = filename.includes('/lib/');
|
|
|
|
// Skip if not in lib directory
|
|
if (!isInLib) return {};
|
|
|
|
return {
|
|
// Track import statements
|
|
ImportDeclaration(node) {
|
|
const source = node.source.value;
|
|
|
|
// Check for Next.js imports
|
|
if (source === 'next/navigation' ||
|
|
source === 'next/headers' ||
|
|
source === 'next/cookies' ||
|
|
source === 'next/router' ||
|
|
source === 'next/link' ||
|
|
source === 'next/image' ||
|
|
source === 'next/script' ||
|
|
source === 'next/dynamic') {
|
|
|
|
// Check for specific named imports
|
|
node.specifiers.forEach(spec => {
|
|
if (spec.type === 'ImportSpecifier') {
|
|
const imported = spec.imported.name;
|
|
|
|
if (imported === 'redirect') {
|
|
context.report({
|
|
node: spec,
|
|
messageId: 'noNextRedirect',
|
|
});
|
|
} else if (imported === 'cookies') {
|
|
context.report({
|
|
node: spec,
|
|
messageId: 'noNextCookies',
|
|
});
|
|
} else if (imported === 'headers') {
|
|
context.report({
|
|
node: spec,
|
|
messageId: 'noNextHeaders',
|
|
});
|
|
} else {
|
|
context.report({
|
|
node: spec,
|
|
messageId: 'noNextImports',
|
|
data: { import: imported, source },
|
|
});
|
|
}
|
|
} else if (spec.type === 'ImportDefaultSpecifier') {
|
|
context.report({
|
|
node: spec,
|
|
messageId: 'noNextImports',
|
|
data: { import: 'default', source },
|
|
});
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
// Also check for require() calls
|
|
CallExpression(node) {
|
|
if (node.callee.type === 'Identifier' && node.callee.name === 'require') {
|
|
if (node.arguments.length > 0 && node.arguments[0].type === 'Literal') {
|
|
const source = node.arguments[0].value;
|
|
|
|
if (source === 'next/navigation' ||
|
|
source === 'next/headers' ||
|
|
source === 'next/cookies' ||
|
|
source === 'next/router') {
|
|
|
|
context.report({
|
|
node,
|
|
messageId: 'noNextImports',
|
|
data: { import: 'require()', source },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|