theatre/devEnv/eslint/rules/no-relative-imports.js

132 lines
3 KiB
JavaScript
Raw Normal View History

2021-06-18 13:05:06 +02:00
const path = require('path')
const minimatch = require('minimatch')
// @todo find and acknowledge the original source of this (it was a github gist)
// @todo this rule is clearly ignoring a bunch of files that do have parent
// relative paths but don't get caught.
const replace = {
meta: {
type: 'layout',
fixable: 'code',
schema: [
{
type: 'object',
properties: {
ignore: {
type: 'array',
items: {
type: 'string',
},
},
method: {
type: 'string',
enum: ['all', 'only-parent'],
},
aliases: {
type: 'array',
minItems: 1,
items: {
properties: {
name: {
type: 'string',
},
path: {
type: 'string',
},
},
required: ['name', 'path'],
additionalProperties: false,
},
},
},
required: ['aliases'],
additionalProperties: false,
},
],
messages: {
'can-replace':
'Relative import found. Run autofix to replace these relative imports!',
'cannot-replace':
'Relative import found. No alias has been defined which matches this import path',
},
},
create: (context) => {
return {
ImportDeclaration: (node) => {
const config = getConfiguration(context)
if (
config.ignorePatterns.some((pattern) =>
minimatch(context.getFilename(), pattern),
)
) {
return
}
evaluateImport(node, config, context)
},
}
},
}
function evaluateImport(node, config, context) {
const {
source: {value, range, loc},
} = node
if (value.startsWith('./') && config.replaceMethod === 'only-parent') {
return
} else if (!value.startsWith('../')) {
return
}
let canFix = false
const currentFileDirectory = path.dirname(context.getFilename())
for (const alias of config.replaceAliases) {
const {replaceDir, replaceWith} = alias
const fullImportPath = path.resolve(currentFileDirectory, value)
if (fullImportPath.startsWith(replaceDir)) {
canFix = true
context.report({
messageId: 'can-replace',
loc,
fix: (fixer) =>
fixer.replaceTextRange(
range,
`'${fullImportPath.replace(replaceDir, replaceWith)}'`,
),
})
break
}
}
if (!canFix) {
context.report({
messageId: 'cannot-replace',
loc,
})
}
}
function getConfiguration(context) {
const options = {
ignore: [],
method: 'only-parent',
...context.options[0],
}
return {
ignorePatterns: options.ignore,
replaceMethod: options.method,
replaceAliases: options.aliases.map((alias) => ({
replaceDir: alias.path,
replaceWith: alias.name,
})),
}
}
module.exports = replace