118 lines
3.4 KiB
JavaScript
118 lines
3.4 KiB
JavaScript
import js from '@eslint/js'
|
|
import globals from 'globals'
|
|
import reactHooks from 'eslint-plugin-react-hooks'
|
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
import tailwindcss from 'eslint-plugin-tailwindcss'
|
|
import tseslint from 'typescript-eslint'
|
|
import { globalIgnores } from 'eslint/config'
|
|
import noRelativeImportPaths from 'eslint-plugin-no-relative-import-paths'
|
|
import ghostPlugin from 'eslint-plugin-ghost';
|
|
|
|
const noHardcodedGhostPaths = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Disallow hardcoded /ghost/ paths that break subdirectory installations',
|
|
},
|
|
messages: {
|
|
noHardcodedPath: 'Do not hardcode /ghost/ paths. Use getGhostPaths() from @tryghost/admin-x-framework/helpers to support subdirectory installations.',
|
|
},
|
|
},
|
|
create(context) {
|
|
const pattern = /^\/ghost\//;
|
|
return {
|
|
Literal(node) {
|
|
if (typeof node.value === 'string' && pattern.test(node.value)) {
|
|
context.report({node, messageId: 'noHardcodedPath'});
|
|
}
|
|
},
|
|
TemplateLiteral(node) {
|
|
const first = node.quasis[0];
|
|
if (first && pattern.test(first.value.raw)) {
|
|
context.report({node, messageId: 'noHardcodedPath'});
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
const localPlugin = {
|
|
rules: {
|
|
'no-hardcoded-ghost-paths': noHardcodedGhostPaths,
|
|
},
|
|
};
|
|
const tailwindCssConfig = `${import.meta.dirname}/src/index.css`;
|
|
|
|
export default tseslint.config([
|
|
globalIgnores(['dist']),
|
|
{
|
|
files: ['**/*.{ts,tsx}'],
|
|
extends: [
|
|
js.configs.recommended,
|
|
tseslint.configs.recommendedTypeChecked,
|
|
reactHooks.configs['recommended-latest'],
|
|
reactRefresh.configs.vite,
|
|
],
|
|
plugins: {
|
|
'no-relative-import-paths': noRelativeImportPaths,
|
|
ghost: ghostPlugin,
|
|
local: localPlugin,
|
|
tailwindcss,
|
|
},
|
|
languageOptions: {
|
|
parserOptions: {
|
|
projectService: true,
|
|
tsconfigRootDir: import.meta.dirname,
|
|
},
|
|
ecmaVersion: 2020,
|
|
globals: globals.browser,
|
|
},
|
|
settings: {
|
|
tailwindcss: {
|
|
config: tailwindCssConfig,
|
|
},
|
|
},
|
|
rules: {
|
|
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false],
|
|
'no-restricted-imports': ['error', {
|
|
paths: [{
|
|
name: '@tryghost/shade',
|
|
message: 'Import from layered subpaths instead (components/primitives/patterns/utils/app/tokens).',
|
|
}],
|
|
}],
|
|
'tailwindcss/classnames-order': 'error',
|
|
'tailwindcss/no-contradicting-classname': 'error',
|
|
},
|
|
},
|
|
// Apply no-relative-import-paths rule for src files (auto-fix supported)
|
|
{
|
|
files: ['src/**/*.{ts,tsx}'],
|
|
rules: {
|
|
'no-relative-import-paths/no-relative-import-paths': [
|
|
'error',
|
|
{ allowSameFolder: true, rootDir: 'src', prefix: '@' },
|
|
],
|
|
},
|
|
},
|
|
// Prevent hardcoded /ghost/ paths in production code (not tests, where mocks need fixed paths)
|
|
{
|
|
files: ['src/**/*.{ts,tsx}'],
|
|
ignores: ['src/**/*.test.*'],
|
|
rules: {
|
|
'local/no-hardcoded-ghost-paths': 'error',
|
|
},
|
|
},
|
|
// Apply no-relative-import-paths rule for test-utils files
|
|
// Note: auto-fix may produce incorrect paths for cross-directory imports
|
|
// Use the correct alias manually: @/* for src/, @test-utils/* for test-utils/
|
|
{
|
|
files: ['test-utils/**/*.{ts,tsx}'],
|
|
rules: {
|
|
'no-relative-import-paths/no-relative-import-paths': [
|
|
'error',
|
|
{ allowSameFolder: true },
|
|
],
|
|
},
|
|
},
|
|
])
|