-eslint-plugin-import

例子

我们从一个简单的例子去看,比如在规则中的exports-last, 他的作用是确保export 放到最后导出

exports-last

/** valid */
const foo = 'bar'
export default foo
export const bar = true
/** invalid */
export default 'such foo many bar'
export const so = 'many'
const foo = 'bar'
export const exports = ':)'
export const i = 'cant'
export const even = 'count'
export const how = 'many'

function isNonExportStatement({ type }) {
  return type !== 'ExportDefaultDeclaration'
    && type !== 'ExportNamedDeclaration'
    && type !== 'ExportAllDeclaration';
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      category: 'Style guide',
      description: 'Ensure all exports appear after other statements.',
      url: docsUrl('exports-last'),
    },
    schema: [],
  },

  create(context) {
    return {
      Program({ body }) {
        /**使用findLastIndex函数查找body中最后一个非导出语句的索引 */
        const lastNonExportStatementIndex = findLastIndex(body, isNonExportStatement);

        if (lastNonExportStatementIndex !== -1) {
          /** 获取索引位置之前的所有节点(包括非导出语句)*/
          body.slice(0, lastNonExportStatementIndex).forEach((node) => {
            // 如果节点不是非导出语句,说明它是一个导出语句
            if (!isNonExportStatement(node)) {
              context.report({
                node,
                message: 'Export statements should appear at the end of the file',
              });
            }
          });
        }
      },
    };
  },
};

eslint插件中的meta属性

meta对象提供了一些元数据,用于描述规则的基本信息和行为。这些信息帮助ESLint和用户了解规则的目的、使用方式以及如何与ESLint的其他部分交互

meta: {
  // "problem"、"suggestion"、"layout" 或 "directive"
  type: "suggestion",
  docs: {
    description: "描述",
    category: "Best Practices",
    recommended: false,
  },
  // 一个JSON Schema对象,用于定义规则可以接受的选项
  schema: [
    {
      type: "object",
      properties: {
        ignoreStrings: {
          type: "boolean",
        },
        ignoreTemplateLiterals: {
          type: "boolean",
        },
      },
      additionalProperties: false,
    },
  ],
  // 报错信息提示,通常用context.report({messageId: 'unexpectedConcatenation'})
  messages: {
    unexpectedConcatenation: "Unexpected concatenation of literals.",
  },
  // 指示规则是否能够自动修复发现的问题。如果设置为true,则在ESLint的命令行界面中可以使用--fix选项自动修复问题
  fixable: "code",
  // 指示规则是否能够提供代码修复的建议。这通常用于--fix选项无法自动修复问题,但规则可以提供一些手动修复建议的情况
  hasSuggestions: true,
}

create 方法

在create 方法中我们可以解析遍历AST, 其入参是context,有以下属性

  • parserOptions-编译器选项,就是我们.eslintrc.js里的那个
  • id-规则id
  • options-通过这个可以拿到规则传进来的参数
  • getFilename()-返回源文件文件名
  • getScope()- 返回当前遍历节点的作用域
  • getSourceCode()- 返回SourceCode对象,就是源码对象,很有用
  • report()- 当校验不通过的时候,通过这个方法输出错误信息
  • 可以通过 context.settings 拿到.eslintrc.js 全局设置的settings

在来另一个例子,使用fix 去修复代码

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      category: 'Static analysis',
      description: 'Forbid import of modules using absolute paths.',
      url: docsUrl('no-absolute-path'),
    },
    fixable: 'code',
    schema: [makeOptionsSchema()],
  },

  create(context) {
    function reportIfAbsolute(source) {
      if (isAbsolute(source.value)) {
        context.report({
          node: source,
          message: 'Do not import modules using an absolute path',
          fix(fixer) {
            const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
            // node.js and web imports work with posix style paths ("/")
            let relativePath = path.posix.relative(path.dirname(resolvedContext), source.value);
            if (!relativePath.startsWith('.')) {
              relativePath = `./${relativePath}`;
            }
            return fixer.replaceText(source, JSON.stringify(relativePath));
          },
        });
      }
    }

    const options = { esmodule: true, commonjs: true, ...context.options[0] };
    return moduleVisitor(reportIfAbsolute, options);
  },
};

自己写一个plugin

  1. 导入的模块名需要是下划线或是驼峰式
  2. 除了个别目录外不能跨目录导入文件,比如说不能跨模块导入组件等,但可以在子级下导入文件