💡

Astro v2の環境でESLint・Stylelintの導入を行う

2023/02/19に公開

Astro v2の環境でESLint・Stylelintを入れた際に少し躓いたので、対応したことを記載します。

結論

以下を実行・コードを記載して対応しました。

パッケージ追加したコマンド

yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-plugin-astro eslint-plugin-import postcss-html prettier prettier-plugin-astro stylelint-config-recess-order stylelint-declaration-block-no-ignored-properties stylelint-no-unsupported-browser-features stylelint-prettier stylelint-scss

今回追加したパッケージのバージョン

執筆時点(2023/2/19時点)での最新バージョンです。

  • "astro": "^2.0.2",
  • "@typescript-eslint/eslint-plugin": "^5.52.0",
  • "@typescript-eslint/parser": "^5.52.0",
  • "eslint": "^8.34.0",
  • "eslint-plugin-astro": "^0.23.0",
  • "eslint-plugin-import": "^2.27.5",
  • "postcss-html": "^1.5.0",
  • "prettier": "^2.8.3",
  • "prettier-plugin-astro": "^0.8.0",
  • "stylelint": "^15.1.0",
  • "stylelint-config-recess-order": "^4.0.0",
  • "stylelint-declaration-block-no-ignored-properties": "^2.7.0",
  • "stylelint-no-unsupported-browser-features": "^6.1.0",
  • "stylelint-prettier": "^2.0.0",
  • "stylelint-scss": "^4.4.0",

ファイル群

.eslintrc.cjs

.eslintrc.cjs
module.exports = {
  ignorePatterns: ['*.d.ts'],
  extends: ['eslint:recommended', 'plugin:import/typescript', 'plugin:astro/recommended'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    project: './tsconfig.json',
  },
  globals: {
    astroHTML: true,
  },
  plugins: ['@typescript-eslint', 'import'],
  rules: {
    '@typescript-eslint/consistent-type-imports': [
      2,
      {
        prefer: 'type-imports',
      },
    ],
    'import/order': [2, { alphabetize: { order: 'asc' } }],
  },
  overrides: [
    {
      // Define the configuration for `.astro` file.
      files: ['*.astro'],
      // Allows Astro components to be parsed.
      parser: 'astro-eslint-parser',
      // Parse the script in `.astro` as TypeScript by adding the following configuration.
      // It's the setting you need when using TypeScript.
      parserOptions: {
        parser: '@typescript-eslint/parser',
        extraFileExtensions: ['.astro'],
      },
      rules: {
        // override/add rules settings here, such as:
        'astro/no-set-html-directive': 'error',
      },
    },
    {
      files: ['*.ts'],
      parser: '@typescript-eslint/parser',
    },
  ],
}

.prettierrc

.prettierrc
{
  "semi": false,
  "arrowParens": "always",
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "singleQuote": true,
  "proseWrap": "preserve"
}

stylelint.config.cjs

stylelint.config.cjs
module.exports = {
  // add your custom config here
  // https://stylelint.io/user-guide/configuration
  customSyntax: 'postcss-html',
  plugins: [
    'stylelint-scss',
    'stylelint-declaration-block-no-ignored-properties',
    'stylelint-no-unsupported-browser-features',
    'stylelint-prettier',
  ],
  extends: ['stylelint-config-recess-order'],
  rules: {
    'prettier/prettier': true,
    'plugin/declaration-block-no-ignored-properties': true,
    'plugin/no-unsupported-browser-features': [
      true,
      {
        severity: 'warning',
      },
    ],
    'font-family-no-missing-generic-family-keyword': true,
    'declaration-block-no-shorthand-property-overrides': true,
    'selector-pseudo-element-colon-notation': 'double',
  },
}

コマンド

{
  scripts: {
    "format": "prettier --write **/*.{css,scss,ts,tsx,astro}",
    "lint:js": "eslint \"./src/**/*.{js,jsx,ts,tsx,astro}\" --fix --ignore-path .gitignore",
    "lint:style": "stylelint \"./src/**/*.{css,scss,astro}\" --fix --ignore-path .gitignore",
  }
}

今回設定した内容について

1. Prettierの追加

Prettierの追加については、公式のドキュメントでのやり方で問題なく行うことができました。

https://docs.astro.build/ja/editor-setup/#prettier

2. ESLintの追加

2-1. 公式の導入方法に基づいて追加をしてみる

https://docs.astro.build/ja/editor-setup/#eslint

https://ota-meshi.github.io/eslint-plugin-astro/user-guide/

公式の通り以下のパッケージを追加し、.eslintrc.jsのファイルでコマンドを実行してみる

対応したこと

パッケージ追加

$ yarn add -D eslint eslint-plugin-astro @typescript-eslint/parser
.eslintrc.js
module.exports = {
  extends: [
    "plugin:astro/recommended",
  ],
  overrides: [
    {
      // Define the configuration for `.astro` file.
      files: ["*.astro"],
      // Allows Astro components to be parsed.
      parser: "astro-eslint-parser",
      // Parse the script in `.astro` as TypeScript by adding the following configuration.
      // It's the setting you need when using TypeScript.
      parserOptions: {
        parser: "@typescript-eslint/parser",
        extraFileExtensions: [".astro"],
      },
      rules: {
        // override/add rules settings here, such as:
        // "astro/no-set-html-directive": "error"
      },
    },
  ],
}

エラー内容

$ eslint "./src/**/*.{js,jsx,ts,tsx,astro}" --fix --ignore-path .gitignore

Oops! Something went wrong! :(

ESLint: 8.34.0

Error [ERR_REQUIRE_ESM]: require() of ES Module /.eslintrc.js from /node_modules/@eslint/eslintrc/dist/eslintrc.cjs not supported.
.eslintrc.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename .eslintrc.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

    at module.exports [as default] (/node_modules/import-fresh/index.js:32:59)
    at loadJSConfigFile (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2562:47)
    at loadConfigFile (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2646:20)
    at ConfigArrayFactory.loadInDirectory (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2856:34)
    at CascadingConfigArrayFactory._loadConfigInAncestors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3848:46)
    at CascadingConfigArrayFactory._loadConfigInAncestors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3867:20)
    at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3769:18)
    at FileEnumerator._iterateFilesRecursive (/node_modules/eslint/lib/cli-engine/file-enumerator.js:448:49)
    at _iterateFilesRecursive.next (<anonymous>)
    at FileEnumerator.iterateFiles (/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:49)
    at iterateFiles.next (<anonymous>)
    at CLIEngine.executeOnFiles (/node_modules/eslint/lib/cli-engine/cli-engine.js:786:48)
    at ESLint.lintFiles (/node_modules/eslint/lib/eslint/eslint.js:551:23)
    at Object.execute (/node_modules/eslint/lib/cli.js:417:36)
    at async main (/node_modules/eslint/bin/eslint.js:135:24)
error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

2-2. .eslintrc.cjsに変更して再度実行してみる

「2-1」でのエラー内容が以下のため、.eslintrc.jsから.eslintrc.cjsに変更して、再度Lintの実行をしてみる。

Error [ERR_REQUIRE_ESM]: require() of ES Module /.eslintrc.js from /node_modules/@eslint/eslintrc/dist/eslintrc.cjs not supported.
.eslintrc.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename .eslintrc.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

変更した結果以下のようになったため、これでESLintを使用することができるようになりました。

$ eslint "./src/**/*.{js,jsx,ts,tsx,astro}" --fix --ignore-path .gitignore
✨  Done in 2.52s.

2-3. TypeScriptでのエラーが出るため、TSファイルの設定を行う

独自の型定義ファイルやTSファイルがある場合Lintがエラーになってしまうため、TypeScriptファイルでもLintが実行されるようにします。

TSファイル

astro-imagetools.d.ts
declare module 'astro-imagetools/components'
src/utils/hello.ts
export const hello = (name: string): string => {
  return `Hello,${name}!`
}

エラー内容

$ eslint "./src/**/*.{js,jsx,ts,tsx,astro}" --fix --ignore-path .gitignore

/src/astro-imagetools.d.ts
  1:9  error  Parsing error: Unexpected token module

/src/utils/hello.ts
  1:27  error  Parsing error: Unexpected token :

対応したこと

.tsファイルについては、以下を参考に@typescript-eslint/parserを追加。

https://ja.stackoverflow.com/questions/57076/interfaceのプロパティがparsing-error-unexpected-token

.eslintrc.cjs
module.exports = {
  overrides: [
    {
      files: ['*.ts'],
      parser: '@typescript-eslint/parser',
    },
  ]
}

.d.tsについては、特に確認したい内容もなかったため、ignorePatternsにてESLintのルールを無視するようにしました。

https://eslint.org/docs/latest/use/configure/ignore#ignorepatterns-in-config-files

.eslintrc.cjs
module.export = {
  ignorePatterns: ['*.d.ts'],
}

上記の設定を行うことでLintが通るようになりました。

$ eslint "./src/**/*.{js,jsx,ts,tsx,astro}" --fix --ignore-path .gitignore
✨  Done in 2.30s.

2-4. ESLintのルール・パッケージの一部を追加

consistent-type-importsのESLintルールとeslint-plugin-importのパッケージも追加しました。

https://typescript-eslint.io/rules/consistent-type-imports/

https://www.npmjs.com/package/eslint-plugin-import

詳しい導入方法については過去に記載していますので、こちらをご覧ください。

https://jackswim3411.hatenablog.com/entry/2022/07/18/022529

3. Stylelintの追加

StylelintについてもESLintと同様、stylelint.config.jsだとESLintと同様のエラーが出たため、stylelint.config.cjsで実行させたところ、問題なくうごきました。

3-1. 基本ルールの追加

$ yarn add -D stylelint stylelint-prettier stylelint-scss
stylelint.config.cjs
module.exports = {
  plugins: [
    'stylelint-scss',
    'stylelint-prettier',
  ],
  rules: {
    'prettier/prettier': true,
    'font-family-no-missing-generic-family-keyword': true,
    'declaration-block-no-shorthand-property-overrides': true,
    'selector-pseudo-element-colon-notation': 'double',
  },
}

追加しているルール

https://stylelint.io/user-guide/rules/font-family-no-missing-generic-family-keyword/

https://stylelint.io/user-guide/rules/declaration-block-no-shorthand-property-overrides/

https://stylelint.io/user-guide/rules/selector-pseudo-element-colon-notation/

3-2. CssSyntaxErrorのエラーが出るため、修正

「3-1」で追加したLintルールで以下のエラーが出るため、customSyntaxのパッケージを追加

エラー内容・該当のファイル

$ stylelint './src/**/*.{css,scss,astro}' --fix --ignore-path .gitignore

/src/styles/style.scss: you should use the "customSyntax" option when linting something other than CSS
/src/pages/index.astro: you should use the "customSyntax" option when linting something other than CSS

src/pages/index.astro
 2:10  ✖  Unknown word  CssSyntaxError

src/styles/style.scss
 2:1  ✖  Unknown word  CssSyntaxError

src/pages/index.astro

---
import { SITE_TITLE, SITE_DESCRIPTION } from '~/consts'
import Layout from '~/layouts.astro'
import { hello } from '~/utils/hello'
---

<p>ページ情報</p>
<!-- その他HTMLの情報 -->

src/styles/style.scss

src/styles/style.scss
@forward 'setup';
// @forward 'check';

対応したこと

postcss-htmlのパッケージをインストールして、customSyntaxpostcss-htmlを記載。

$ yarn add -D postcss-html
stylelint.config.cjs
module.exports = {
  customSyntax: 'postcss-html',
}

上記の設定を行うことでLintが通るようになりました。

$ stylelint './src/**/*.{css,scss,astro}' --fix --ignore-path .gitignore
✨  Done in 0.66s.

https://github.com/stylelint/stylelint/blob/14.0.0/docs/migration-guide/to-14.md#syntax-option-and-automatic-inferral-of-syntax

https://www.npmjs.com/package/postcss-html

3-3. カスタムルールの追加

「3-2」まででStylelintを動かすことはできたため、一部パッケージやルールを追加していきます。

$ yarn add -D stylelint-config-recess-order stylelint-declaration-block-no-ignored-properties stylelint-no-unsupported-browser-features

https://www.npmjs.com/package/stylelint-config-recess-order

https://www.npmjs.com/package/stylelint-declaration-block-no-ignored-properties

https://www.npmjs.com/package/stylelint-no-unsupported-browser-features

今回追加したstylelint.config.cjs

stylelint.config.cjs
module.exports = {
  plugins: [
    'stylelint-declaration-block-no-ignored-properties',
    'stylelint-no-unsupported-browser-features',
  ],
  extends: ['stylelint-config-recess-order'],
  rules: {
    'plugin/declaration-block-no-ignored-properties': true,
    'plugin/no-unsupported-browser-features': [
      true,
      {
        severity: 'warning',
      },
    ],
  },
}

全体のstylelint.config.cjs

stylelint.config.cjs
module.exports = {
  customSyntax: 'postcss-html',
  plugins: [
    'stylelint-scss',
    'stylelint-declaration-block-no-ignored-properties',
    'stylelint-no-unsupported-browser-features',
    'stylelint-prettier',
  ],
  extends: ['stylelint-config-recess-order'],
  rules: {
    'prettier/prettier': true,
    'plugin/declaration-block-no-ignored-properties': true,
    'plugin/no-unsupported-browser-features': [
      true,
      {
        severity: 'warning',
      },
    ],
    'font-family-no-missing-generic-family-keyword': true,
    'declaration-block-no-shorthand-property-overrides': true,
    'selector-pseudo-element-colon-notation': 'double',
  },
}

今回参考にしたもの

https://github.com/withastro/docs

https://docs.astro.build/ja/editor-setup/#その他のツール

https://github.com/ota-meshi/eslint-plugin-astro

https://ota-meshi.github.io/eslint-plugin-astro/user-guide/

Discussion