eslint-plugin-fsd-architecture
Version:
ESlint plugin to comply with FSD (Feature Sliced Design) frontend architecture rules
92 lines (82 loc) • 2.62 kB
JavaScript
const path = require('path')
const micromatch = require('micromatch')
const {isPathRelative} = require('../helpers')
module.exports = {
meta: {
type: 'problem', // `problem`, `suggestion`, or `layout`
messages: [],
docs: {
description: "Rule for check imports from layers structure of FSD architecture",
recommended: false,
url: null, // URL to the documentation page for this rule
},
fixable: null, // Or `code` or `whitespace`
schema: [
{
type: 'object',
properties: {
alias: {
type: 'string'
},
ignoreImportPatterns: {
type: 'array'
}
}
}
],
},
create(context) {
const layers = {
app: ['pages', 'widgets', 'features', 'entities', 'shared'],
pages: ['widgets', 'features', 'entities', 'shared'],
widgets: ['features', 'entities', 'shared'],
features: ['entities', 'shared'],
entities: ['shared', 'entities'],
shared: ['shared'],
}
const availableLayers = {
app: 'app',
processes: 'processes',
pages: 'pages',
widgets: 'widgets',
features: 'features',
entities: 'entities',
shared: 'shared',
}
const { alias = '', ignoreImportPatterns = []} = context.options[0] ?? {}
const getCurrentFileLayer = () => {
const currentFilePath = context.getFilename()
const normalizedPath = path.toNamespacedPath(currentFilePath)
const projectPath = normalizedPath?.split('src')[1]
const segments = projectPath?.split(/\\|\//)
return segments?.[1]
}
const getImportLayer = (value) => {
const importPath = alias ? value.replace(`${alias}/`,'') : value
const segments = importPath?.split(/\\|\//)
return segments?.[0]
}
return {
ImportDeclaration(node) {
const importPath = node.source.value
const currentFileLayer = getCurrentFileLayer()
const importLayer = getImportLayer(importPath)
if (isPathRelative(importPath)) {
return
}
if (!availableLayers[importLayer] || !availableLayers[currentFileLayer]) {
return
}
const isIgnored = ignoreImportPatterns.some(
pattern => micromatch.isMatch(importPath, pattern)
)
if (isIgnored) {
return
}
if (!layers[currentFileLayer]?.includes(importLayer)) {
context.report(node, 'A layer can only import the underlying layers into itself. (app > pages > widgets > features > entities > shared)')
}
}
};
},
};