# @scayle/eslint-config-storefront

A [shared `eslint` config](https://eslint.org/docs/latest/extend/shareable-configs#creating-a-shareable-config) for SCAYLE Storefront projects and packages.
Provides SCAYLE-specific `eslint` configuration for Frontend and Node.js projects using TypeScript.

Flat config only ([ESLint 10.4+](https://eslint.org/docs/latest/use/configure/configuration-files)). Built directly on first-class plugins (`@eslint/js`, `typescript-eslint`, `eslint-plugin-n`, `eslint-plugin-jsonc`, `eslint-plugin-jsdoc`, `eslint-plugin-unicorn`, `eslint-plugin-regexp`, `@e18e/eslint-plugin`, and others) with no shared-config aggregator dependency.

Should you require support for `eslint@8`, please use `@scayle/eslint-config-storefront@3`.

- [Official ESLint Documentation](https://eslint.org/docs/latest/)

---

## Install

Add the `@scayle/eslint-config-storefront` manually to your project:

`pnpm i -D eslint @scayle/eslint-config-storefront`

`yarn add -D eslint @scayle/eslint-config-storefront`

And create a `eslint.config.mjs` in your project root:

```js
// eslint.config.mjs
import storefront from '@scayle/eslint-config-storefront'

export default storefront()
```

### Nuxt projects

Nuxt projects already get JavaScript, TypeScript, JSDoc, and import rules from
[`@nuxt/eslint`](https://eslint.nuxt.com/). Use the `/nuxt` entry point instead, which
only adds the framework-agnostic rules (comments hygiene, Node.js, JSON/JSONC, test-file
conventions, SonarJS, Promise usage, modernization, and regex safety):

```js
// eslint.config.mjs
import storefrontNuxt from '@scayle/eslint-config-storefront/nuxt'
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt(storefrontNuxt())
```

### Storefront Application V3 projects

The Storefront Application V3 stack (Hono + Inertia.js + Vue 3, not Nuxt) should use the
`/v3` entry point. It builds on the default config and adds Vue language wiring, Vue rules,
accessibility rules, `vue-i18n` linting, `@scayle/vue-composable` rules, Storybook file
conventions, Tailwind CSS class validation, and a TypeScript-aware import resolver:

```js
// eslint.config.mjs
import storefrontV3 from '@scayle/eslint-config-storefront/v3'

export default storefrontV3({
  tailwind: {
    entryPoint: './src/client/index.css',
  },
  i18n: {
    localeDir: './src/i18n/locales/*.json',
  },
})
```

`tailwind.entryPoint` and `i18n.localeDir` are required, the underlying plugins can't
validate Tailwind classes or i18n keys without a real path. Optional options:

- `typescript.tsconfigPath`: absolute path to a `tsconfig.json`. Enables type-aware
  TypeScript rules and points the import resolver at that file instead of
  `<root>/tsconfig.json`
- `resolver.tsconfigPath`: absolute path to a `tsconfig.json` for the import resolver only,
  independent of `typescript.tsconfigPath`. Use this instead when you want the resolver
  fix below without also switching on type-aware TypeScript rules
- `i18n.messageSyntaxVersion`: `vue-i18n` message syntax version, defaults to `^11.2.8`
- `i18n.unusedKeyIgnores`: regex-string patterns for translation keys used dynamically in code
- `vue.undefComponentIgnores`: custom element (web component) tag names to exempt from `vue/no-undef-components`
- `vueComposable.fallthroughAttrsIgnores`: path fragments to exempt from `require-explicit-fallthrough-attrs` (e.g. router-mounted page/layout roots that never receive fallthrough attributes)

#### Import resolution

`/v3` wires `eslint-import-resolver-typescript`, so `import-x` rules resolve `tsconfig.json`
path aliases (`@client/*`, `@server/*`, or any other `paths` entry) to their real files
instead of only matching literal relative imports. Three rules depend on that resolution
to work correctly against an aliased import:

- `import-x/no-restricted-paths`: zone violations across an alias fail lint, not just
  across a relative path
- `import-x/no-cycle`: circular dependencies through an aliased import are caught. `.vue`
  files are the one exception, a cycle through a `.vue` file only surfaces when ESLint
  lints that file directly
- `import-x/order`: aliased imports rank alongside package imports (`external`), ahead of
  relative ones, instead of dropping to last place once the resolver can see they resolve
  to a project file

Without `resolver.tsconfigPath` or `typescript.tsconfigPath` set, both the resolver and
`import-x/no-restricted-paths`'s zones resolve relative to ESLint's own working directory.
That's correct for `pnpm lint` and CI, since a monorepo's task runner always runs a
package's lint script with that package as the working directory, but not guaranteed for
an editor's ESLint extension, which can run with the workspace root as its working
directory instead. Set `resolver.tsconfigPath` to an absolute path (for example,
`new URL('./tsconfig.json', import.meta.url).pathname` in your `eslint.config.mjs`) if your
editor's linting needs to match the command-line result.

### Add script for package.json

For example:

```json
{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  }
}
```

## VS Code support (auto fix on save)

Install [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)

Add the following settings to your `.vscode/settings.json`:

```jsonc
{
  // Enable the ESlint flat config support
  "eslint.experimental.useFlatConfig": true,

  // Auto fix
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "never",
  },

  // Enable eslint for all supported languages
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
    "html",
    "markdown",
    "json",
    "jsonc",
    "yaml",
  ],
}
```

## Customization

### Default config / `@scayle/eslint-config-storefront`

```js
// eslint.config.mjs
import storefront from '@scayle/eslint-config-storefront'

export default storefront({
  typescript: {
    // Enables type-aware rules for the given tsconfig
    tsconfigPath: 'path/to/tsconfig.json',
  },
})
```

### Nuxt config / `@scayle/eslint-config-storefront/nuxt`

```js
// eslint.config.mjs
import storefrontNuxt from '@scayle/eslint-config-storefront/nuxt'

export default storefrontNuxt()
```

### Storefront Application V3 config / `@scayle/eslint-config-storefront/v3`

See [Storefront Application V3 projects](#storefront-application-v3-projects) above for the full option list.

All three entry points return a [`FlatConfigComposer`](https://github.com/antfu/eslint-flat-config-utils),
so you can chain `.append(...)`, `.prepend(...)`, `.override(...)`, or `.renamePlugins(...)` to layer
project-specific rules on top.

### Plugin names

Rules use each plugin's own published name (`@typescript-eslint/*`, `import-x/*`, `jsonc/*`,
`jsdoc/*`, `node/*`, `promise/*`, `sonarjs/*`, `unicorn/*`, `regexp/*`, `e18e/*`, `vitest/*`,
`eslint-comments/*`). There's no alias renaming. When you override or disable a rule inline,
use that plugin's real prefix:

```js
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type Foo = { bar: 2 }
```

### `e18e/*` rules ship as warnings

The [e18e](https://e18e.dev) community's rule set (modernization, module-replacement, and
performance rules) is enabled by default, but every `e18e/*` rule ships at `warn`, not `error`.
The plugin is still pre-1.0, and its rule set hasn't been reviewed against every consuming
project. A warning won't fail your build.

One rule worth knowing about specifically: `e18e/ban-dependencies` flags a dependency in your
project that has a lighter recommended alternative (for example `ora` in favor of
`nanospinner`). Treat that finding as something to evaluate, not something to fix immediately.
Swapping a dependency is a real decision with its own testing cost, not a lint fix. If you've
decided to keep a flagged dependency, allowlist it explicitly rather than turning the rule off:

```js
export default storefront().append({
  rules: {
    'e18e/ban-dependencies': ['warn', { allowed: ['ora'] }],
  },
})
```

## View what rules are enabled

[@eslint/config-inspector](https://github.com/eslint/config-inspector) is a visual tool maintained
by the ESLint core team to help you view what rules are enabled in your project and which files
they apply to.

Go into your project root that contains eslint.config.js and run:

`npx @eslint/config-inspector`

## License

Licensed under the [MIT License](https://opensource.org/license/mit/)
