fg-devkit 1.0.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,203 @@
1
+ ---
2
+ description: Core coding standards for Vue / fds-vue-core frontend projects
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Core Coding Standards
7
+
8
+ ## Keep changes focused
9
+
10
+ - Make the smallest safe change that solves the request.
11
+ - Avoid unrelated refactors in the same task.
12
+ - Preserve existing behavior unless a behavior change is requested.
13
+
14
+ ## Communication efficiency
15
+
16
+ - Do not waste tokens: keep responses concise, high-signal, and scoped to the request.
17
+ - Prefer short summaries over long explanations unless deeper detail is explicitly requested.
18
+
19
+ ## Evidence-first implementation
20
+
21
+ - Never guess or hallucinate APIs, component props, or framework behavior.
22
+ - Verify implementation details against authoritative sources first (official docs, project code, and library types/source).
23
+ - If documentation is missing or ambiguous, state uncertainty explicitly and choose the safest validated approach.
24
+
25
+ ## Match project conventions
26
+
27
+ - Follow established naming, file structure, and component patterns.
28
+ - Reuse existing utilities and query/composable patterns before adding new ones.
29
+ - Keep TypeScript types explicit at API boundaries.
30
+
31
+ ## Strict TypeScript
32
+
33
+ - Keep TypeScript and Vue template typing strict (`strict: true`, `strictTemplates: true`).
34
+ - Do not introduce `any`; use `unknown` for unsafe input and narrow before use.
35
+ - Type API boundaries explicitly (request payloads, response DTOs, mutation variables, callback contracts).
36
+ - Prefer named reusable types (for example under `src/types`) for shared domain shapes.
37
+ - Avoid non-null assertions unless guarded by `enabled` checks or equivalent runtime guarantees.
38
+
39
+ ## API boundaries
40
+
41
+ - Components and views should not call low-level HTTP/API clients directly for feature data fetching.
42
+ - Centralize HTTP and endpoint wiring in a dedicated API layer (for example `src/api/endpoints/*` or the convention used in the repo).
43
+ - Consume server state through query/mutation hooks (for example `src/queries/*` or equivalent), not ad-hoc fetches in UI code.
44
+
45
+ ## State-management boundaries
46
+
47
+ - Use TanStack Query (or the project’s chosen data library) for server state and caching.
48
+ - Use Pinia/Vuex only for client/app state that is not owned by the server.
49
+ - Do not duplicate the same state across the query cache and global stores unless the project explicitly documents that pattern.
50
+
51
+ ## TanStack Query server-state conventions
52
+
53
+ - Use TanStack Query for server state; avoid duplicating server data in ad-hoc local state.
54
+ - Keep API calls in the API layer and expose query/mutation hooks from a dedicated hooks folder.
55
+ - Build query keys as stable arrays of serializable primitives; when using refs, key by `.value` in a computed key.
56
+ - Prefer shared query key constants/factories to keep invalidation and cache updates consistent.
57
+ - Add `enabled` guards for required IDs/paths to prevent invalid requests.
58
+ - Use `placeholderData: keepPreviousData` for paginated/filter-driven views to avoid UI flicker.
59
+ - Set `staleTime` intentionally per resource; keep `refetchOnWindowFocus` off unless fresh-on-focus behavior is required.
60
+ - On mutation success, update cache intentionally (`setQueryData`) and/or invalidate the narrowest matching keys.
61
+ - Respect shared query client retry behavior and only override retries when endpoint-specific behavior is needed.
62
+
63
+ ## Mutation cache behavior
64
+
65
+ - Every mutation should define post-success cache behavior.
66
+ - Use `invalidateQueries` for stale collections and `setQueryData` for direct entity updates.
67
+ - Invalidate the narrowest query keys that match changed resources.
68
+
69
+ ## Vue Router conventions
70
+
71
+ - Organize route definitions in modules (for example `src/router/modules/*`) composed from a single router entry (for example `src/router/index.ts`), matching the repo layout.
72
+ - Every user-facing route should define `meta.title`; provide breadcrumb metadata for detail/flow routes when needed.
73
+ - Use shared route guards for auth/anonymous access control instead of duplicating checks in views.
74
+ - Prefer `props: true` and typed route params in components instead of reading raw params everywhere.
75
+ - Prefer named-route navigation for internal flows (`{ name, params, query }`) and reserve raw path pushes for simple/static redirects.
76
+ - Preserve existing query params intentionally when updating filters, pagination, or tab state.
77
+ - Lazy-load route components with dynamic imports to keep initial bundles smaller.
78
+
79
+ ## Vue and UI expectations
80
+
81
+ - Prefer clear, readable templates over compact but hard-to-follow expressions.
82
+ - Keep components focused; extract repeated UI blocks when duplication grows.
83
+ - Keep localization text in language files instead of hardcoded UI strings.
84
+
85
+ ## FDS-first component usage
86
+
87
+ - Use `fds-vue-core` components as the default choice for UI building blocks.
88
+ - Before adding custom UI, check whether an FDS component already solves the use case.
89
+ - Prefer extending composition around FDS components over re-implementing design-system behavior.
90
+ - Keep visual consistency by matching FDS variants, spacing patterns, and interaction styles.
91
+
92
+ ## Asset creation policy
93
+
94
+ - Do not create icons, images, illustrations, or placeholder content unless explicitly requested.
95
+ - Assume `fds-vue-core` provides the required visual assets and use those first.
96
+ - If a required asset appears missing, clearly point out the gap instead of inventing a replacement.
97
+
98
+ ## FDS typography (fds-vue-core)
99
+
100
+ - For headings, use `FdsH1`, `FdsH2`, and `FdsH3` (not raw `h1/h2/h3`).
101
+ - `FdsH1/FdsH2/FdsH3` use the `text` prop for content and support `size` (`sm|md|lg|xl|responsive`).
102
+ - When layout spacing needs to be neutralized, reset heading spacing with `class="m-0"` (and add explicit margins as needed).
103
+ - Use `FdsLead` for intro/lead paragraphs, `FdsMeta` for metadata/secondary text, and `FdsLabel` for short labels.
104
+ - `FdsLead`, `FdsMeta`, and `FdsLabel` render slot content; prefer these over custom styled spans for text semantics.
105
+
106
+ ## Tailwind (v4+)
107
+
108
+ - Use Tailwind v4 patterns and utilities (for example `tailwindcss@4` with `@tailwindcss/vite` where applicable).
109
+ - Prefer utility classes in templates; only add custom CSS when utilities cannot express the design.
110
+ - Follow existing FDS token/preset styling (`fds-vue-core` tokens and preset) instead of inventing parallel design tokens.
111
+ - Always use Tailwind/FDS design tokens for colors, spacing, typography, radius, and shadows.
112
+ - Never invent ad-hoc color values (for example hex/rgb/hsl literals) when a tokenized utility exists.
113
+ - Keep spacing and typography explicit in class names; avoid hidden styling in deep selectors.
114
+ - Use `!` utility overrides sparingly and only when a component default cannot be changed safely.
115
+ - For Tailwind v4 important modifiers, use postfix-important utility syntax (for example `grid-cols-1!`) instead of prefix syntax (for example `!grid-cols-1`).
116
+
117
+ ## Figma MCP implementation workflow
118
+
119
+ - When using Figma MCP, implement the design as specified (layout, spacing, typography, states, and interactions).
120
+ - Treat the Figma file as source of truth for UI behavior unless product/technical constraints are explicitly documented.
121
+ - Primary choice for implementation is `fds-vue-core` components that best match the design intent.
122
+ - Only create custom UI when no suitable FDS component exists; keep custom parts minimal and design-token based.
123
+ - If any Figma detail cannot be implemented exactly, document the deviation and choose the closest FDS-aligned solution.
124
+
125
+ ## i18n and content
126
+
127
+ - Do not hardcode user-facing copy in templates or script logic; use `t(...)` keys.
128
+ - Add or update locale entries under the project’s language folder (for example `src/lang/*.json`) when introducing new text.
129
+ - Keep translation keys descriptive and grouped by screen/feature.
130
+ - Avoid storing i18n keys inside data/definition arrays (e.g. `i18nKey`) and translating later via dynamic keys or large switch statements. Prefer building definitions with already-translated `label: t('...')` (often as a `computed`) so types stay strict and templates stay simple.
131
+
132
+ ## Formatting and locale consistency
133
+
134
+ - Prefer shared formatting utilities for date/number/currency output instead of ad-hoc inline formatting.
135
+ - Use Swedish locale conventions (`sv-SE`) unless a feature explicitly requires another locale.
136
+
137
+ ## Accessibility baseline
138
+
139
+ - Use semantic HTML and accessible FDS components first; avoid click-only `div`/`span` patterns.
140
+ - Ensure form controls have proper labels and required ARIA attributes when needed.
141
+ - All interactive elements must be keyboard reachable and show visible focus states.
142
+ - Provide meaningful accessible names for icon-only actions and links.
143
+
144
+ ## Async UI states
145
+
146
+ - For data-driven screens, always handle loading, empty, error, and success states explicitly.
147
+ - Use consistent FDS-based feedback patterns for loaders, alerts, and empty-state messaging.
148
+
149
+ ## Error handling UX
150
+
151
+ - Do not swallow errors silently; surface actionable feedback to users.
152
+ - Use consistent FDS feedback patterns for API failures (alerts/toasts/inline messages).
153
+ - Keep technical error details out of user-facing copy unless explicitly required.
154
+
155
+ ## Performance guardrails
156
+
157
+ - Avoid unnecessary reactive churn (redundant watchers/computed chains in hot paths).
158
+ - Memoize expensive derived values and keep render-time computations lightweight.
159
+ - Keep route-level and heavy feature components lazy-loaded.
160
+
161
+ ## Security and data handling
162
+
163
+ - Never commit or expose secrets, tokens, or sensitive identifiers in code, logs, or UI.
164
+ - Validate and sanitize untrusted values from route/query params before use.
165
+ - Type and validate outgoing payloads to prevent accidental over-posting.
166
+
167
+ ## Testing expectations
168
+
169
+ - For non-trivial logic changes, add or update automated tests close to the affected feature.
170
+ - Validate critical flows for changed views: loading, empty, error, and success states.
171
+ - Keep tests behavior-focused and avoid brittle implementation-detail assertions.
172
+
173
+ ## CI/CD
174
+
175
+ - Treat the repository’s CI configuration as the source of truth (`Jenkinsfile`, GitHub Actions, or equivalent).
176
+ - Prefer frozen lockfile installs in CI (for example `pnpm install --frozen-lockfile --prefer-offline`) so CI matches local installs.
177
+ - Keep build, lint, typecheck, and test stages deterministic; document any project-specific stages (security scans, i18n download, Docker publish) in the repo README or a dedicated rule file.
178
+
179
+ ## Quality checks before finishing
180
+
181
+ - Run relevant checks for touched code (lint, typecheck, or tests when practical).
182
+ - Fix issues introduced by your change before handing off.
183
+ - Call out anything you could not verify.
184
+
185
+ ## Definition of done
186
+
187
+ - Lint and type checks pass for touched code.
188
+ - UI matches Figma/FDS expectations and uses tokenized styling.
189
+ - i18n keys, async states, and cache behavior are handled for changed features.
190
+ - Note any constraints, deviations, or unverified parts in handoff.
191
+
192
+ ## Legacy/deprecation policy
193
+
194
+ - For new work, follow the current preferred patterns in this document.
195
+ - Do not expand usage of known legacy patterns when a supported modern pattern exists.
196
+ - If migration is out of scope, isolate legacy touchpoints and avoid spreading them.
197
+
198
+ ## Example: API data safety
199
+
200
+ ```ts
201
+ // Prefer typed fallbacks when API data may be missing.
202
+ const total = response?.stats?.total ?? 0
203
+ ```
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ PROPRIETARY LICENSE
2
+
3
+ Copyright (c) 2026 the authors
4
+
5
+ All rights reserved.
6
+
7
+ This software and associated documentation files (the "Software") are the
8
+ proprietary and confidential property of the copyright holders.
9
+
10
+ The Software is protected by copyright laws and international copyright
11
+ treaties, as well as other intellectual property laws and treaties.
12
+
13
+ Unauthorized copying, modification, distribution, or use of this Software,
14
+ via any medium, is strictly prohibited and may result in severe civil and
15
+ criminal penalties.
16
+
17
+ The Software is provided "AS IS", without warranty of any kind, express or
18
+ implied, including but not limited to the warranties of merchantability,
19
+ fitness for a particular purpose and noninfringement.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # fg-devkit
2
+
3
+ Shared settings and tools to improve the developer experience for Vue and TypeScript projects in the npm ecosystem.
4
+
5
+ ## Quick command alias (zsh)
6
+
7
+ To run `fg` instead of `pnpm exec fg`, add this alias:
8
+
9
+ ```bash
10
+ echo "alias fg='pnpm exec fg'" >> ~/.zshrc && source ~/.zshrc
11
+ ```
12
+
13
+ This works in repos where `fg-devkit` is installed.
@@ -0,0 +1,116 @@
1
+ {
2
+ "managedDependencies": {
3
+ "dependencies": {
4
+ "@element-plus/icons-vue": "2.3.2",
5
+ "@lexical/html": "0.30.0",
6
+ "@lexical/list": "0.30.0",
7
+ "@lexical/rich-text": "0.30.0",
8
+ "@piwikpro/vue-piwik-pro": "1.4.0",
9
+ "@sentry/browser": "7.67.0",
10
+ "@sentry/tracing": "7.37.2",
11
+ "@sentry/vue": "7.37.2",
12
+ "@sentry/wasm": "7.67.0",
13
+ "@tailwindcss/postcss": "4.2.2",
14
+ "@tanstack/vue-query": "5.96.2",
15
+ "@types/leaflet": "1.5.7",
16
+ "@unhead/vue": "2.0.5",
17
+ "@vue-leaflet/vue-leaflet": "0.10.1",
18
+ "@vuelidate/core": "2.0.3",
19
+ "@vuelidate/validators": "2.0.4",
20
+ "@vueuse/components": "12.7.0",
21
+ "@vueuse/core": "14.2.1",
22
+ "chart.js": "4.5.1",
23
+ "content-disposition-header": "0.6.0",
24
+ "date-fns": "4.1.0",
25
+ "element-plus": "2.9.7",
26
+ "fds-vue-core": "6.2.1",
27
+ "imask": "7.6.1",
28
+ "leaflet": "1.8.0",
29
+ "lexical": "0.30.0",
30
+ "lodash": "4.17.23",
31
+ "lodash-es": "4.18.1",
32
+ "markdown-it": "14.1.1",
33
+ "pinia": "3.0.4",
34
+ "playwright": "1.57.0",
35
+ "quill": "2.0.3",
36
+ "randexp": "0.5.3",
37
+ "register-service-worker": "1.7.2",
38
+ "vega-embed": "7.1.0",
39
+ "vega-lite": "6.4.1",
40
+ "vue-chartjs": "5.3.3",
41
+ "vue-i18n": "11.3.2",
42
+ "vue-quilly": "1.0.5",
43
+ "vue-router": "4.6.3",
44
+ "vue3-touch-keyboard": "1.3.9",
45
+ "vuex": "4.1.0",
46
+ "vuex-persist": "3.1.3",
47
+ "yup": "1.7.1"
48
+ },
49
+ "devDependencies": {
50
+ "@chromatic-com/storybook": "5.0.1",
51
+ "@eslint/js": "9.39.1",
52
+ "@intlify/eslint-plugin-vue-i18n": "4.3.0",
53
+ "@intlify/unplugin-vue-i18n": "11.0.7",
54
+ "@playwright/test": "1.59.1",
55
+ "@rushstack/eslint-patch": "1.16.1",
56
+ "@storybook/addon-a11y": "10.1.10",
57
+ "@storybook/addon-docs": "10.1.10",
58
+ "@storybook/addon-vitest": "10.2.12",
59
+ "@storybook/vue3": "10.2.12",
60
+ "@storybook/vue3-vite": "10.2.12",
61
+ "@tanstack/vue-query-devtools": "6.1.13",
62
+ "@tsconfig/node22": "22.0.5",
63
+ "@types/lodash-es": "4.17.12",
64
+ "@types/node": "22.16.5",
65
+ "@typescript-eslint/eslint-plugin": "8.57.2",
66
+ "@typescript-eslint/parser": "8.57.2",
67
+ "@vitest/browser": "3.2.4",
68
+ "@vitest/eslint-plugin": "1.6.14",
69
+ "@vue/compiler-dom": "3.5.29",
70
+ "@vue/eslint-config-prettier": "10.2.0",
71
+ "@vue/eslint-config-standard": "8.0.1",
72
+ "@vue/eslint-config-typescript": "14.7.0",
73
+ "@vue/server-renderer": "3.5.29",
74
+ "@vue/test-utils": "2.4.6",
75
+ "@vue/tsconfig": "0.8.1",
76
+ "@vuepic/vue-datepicker": "6.0.3",
77
+ "arg": "5.0.2",
78
+ "babel-eslint": "10.1.0",
79
+ "commitizen": "4.3.1",
80
+ "eslint": "10.0.2",
81
+ "eslint-plugin-prettier": "5.5.5",
82
+ "eslint-plugin-storybook": "10.1.10",
83
+ "eslint-plugin-vue": "10.8.0",
84
+ "globals": "15.11.0",
85
+ "husky": "9.1.6",
86
+ "jsdom": "26.1.0",
87
+ "jsonc-eslint-parser": "2.4.1",
88
+ "lint-staged": "16.4.0",
89
+ "postcss": "8.5.6",
90
+ "prettier": "3.8.1",
91
+ "sass": "1.97.3",
92
+ "start-server-and-test": "2.1.3",
93
+ "storybook": "10.2.12",
94
+ "tsx": "4.21.0",
95
+ "typescript": "5.9.3",
96
+ "vega": "6.2.0",
97
+ "vite-plugin-dts": "4.5.4",
98
+ "vite-plugin-vue-devtools": "8.0.7",
99
+ "vitest": "3.2.4",
100
+ "vue-eslint-parser": "10.4.0",
101
+ "vue-tsc": "3.2.6",
102
+ "yaml": "2.8.3",
103
+ "yaml-eslint-parser": "1.3.1"
104
+ }
105
+ },
106
+ "managedAnywhere": {
107
+ "@tailwindcss/vite": "4.2.2",
108
+ "@vitejs/plugin-vue": "6.0.5",
109
+ "axios": "1.15.0",
110
+ "dotenv": "16.4.5",
111
+ "qrcode.vue": "3.8.1",
112
+ "tailwindcss": "4.2.2",
113
+ "vite": "7.3.2",
114
+ "vue": "3.5.32"
115
+ }
116
+ }
@@ -0,0 +1,262 @@
1
+ // Base ESLint configuration for Vue / TypeScript FE repos
2
+ // Extend this in your project's eslint.config.js
3
+
4
+ import tseslint from '@typescript-eslint/eslint-plugin'
5
+ import tsparser from '@typescript-eslint/parser'
6
+ import pluginVitest from '@vitest/eslint-plugin'
7
+ import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
8
+ import pluginVue from 'eslint-plugin-vue'
9
+ import { globalIgnores } from 'eslint/config'
10
+ import vueParser from 'vue-eslint-parser'
11
+
12
+ export default function createBaseConfig(options = {}) {
13
+ const {
14
+ tsconfigRootDir = process.cwd(),
15
+ tsconfigFiles = ['./tsconfig.json'],
16
+ additionalIgnores = [],
17
+ extraNonTypeAwareFiles = [],
18
+ enablePrettier = false,
19
+ enableI18n = false,
20
+ i18nLocaleDir = './src/lang/*.json',
21
+ additionalRules = {},
22
+ additionalPlugins = {},
23
+ additionalSettings = {},
24
+ } = options
25
+
26
+ // Note: Optional plugins should be imported by the consuming project
27
+ // This config accepts them via additionalPlugins option
28
+
29
+ const defaultAdditionalIgnores = [
30
+ 'test-results/**',
31
+ 'playwright-report/**',
32
+ 'blob-report/**',
33
+ 'playwright/.cache/**',
34
+ 'reports/**',
35
+ '**/fg-devkit/configs/**',
36
+ ]
37
+
38
+ const configs = [
39
+ // Non-type-aware config for config files (must come first)
40
+ {
41
+ name: 'fg-devkit/config-files',
42
+ files: ['*.config.{ts,mts,js,mjs,cjs}', 'eslint.config.{js,ts}'],
43
+ languageOptions: {
44
+ parserOptions: {
45
+ project: null,
46
+ tsconfigRootDir,
47
+ },
48
+ },
49
+ },
50
+ {
51
+ name: 'fg-devkit/base',
52
+ files: ['**/*.{ts,mts,tsx,vue}'],
53
+ },
54
+ {
55
+ name: 'fg-devkit/vue-non-type-aware',
56
+ files: ['**/*.vue'],
57
+ languageOptions: {
58
+ parser: vueParser,
59
+ parserOptions: {
60
+ parser: tsparser,
61
+ project: null,
62
+ },
63
+ },
64
+ },
65
+ {
66
+ name: 'fg-devkit/typescript',
67
+ files: ['**/*.{ts,mts,tsx}'],
68
+ languageOptions: {
69
+ parser: tsparser,
70
+ parserOptions: {
71
+ project: null,
72
+ },
73
+ },
74
+ plugins: {
75
+ '@typescript-eslint': tseslint,
76
+ },
77
+ },
78
+ // Non-type-aware config for extra files outside tsconfig
79
+ ...(extraNonTypeAwareFiles.length
80
+ ? [
81
+ {
82
+ name: 'fg-devkit/extra-non-type-aware',
83
+ files: extraNonTypeAwareFiles,
84
+ languageOptions: {
85
+ parserOptions: {
86
+ project: null,
87
+ tsconfigRootDir,
88
+ },
89
+ },
90
+ },
91
+ ]
92
+ : []),
93
+ // Type-aware config for TypeScript files (if tsconfig files provided)
94
+ ...(tsconfigFiles && tsconfigFiles.length > 0
95
+ ? [
96
+ {
97
+ name: 'fg-devkit/type-aware',
98
+ files: ['src/**/*.{ts,mts,tsx}'],
99
+ languageOptions: {
100
+ parser: tsparser,
101
+ parserOptions: {
102
+ project: tsconfigFiles,
103
+ tsconfigRootDir,
104
+ },
105
+ },
106
+ },
107
+ ]
108
+ : []),
109
+ globalIgnores([
110
+ '**/dist/**',
111
+ '**/coverage/**',
112
+ '**/.storybook/**',
113
+ '**/node_modules/**',
114
+ '*.min.js',
115
+ 'tsconfig.tsbuildinfo',
116
+ '.env.local',
117
+ '.env.*.local',
118
+ '*.config.cjs',
119
+ '.eslintrc.cjs',
120
+ ...defaultAdditionalIgnores,
121
+ ...additionalIgnores,
122
+ ]),
123
+ ...pluginVue.configs['flat/essential'],
124
+ {
125
+ ...pluginVitest.configs.recommended,
126
+ files: ['**/__tests__/**', '**/*.test.*', '**/*.spec.*'],
127
+ },
128
+ {
129
+ name: 'fg-devkit/rules',
130
+ files: ['**/*.vue', '**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
131
+ plugins: {
132
+ '@typescript-eslint': tseslint,
133
+ ...additionalPlugins,
134
+ },
135
+ settings: {
136
+ ...(enableI18n
137
+ ? {
138
+ 'vue-i18n': {
139
+ localeDir: {
140
+ pattern: i18nLocaleDir,
141
+ localeKey: 'file',
142
+ },
143
+ },
144
+ }
145
+ : {}),
146
+ ...additionalSettings,
147
+ },
148
+ rules: {
149
+ 'vue/multi-word-component-names': 'off',
150
+ 'vue/comment-directive': 'off',
151
+ 'vue/no-deprecated-slot-attribute': 'off',
152
+ 'no-console': ['error', { allow: ['warn', 'error'] }],
153
+ 'arrow-body-style': ['warn', 'as-needed', { requireReturnForObjectLiteral: false }],
154
+ // Vue component recognition - allow fds-* components and router components
155
+ 'vue/no-undef-components': [
156
+ 'error',
157
+ {
158
+ ignorePatterns: ['^fds-', '^router-', '^Router', '^VueQuery'],
159
+ },
160
+ ],
161
+ 'object-shorthand': ['error', 'always'],
162
+ 'prefer-destructuring': [
163
+ 'error',
164
+ {
165
+ array: false,
166
+ object: true,
167
+ },
168
+ ],
169
+ 'prefer-arrow-callback': 'error',
170
+ 'prefer-template': 'error',
171
+ 'no-unused-expressions': 'error',
172
+ 'no-useless-return': 'error',
173
+ 'no-useless-concat': 'error',
174
+ '@typescript-eslint/no-unused-vars': [
175
+ 'error',
176
+ {
177
+ argsIgnorePattern: '^_',
178
+ varsIgnorePattern: '^_',
179
+ },
180
+ ],
181
+ '@typescript-eslint/ban-ts-comment': 'off',
182
+ '@typescript-eslint/explicit-function-return-type': 'off',
183
+ '@typescript-eslint/no-var-requires': 'off',
184
+ 'prefer-const': 'error',
185
+ 'no-var': 'error',
186
+ 'prefer-spread': 'error',
187
+ 'prefer-rest-params': 'error',
188
+ 'no-duplicate-imports': 'error',
189
+ 'no-useless-rename': 'error',
190
+ 'object-curly-spacing': ['error', 'always'],
191
+ 'array-bracket-spacing': ['error', 'never'],
192
+ // Note: comma-dangle removed - Prettier handles trailing commas via trailingComma: 'all' setting
193
+ // skipFormatting at the end of config array disables formatting rules that conflict with Prettier
194
+ quotes: ['error', 'single', { avoidEscape: true }],
195
+ semi: ['error', 'never'],
196
+ 'no-trailing-spaces': 'error',
197
+ 'eol-last': 'error',
198
+ 'no-lonely-if': 'error',
199
+ // Prettier integration (if enabled and plugin provided)
200
+ ...(enablePrettier && additionalPlugins.prettier ? { 'prettier/prettier': 'warn' } : {}),
201
+ // i18n rules (if enabled and plugin provided)
202
+ ...(enableI18n && additionalPlugins['@intlify/vue-i18n']
203
+ ? {
204
+ '@intlify/vue-i18n/no-missing-keys': 'error',
205
+ '@intlify/vue-i18n/no-raw-text': [
206
+ 'error',
207
+ {
208
+ attributes: {
209
+ '/.+/': [
210
+ 'title',
211
+ 'aria-label',
212
+ 'aria-placeholder',
213
+ 'aria-roledescription',
214
+ 'aria-valuetext',
215
+ 'label',
216
+ 'placeholder',
217
+ 'alt',
218
+ 'meta',
219
+ ],
220
+ input: ['placeholder'],
221
+ img: ['alt'],
222
+ },
223
+ ignorePattern: '^[-:/()%,.\'"]+$',
224
+ ignoreNodes: ['md-icon', 'v-icon', 'fds-icon'],
225
+ },
226
+ ],
227
+ '@intlify/vue-i18n/no-unused-keys': [
228
+ 'warn',
229
+ {
230
+ src: './src',
231
+ extensions: ['.js', '.vue', '.ts'],
232
+ enableFix: false,
233
+ },
234
+ ],
235
+ '@intlify/vue-i18n/no-duplicate-keys-in-locale': 'error',
236
+ '@intlify/vue-i18n/no-dynamic-keys': 'warn',
237
+ '@intlify/vue-i18n/key-format-style': [
238
+ 'warn',
239
+ 'camelCase',
240
+ {
241
+ allowArray: false,
242
+ splitByDots: true,
243
+ },
244
+ ],
245
+ }
246
+ : {}),
247
+ // Additional project-specific rules
248
+ ...additionalRules,
249
+ },
250
+ },
251
+ // JS/CJS files config
252
+ {
253
+ files: ['**/*.js', '**/*.cjs'],
254
+ rules: {
255
+ '@typescript-eslint/no-var-requires': 'off',
256
+ },
257
+ },
258
+ skipFormatting,
259
+ ]
260
+
261
+ return configs
262
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Minimal ESLint flat config for apps using fg-devkit.
3
+ * Copied to eslint.config.js by sync-tooling-configs / sync-project-configs.
4
+ * Customize locally (plugins, rules) or pass options via createBaseConfig after syncing.
5
+ */
6
+ import createBaseConfig from 'fg-devkit/eslint.config.base.js'
7
+ import { fileURLToPath } from 'node:url'
8
+
9
+ const tsconfigRootDir = fileURLToPath(new URL('.', import.meta.url))
10
+
11
+ export default [
12
+ {
13
+ ignores: ['.yalc/**', '**/node_modules/**'],
14
+ },
15
+ ...createBaseConfig({
16
+ tsconfigRootDir,
17
+ tsconfigFiles: ['./tsconfig.json'],
18
+ }),
19
+ ]
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Default Prettier options for repos that extend fg-devkit. Import or re-export from your own prettier.config.
3
+ */
4
+ export default {
5
+ semi: false,
6
+ singleQuote: true,
7
+ printWidth: 120,
8
+ trailingComma: 'all',
9
+ tabWidth: 2,
10
+ useTabs: false,
11
+ arrowParens: 'always',
12
+ endOfLine: 'lf',
13
+ singleAttributePerLine: false,
14
+ bracketSameLine: false,
15
+ htmlWhitespaceSensitivity: 'ignore',
16
+ }