@scayle/eslint-config-storefront 4.8.3-alpha.1 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ import tseslint from 'typescript-eslint'
2
+ import vueParser from 'vue-eslint-parser'
3
+ import pluginVue from 'eslint-plugin-vue'
4
+ import pluginVueA11y from 'eslint-plugin-vuejs-accessibility'
5
+
6
+ const GLOB_VUE = ['**/*.vue']
7
+
8
+ // Vue parser wiring so `.vue` files (including `<script setup lang="ts">`) parse correctly.
9
+ /** @returns {import('eslint').Linter.Config[]} Flat config entries wiring the Vue parser. */
10
+ function vueLanguageConfig() {
11
+ return [
12
+ ...pluginVue.configs['flat/essential'],
13
+ ...pluginVue.configs['flat/strongly-recommended'],
14
+ ...pluginVue.configs['flat/recommended'],
15
+ {
16
+ name: 'storefront/v3/vue-language',
17
+ files: GLOB_VUE,
18
+ languageOptions: {
19
+ parser: vueParser,
20
+ parserOptions: {
21
+ ecmaVersion: 'latest',
22
+ parser: tseslint.parser,
23
+ sourceType: 'module',
24
+ extraFileExtensions: ['.vue'],
25
+ ecmaFeatures: { jsx: true },
26
+ },
27
+ },
28
+ },
29
+ ]
30
+ }
31
+
32
+ /**
33
+ * @param {object} [config]
34
+ * @param {string[]} [config.undefComponentIgnores] Custom element tag names (e.g. web
35
+ * components) to exempt from `vue/no-undef-components`.
36
+ * @returns {import('eslint').Linter.Config[]} Flat config entries for Vue-specific rule overrides.
37
+ */
38
+ function vueRulesConfig(config = {}) {
39
+ const { undefComponentIgnores = [] } = config
40
+
41
+ return [
42
+ {
43
+ name: 'storefront/v3/vue-rules',
44
+ files: GLOB_VUE,
45
+ plugins: { vue: pluginVue },
46
+ rules: {
47
+ 'eslint-comments/no-unlimited-disable': 'off',
48
+ // Core no-unused-vars isn't type-aware and misreads TypeScript-only
49
+ // constructs in <script setup lang="ts"> as real unused bindings.
50
+ // typescript.mjs only scopes the @typescript-eslint replacement to
51
+ // .ts/.tsx/.mts/.cts, so .vue files need their own registration.
52
+ 'no-unused-vars': 'off',
53
+ '@typescript-eslint/no-unused-vars': [
54
+ 'error',
55
+ {
56
+ args: 'all',
57
+ argsIgnorePattern: '^_',
58
+ caughtErrors: 'all',
59
+ caughtErrorsIgnorePattern: '^_',
60
+ destructuredArrayIgnorePattern: '^_',
61
+ varsIgnorePattern: '^_',
62
+ ignoreRestSiblings: true,
63
+ },
64
+ ],
65
+ '@typescript-eslint/no-empty-object-type': [
66
+ 'error',
67
+ { allowInterfaces: 'always' },
68
+ ],
69
+ 'vue/html-self-closing': [
70
+ 'error',
71
+ {
72
+ html: { void: 'any', normal: 'any', component: 'always' },
73
+ },
74
+ ],
75
+ 'vue/v-bind-style': [
76
+ 'error',
77
+ 'shorthand',
78
+ { sameNameShorthand: 'never' },
79
+ ],
80
+ 'vue/no-undef-components': [
81
+ 'error',
82
+ { ignorePatterns: undefComponentIgnores },
83
+ ],
84
+ 'vue/define-emits-declaration': ['error', 'type-literal'],
85
+ 'vue/custom-event-name-casing': ['error', 'camelCase'],
86
+ 'vue/slot-name-casing': ['error', 'kebab-case'],
87
+ 'vue/require-default-prop': 'off',
88
+ 'vue/multi-word-component-names': 'warn',
89
+ 'vue/block-order': [
90
+ 'error',
91
+ { order: ['template', 'script', 'style'] },
92
+ ],
93
+ // Vue Styling
94
+ // Based on https://github.com/nuxt/eslint/blob/main/packages/eslint-config/src/configs/vue.ts#L142
95
+ 'vue/html-closing-bracket-newline': 'off',
96
+ 'vue/html-closing-bracket-spacing': 'off',
97
+ 'vue/html-indent': 'off',
98
+ 'vue/html-quotes': 'off',
99
+ 'vue/max-attributes-per-line': 'off',
100
+ 'vue/multiline-html-element-content-newline': 'off',
101
+ 'vue/mustache-interpolation-spacing': 'off',
102
+ 'vue/no-multi-spaces': 'off',
103
+ 'vue/no-spaces-around-equal-signs-in-attribute': 'off',
104
+ 'vue/singleline-html-element-content-newline': 'off',
105
+ },
106
+ },
107
+ ]
108
+ }
109
+
110
+ // Vue accessibility. Every rule ships `off` by default: this preset's baseline reflects
111
+ // that a11y coverage is opt-in until a consumer's own audit turns rules on deliberately,
112
+ // not a statement that these rules don't matter.
113
+ /** @returns {import('eslint').Linter.Config[]} Flat config entries for Vue accessibility rules, defaulted off. */
114
+ function vueA11yConfig() {
115
+ return [
116
+ ...pluginVueA11y.configs['flat/recommended'],
117
+ {
118
+ name: 'storefront/v3/vue-a11y',
119
+ files: GLOB_VUE,
120
+ rules: {
121
+ 'vuejs-accessibility/mouse-events-have-key-events': 'off',
122
+ 'vuejs-accessibility/no-static-element-interactions': 'off',
123
+ 'vuejs-accessibility/anchor-has-content': 'off',
124
+ 'vuejs-accessibility/aria-props': 'off',
125
+ 'vuejs-accessibility/aria-role': 'off',
126
+ 'vuejs-accessibility/aria-unsupported-elements': 'off',
127
+ 'vuejs-accessibility/click-events-have-key-events': 'off',
128
+ 'vuejs-accessibility/form-control-has-label': 'off',
129
+ 'vuejs-accessibility/heading-has-content': 'off',
130
+ 'vuejs-accessibility/iframe-has-title': 'off',
131
+ 'vuejs-accessibility/interactive-supports-focus': 'off',
132
+ 'vuejs-accessibility/label-has-for': 'off',
133
+ 'vuejs-accessibility/media-has-caption': 'off',
134
+ 'vuejs-accessibility/no-access-key': 'off',
135
+ 'vuejs-accessibility/no-autofocus': 'off',
136
+ 'vuejs-accessibility/no-distracting-elements': 'off',
137
+ 'vuejs-accessibility/no-redundant-roles': 'off',
138
+ 'vuejs-accessibility/role-has-required-aria-props': 'off',
139
+ 'vuejs-accessibility/tabindex-no-positive': 'off',
140
+ },
141
+ },
142
+ ]
143
+ }
144
+
145
+ /**
146
+ * @param {object} [config]
147
+ * @param {string[]} [config.undefComponentIgnores] Custom element tag names exempt from
148
+ * `vue/no-undef-components`.
149
+ * @returns {import('eslint').Linter.Config[]} Flat config entries for Vue language wiring, rules, and accessibility.
150
+ */
151
+ export function vueConfig(config = {}) {
152
+ return [...vueLanguageConfig(), ...vueRulesConfig(config), ...vueA11yConfig()]
153
+ }
@@ -0,0 +1,29 @@
1
+ import vueComposable from '@scayle/eslint-plugin-vue-composable'
2
+
3
+ /**
4
+ * @param {object} [config]
5
+ * @param {string[]} [config.fallthroughAttrsIgnores] Path fragments (e.g. `/src/client/pages/`)
6
+ * exempt from `require-explicit-fallthrough-attrs`, for components that never receive
7
+ * fallthrough attributes (page/layout roots mounted by a router).
8
+ * @returns {import('eslint').Linter.Config[]} Flat config entries for `@scayle/vue-composable` rules.
9
+ */
10
+ export function vueComposableConfig(config = {}) {
11
+ const { fallthroughAttrsIgnores = [] } = config
12
+
13
+ return [
14
+ {
15
+ name: 'storefront/v3/vue-composable',
16
+ plugins: { '@scayle/vue-composable': vueComposable },
17
+ rules: {
18
+ '@scayle/vue-composable/no-composable-after-await': 'error',
19
+ '@scayle/vue-composable/no-lifecycle-after-await': 'error',
20
+ '@scayle/vue-composable/no-watch-after-await': 'error',
21
+ '@scayle/vue-composable/no-computed-after-await': 'error',
22
+ '@scayle/vue-composable/require-explicit-fallthrough-attrs': [
23
+ 'error',
24
+ { ignores: fallthroughAttrsIgnores },
25
+ ],
26
+ },
27
+ },
28
+ ]
29
+ }
@@ -0,0 +1,52 @@
1
+ import vueI18n from '@intlify/eslint-plugin-vue-i18n'
2
+
3
+ /**
4
+ * @param {object} config
5
+ * @param {string} config.localeDir Glob pointing at the project's locale JSON files
6
+ * (e.g. `./src/i18n/locales/*.json`), passed to the `vue-i18n` plugin's `localeDir` setting.
7
+ * @param {string} [config.messageSyntaxVersion] `vue-i18n` message syntax version.
8
+ * @param {string[]} [config.unusedKeyIgnores] Regex-string patterns (e.g. `/foo.bar/`) for keys
9
+ * used dynamically in code, which `no-unused-keys` can't detect statically.
10
+ * @returns {import('eslint').Linter.Config[]} Flat config entries for `vue-i18n` linting.
11
+ */
12
+ export function vueI18nConfig(config) {
13
+ const {
14
+ localeDir,
15
+ messageSyntaxVersion = '^11.2.8',
16
+ unusedKeyIgnores = [],
17
+ } = config ?? {}
18
+
19
+ if (!localeDir) {
20
+ throw new Error('storefrontV3: config.i18n.localeDir is required')
21
+ }
22
+
23
+ return [
24
+ ...vueI18n.configs['flat/base'],
25
+ {
26
+ name: 'storefront/v3/vue-i18n',
27
+ rules: {
28
+ '@intlify/vue-i18n/valid-message-syntax': 'error',
29
+ // Passes when any of the locale files contains a used key
30
+ '@intlify/vue-i18n/no-missing-keys': 'error',
31
+ // Passes when all locale files contain the same keys.
32
+ '@intlify/vue-i18n/no-missing-keys-in-other-locales': process.env.CI
33
+ ? 'error'
34
+ : 'warn',
35
+ '@intlify/vue-i18n/no-duplicate-keys-in-locale': 'error',
36
+ '@intlify/vue-i18n/no-dynamic-keys': 'off',
37
+ // Performance-intensive: scans all matching files for unused translation keys.
38
+ '@intlify/vue-i18n/no-unused-keys': [
39
+ 'error',
40
+ {
41
+ src: './src/{client,server,shared}/**',
42
+ extensions: ['.vue', '.ts'],
43
+ ignores: unusedKeyIgnores,
44
+ },
45
+ ],
46
+ },
47
+ settings: {
48
+ 'vue-i18n': { localeDir, messageSyntaxVersion },
49
+ },
50
+ },
51
+ ]
52
+ }
package/config/v3.mjs ADDED
@@ -0,0 +1,66 @@
1
+ import { FlatConfigComposer } from 'eslint-flat-config-utils'
2
+ import { storefront } from './index.mjs'
3
+ import { vueConfig } from './v3/vue.mjs'
4
+ import { vueI18nConfig } from './v3/vueI18n.mjs'
5
+ import { vueComposableConfig } from './v3/vueComposable.mjs'
6
+ import { storybookConfig } from './v3/storybook.mjs'
7
+ import { tailwindConfig } from './v3/tailwind.mjs'
8
+ import {
9
+ importResolverConfig,
10
+ importOrderConfig,
11
+ } from './v3/importResolver.mjs'
12
+
13
+ /**
14
+ * @typedef {object} EslintConfigStorefrontV3Options
15
+ * @property {object} [typescript] Options forwarded to `typescriptConfig` (see `storefront`).
16
+ * @property {string} [typescript.tsconfigPath] Enables type-aware TypeScript rules when set.
17
+ * @property {object} [resolver] Import resolver settings.
18
+ * @property {string} [resolver.tsconfigPath] Absolute path to the tsconfig.json the import
19
+ * resolver reads `paths` aliases from. Independent of `typescript.tsconfigPath`: passing
20
+ * an absolute path here does not switch on type-aware TypeScript rules. Omit to fall back
21
+ * to `typescript.tsconfigPath`, then to the resolver's own `<cwd>/tsconfig.json` default,
22
+ * which only resolves correctly when ESLint's working directory happens to be this project's
23
+ * root (true for `pnpm lint`/CI via Turborepo, not guaranteed for an editor's ESLint
24
+ * extension). Set this explicitly, as an absolute path anchored to the config file's own
25
+ * location, if ESLint might ever run with a different working directory.
26
+ * @property {object} tailwind Tailwind CSS settings.
27
+ * @property {string} tailwind.entryPoint Path to the project's Tailwind CSS entry point.
28
+ * @property {object} i18n `vue-i18n` linting settings.
29
+ * @property {string} i18n.localeDir Glob pointing at the project's locale JSON files.
30
+ * @property {string} [i18n.messageSyntaxVersion] `vue-i18n` message syntax version.
31
+ * @property {string[]} [i18n.unusedKeyIgnores] Regex-string patterns for dynamically-used keys.
32
+ * @property {object} [vue] Vue rule settings.
33
+ * @property {string[]} [vue.undefComponentIgnores] Custom element tag names exempt from `vue/no-undef-components`.
34
+ * @property {object} [vueComposable] `@scayle/vue-composable` rule settings.
35
+ * @property {string[]} [vueComposable.fallthroughAttrsIgnores] Path fragments exempt from `require-explicit-fallthrough-attrs`.
36
+ */
37
+
38
+ /**
39
+ * Builds the SCAYLE Storefront ESLint flat config for the Storefront Application V3 stack
40
+ * (Hono + Inertia.js + Vue 3, not Nuxt). Extends `storefront()` with Vue language wiring,
41
+ * Vue rules, accessibility rules, `vue-i18n` rules, `@scayle/vue-composable` rules,
42
+ * Storybook file conventions, Tailwind CSS class validation, and a TypeScript-aware
43
+ * import resolver. Kept separate from `storefront()`/`storefrontNuxt()` since those two
44
+ * stay framework-agnostic.
45
+ *
46
+ * @param {EslintConfigStorefrontV3Options} config
47
+ * @returns {FlatConfigComposer<import('eslint').Linter.Config, never>} Returns a FlatConfigComposer object to be consumed in projects eslint.config.mjs files
48
+ */
49
+ export const storefrontV3 = function (config) {
50
+ const { typescript, resolver, tailwind, i18n, vue, vueComposable } =
51
+ config ?? {}
52
+
53
+ /** @type {FlatConfigComposer<import('eslint').Linter.Config, never>} */
54
+ const flatConfigComposer = new FlatConfigComposer()
55
+
56
+ return flatConfigComposer.append(
57
+ storefront({ typescript }),
58
+ ...tailwindConfig(tailwind),
59
+ ...storybookConfig(),
60
+ ...vueConfig(vue),
61
+ ...vueI18nConfig(i18n),
62
+ ...vueComposableConfig(vueComposable),
63
+ ...importResolverConfig(resolver?.tsconfigPath ?? typescript?.tsconfigPath),
64
+ ...importOrderConfig(),
65
+ )
66
+ }
@@ -0,0 +1,128 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { storefrontV3 } from './v3.mjs'
3
+
4
+ describe('storefrontV3', () => {
5
+ it('throws without tailwind.entryPoint', () => {
6
+ expect(() =>
7
+ // @ts-expect-error missing required `tailwind`, exercising the runtime guard
8
+ storefrontV3({ i18n: { localeDir: './src/i18n/locales/*.json' } }),
9
+ ).toThrow('config.tailwind.entryPoint is required')
10
+ })
11
+
12
+ it('throws without i18n.localeDir', () => {
13
+ expect(() =>
14
+ // @ts-expect-error missing required `i18n`, exercising the runtime guard
15
+ storefrontV3({ tailwind: { entryPoint: './src/client/index.css' } }),
16
+ ).toThrow('config.i18n.localeDir is required')
17
+ })
18
+
19
+ it('returns a config with both required options set', async () => {
20
+ const config = storefrontV3({
21
+ tailwind: { entryPoint: './src/client/index.css' },
22
+ i18n: { localeDir: './src/i18n/locales/*.json' },
23
+ })
24
+
25
+ const resolved = await config.toConfigs()
26
+
27
+ expect(Array.isArray(resolved)).toBe(true)
28
+ expect(resolved.length).toBeGreaterThan(0)
29
+ })
30
+
31
+ // These settings have no test-visible failure mode of their own: a reverted or
32
+ // misconfigured resolver/extensions/groups entry still produces a config that loads
33
+ // and lints without error, it just silently stops catching what it's meant to catch.
34
+ // Asserting the shape here is what makes a quiet revert fail CI instead of passing it.
35
+ it('wires the import resolver and no-cycle extensions', async () => {
36
+ const config = storefrontV3({
37
+ tailwind: { entryPoint: './src/client/index.css' },
38
+ i18n: { localeDir: './src/i18n/locales/*.json' },
39
+ })
40
+
41
+ const resolved = await config.toConfigs()
42
+ const resolverEntry = resolved.find(
43
+ (c) => c.name === 'storefront/v3/import-resolver',
44
+ )
45
+ if (!resolverEntry) {
46
+ throw new Error('resolverEntry not found')
47
+ }
48
+ const { settings } = resolverEntry
49
+ if (!settings) {
50
+ throw new Error('resolverEntry.settings not found')
51
+ }
52
+
53
+ const resolverNext =
54
+ /** @type {{ name: string, interfaceVersion: number }[]} */ (
55
+ settings['import-x/resolver-next']
56
+ )
57
+ expect(resolverNext).toHaveLength(1)
58
+ expect(resolverNext[0]).toMatchObject({
59
+ name: 'eslint-import-resolver-typescript',
60
+ interfaceVersion: 3,
61
+ })
62
+ expect(settings['import-x/extensions']).toEqual([
63
+ '.js',
64
+ '.mjs',
65
+ '.cjs',
66
+ '.ts',
67
+ '.tsx',
68
+ ])
69
+ expect(settings['import-x/extensions']).not.toContain('.vue')
70
+ })
71
+
72
+ it('ranks internal imports alongside external ones in import-x/order', async () => {
73
+ const config = storefrontV3({
74
+ tailwind: { entryPoint: './src/client/index.css' },
75
+ i18n: { localeDir: './src/i18n/locales/*.json' },
76
+ })
77
+
78
+ const resolved = await config.toConfigs()
79
+ const orderEntry = resolved.find(
80
+ (c) => c.name === 'storefront/v3/import-order',
81
+ )
82
+ if (!orderEntry) {
83
+ throw new Error('orderEntry not found')
84
+ }
85
+ const { rules } = orderEntry
86
+ if (!rules) {
87
+ throw new Error('orderEntry.rules not found')
88
+ }
89
+ const orderRule = rules['import-x/order']
90
+ if (!orderRule) {
91
+ throw new Error('import-x/order rule not found')
92
+ }
93
+ const [severity, options] = /** @type {[string, { groups: unknown[] }]} */ (
94
+ orderRule
95
+ )
96
+
97
+ expect(severity).toBe('error')
98
+ expect(options.groups).toEqual([
99
+ 'builtin',
100
+ ['external', 'internal'],
101
+ 'parent',
102
+ 'sibling',
103
+ 'index',
104
+ ])
105
+ })
106
+
107
+ it('accepts resolver.tsconfigPath independently of typescript.tsconfigPath', async () => {
108
+ // Both must resolve without throwing: resolver.tsconfigPath alone (no type-aware
109
+ // TypeScript rules switched on) and typescript.tsconfigPath alone (the fallback
110
+ // path, still feeding the resolver). The resolver's own internals aren't asserted
111
+ // on here, only that both option shapes are accepted.
112
+ await expect(
113
+ storefrontV3({
114
+ tailwind: { entryPoint: './src/client/index.css' },
115
+ i18n: { localeDir: './src/i18n/locales/*.json' },
116
+ resolver: { tsconfigPath: '/absolute/resolver-tsconfig.json' },
117
+ }).toConfigs(),
118
+ ).resolves.toBeInstanceOf(Array)
119
+
120
+ await expect(
121
+ storefrontV3({
122
+ tailwind: { entryPoint: './src/client/index.css' },
123
+ i18n: { localeDir: './src/i18n/locales/*.json' },
124
+ typescript: { tsconfigPath: '/absolute/typescript-tsconfig.json' },
125
+ }).toConfigs(),
126
+ ).resolves.toBeInstanceOf(Array)
127
+ })
128
+ })
package/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
- import { eslintConfigStorefront } from './config/flat/index.mjs'
1
+ import { storefront } from './config/index.mjs'
2
2
 
3
- export default eslintConfigStorefront
3
+ export default storefront
4
+ export { storefront }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/eslint-config-storefront",
3
- "version": "4.8.3-alpha.1",
3
+ "version": "5.1.0",
4
4
  "description": "SCAYLE Commerce Engine specific eslint configuration",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -17,46 +17,66 @@
17
17
  "type": "module",
18
18
  "exports": {
19
19
  ".": "./index.mjs",
20
- "./config/flat/index.mjs": "./config/flat/index.mjs",
21
- "./config/flat/sonarjs": "./config/flat/sonarjs.mjs",
22
- "./config/flat/promise.mjs": "./config/flat/promise.mjs",
23
- "./config/flat/importX.mjs": "./config/flat/importX.mjs"
20
+ "./nuxt": "./storefront-nuxt.mjs",
21
+ "./v3": "./storefront-v3.mjs"
24
22
  },
25
23
  "main": "./index.mjs",
26
24
  "files": [
27
25
  "CHANGELOG.md",
28
26
  "README.md",
29
27
  "./index.mjs",
28
+ "./storefront-nuxt.mjs",
29
+ "./storefront-v3.mjs",
30
30
  "./config/"
31
31
  ],
32
32
  "engines": {
33
33
  "node": ">= 22.0.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "eslint": ">=9.0.0",
36
+ "eslint": ">=10.4.0",
37
37
  "typescript": ">=5.4.0"
38
38
  },
39
39
  "dependencies": {
40
- "@antfu/eslint-config": "^9.0.0",
41
- "@eslint/eslintrc": "^3.3.5",
42
- "eslint-config-flat-gitignore": "^2.2.0",
40
+ "@e18e/eslint-plugin": "^0.8.0",
41
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.7.2",
42
+ "@eslint/js": "^10.0.0",
43
+ "@intlify/eslint-plugin-vue-i18n": "^4.5.1",
44
+ "@scayle/eslint-plugin-vue-composable": "1.2.1",
45
+ "@vitest/eslint-plugin": "^1.6.20",
46
+ "eslint-config-flat-gitignore": "^2.3.0",
43
47
  "eslint-flat-config-utils": "^3.2.0",
44
- "eslint-plugin-import-x": "^4.16.2",
48
+ "eslint-import-resolver-typescript": "^4.4.5",
49
+ "eslint-plugin-better-tailwindcss": "^4.7.0",
50
+ "eslint-plugin-import-x": "^4.17.1",
51
+ "eslint-plugin-jsdoc": "^64.2.1",
52
+ "eslint-plugin-jsonc": "^3.4.1",
53
+ "eslint-plugin-n": "^18.3.0",
54
+ "eslint-plugin-no-only-tests": "^3.4.0",
45
55
  "eslint-plugin-promise": "^7.3.0",
46
- "eslint-plugin-sonarjs": "^4.0.3",
47
- "local-pkg": "^1.2.1",
48
- "typescript-eslint": "8.67.0"
56
+ "eslint-plugin-regexp": "^3.2.0",
57
+ "eslint-plugin-sonarjs": "^4.2.0",
58
+ "eslint-plugin-storybook": "^10.5.8",
59
+ "eslint-plugin-unicorn": "^73.0.0",
60
+ "eslint-plugin-vue": "^10.10.0",
61
+ "eslint-plugin-vuejs-accessibility": "^2.6.0",
62
+ "globals": "^17.7.0",
63
+ "typescript-eslint": "^8.67.0",
64
+ "vue-eslint-parser": "^10.4.1"
49
65
  },
50
66
  "devDependencies": {
51
- "eslint": "10.8.1",
67
+ "@types/node": "^24",
68
+ "@vitest/coverage-v8": "4.1.11",
69
+ "eslint": "10.9.1",
52
70
  "eslint-formatter-gitlab": "7.2.0",
53
- "publint": "0.3.23",
54
- "typescript": "6.0.3"
71
+ "publint": "0.3.24",
72
+ "typescript": "6.0.3",
73
+ "vitest": "4.1.11"
55
74
  },
56
75
  "scripts": {
57
76
  "lint": "eslint .",
58
77
  "lint:ci": "eslint . --format gitlab",
59
78
  "lint:fix": "eslint . --fix",
60
- "package:lint": "publint"
79
+ "package:lint": "publint",
80
+ "typecheck": "tsc"
61
81
  }
62
82
  }
@@ -0,0 +1,4 @@
1
+ import { storefrontNuxt } from './config/index.mjs'
2
+
3
+ export default storefrontNuxt
4
+ export { storefrontNuxt }
@@ -0,0 +1,4 @@
1
+ import { storefrontV3 } from './config/v3.mjs'
2
+
3
+ export default storefrontV3
4
+ export { storefrontV3 }
@@ -1,22 +0,0 @@
1
- import { importX as pluginImportX } from 'eslint-plugin-import-x'
2
-
3
- // Pre-defined rule configurations for better performance
4
- const IMPORT_X_RULES = {
5
- 'import-x/consistent-type-specifier-style': ['error', 'prefer-top-level'],
6
- 'import-x/first': 'error',
7
- 'import-x/no-duplicates': 'error',
8
- 'import-x/no-mutable-exports': 'error',
9
- 'import-x/no-named-default': 'error',
10
- 'import-x/no-self-import': 'error',
11
- 'import-x/no-extraneous-dependencies': 'error',
12
- }
13
-
14
- export function importX() {
15
- return [
16
- {
17
- name: 'storefront/import-x/rules',
18
- plugins: { 'import-x': pluginImportX },
19
- rules: IMPORT_X_RULES,
20
- },
21
- ]
22
- }