@archpublicwebsite/eslint-config 1.0.21 → 1.0.23

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.
@@ -13,19 +13,16 @@ function log(msg) {
13
13
 
14
14
  function getProjectRoot() {
15
15
  const root = process.env.INIT_CWD || process.cwd()
16
- if (!existsSync(join(root, 'package.json')))
17
- return null
16
+ if (!existsSync(join(root, 'package.json'))) return null
18
17
  return root
19
18
  }
20
19
 
21
20
  function ensureDir(dirPath) {
22
- if (!existsSync(dirPath))
23
- mkdirSync(dirPath, { recursive: true })
21
+ if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true })
24
22
  }
25
23
 
26
24
  function writeIfMissing(filePath, content) {
27
- if (existsSync(filePath))
28
- return false
25
+ if (existsSync(filePath)) return false
29
26
  writeFileSync(filePath, content, 'utf8')
30
27
  return true
31
28
  }
@@ -56,8 +53,7 @@ indent_size = 2
56
53
  [Makefile]
57
54
  indent_style = tab
58
55
  `
59
- if (writeIfMissing(editorConfigPath, content))
60
- log('Created .editorconfig')
56
+ if (writeIfMissing(editorConfigPath, content)) log('Created .editorconfig')
61
57
  }
62
58
 
63
59
  // ─── .eslint-user-ignore ────────────────────────────────────────────────────
@@ -80,8 +76,7 @@ function ensureEslintUserIgnore(projectRoot) {
80
76
  # docs/**
81
77
  # *.pdf
82
78
  `
83
- if (writeIfMissing(ignorePath, content))
84
- log('Created .eslint-user-ignore')
79
+ if (writeIfMissing(ignorePath, content)) log('Created .eslint-user-ignore')
85
80
  }
86
81
 
87
82
  // ─── eslint.config.mjs ─────────────────────────────────────────────────────
@@ -97,8 +92,7 @@ export default createArchipelagoConfig({
97
92
  },
98
93
  })
99
94
  `
100
- if (writeIfMissing(eslintConfigPath, content))
101
- log('Created eslint.config.mjs')
95
+ if (writeIfMissing(eslintConfigPath, content)) log('Created eslint.config.mjs')
102
96
  }
103
97
 
104
98
  // ─── .prettierrc ────────────────────────────────────────────────────────────
@@ -110,8 +104,7 @@ function ensurePrettierConfig(projectRoot) {
110
104
  export default config
111
105
  `
112
106
 
113
- if (writeIfMissing(prettierModuleConfigPath, prettierModuleConfigContent))
114
- log('Created prettier.config.mjs')
107
+ if (writeIfMissing(prettierModuleConfigPath, prettierModuleConfigContent)) log('Created prettier.config.mjs')
115
108
 
116
109
  const prettierPath = join(projectRoot, '.prettierrc')
117
110
  const defaults = {
@@ -135,24 +128,81 @@ export default config
135
128
  const plugins = Array.isArray(current.plugins) ? current.plugins : []
136
129
  const deduped = [...new Set([...plugins, 'prettier-plugin-tailwindcss'])]
137
130
  current.plugins = deduped
138
- if (typeof current.singleQuote !== 'boolean')
139
- current.singleQuote = true
140
- if (typeof current.semi !== 'boolean')
141
- current.semi = false
131
+ if (typeof current.singleQuote !== 'boolean') current.singleQuote = true
132
+ if (typeof current.semi !== 'boolean') current.semi = false
142
133
  writeFileSync(prettierPath, `${JSON.stringify(current, null, 2)}\n`, 'utf8')
143
134
  log('Updated .prettierrc')
144
- }
145
- catch {
135
+ } catch {
146
136
  // Keep existing file untouched if it is not JSON.
147
137
  }
148
138
  }
149
139
 
140
+ // ─── tsconfig.base.json ──────────────────────────────────────────────────────
141
+ // Shared TypeScript compiler settings used by every package and app.
142
+ // Includes @vue/typescript-plugin so the IDE delegates .vue file handling to
143
+ // Volar instead of the plain TS server (eliminates "Cannot find name 'div'" etc.)
144
+
145
+ function ensureTsConfigBase(projectRoot) {
146
+ const tsconfigBasePath = join(projectRoot, 'tsconfig.base.json')
147
+ const content = `{
148
+ "compilerOptions": {
149
+ "target": "ES2020",
150
+ "module": "ESNext",
151
+ "moduleResolution": "Bundler",
152
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
153
+ "strict": true,
154
+ "jsx": "preserve",
155
+ "resolveJsonModule": true,
156
+ "skipLibCheck": true,
157
+ "types": ["vite/client"],
158
+ "plugins": [
159
+ {
160
+ "name": "@vue/typescript-plugin",
161
+ "languages": ["vue"]
162
+ }
163
+ ]
164
+ }
165
+ }
166
+ `
167
+ if (writeIfMissing(tsconfigBasePath, content)) log('Created tsconfig.base.json')
168
+ }
169
+
170
+ // ─── tsconfig.json ───────────────────────────────────────────────────────────
171
+ // Root TypeScript project file that extends tsconfig.base.json.
172
+ // Covers src/ and packages/ source; excludes apps/ (each app owns its tsconfig).
173
+ // Add path aliases here as workspace packages grow.
174
+
175
+ function ensureTsConfig(projectRoot) {
176
+ const tsconfigPath = join(projectRoot, 'tsconfig.json')
177
+ const content = `{
178
+ "extends": "./tsconfig.base.json",
179
+ "compilerOptions": {
180
+ "paths": {}
181
+ },
182
+ "include": [
183
+ "src/**/*.ts",
184
+ "src/**/*.vue",
185
+ "packages/**/*.ts",
186
+ "packages/**/*.vue",
187
+ "vite.config.ts"
188
+ ],
189
+ "exclude": [
190
+ "apps/**",
191
+ "node_modules",
192
+ "**/dist/**",
193
+ "**/.nuxt/**",
194
+ "**/.output/**"
195
+ ]
196
+ }
197
+ `
198
+ if (writeIfMissing(tsconfigPath, content)) log('Created tsconfig.json')
199
+ }
200
+
150
201
  function ensureCommitlintConfig(projectRoot) {
151
202
  const commitlintConfigPath = join(projectRoot, 'commitlint.config.cjs')
152
203
  const content = `module.exports = require('@archpublicwebsite/eslint-config/commitlint')
153
204
  `
154
- if (writeIfMissing(commitlintConfigPath, content))
155
- log('Created commitlint.config.cjs')
205
+ if (writeIfMissing(commitlintConfigPath, content)) log('Created commitlint.config.cjs')
156
206
  }
157
207
 
158
208
  function ensureLintStagedConfig(projectRoot) {
@@ -161,8 +211,7 @@ function ensureLintStagedConfig(projectRoot) {
161
211
 
162
212
  export default config
163
213
  `
164
- if (writeIfMissing(lintStagedConfigPath, content))
165
- log('Created lint-staged.config.mjs')
214
+ if (writeIfMissing(lintStagedConfigPath, content)) log('Created lint-staged.config.mjs')
166
215
  }
167
216
 
168
217
  function ensurePrettierIgnore(projectRoot) {
@@ -178,8 +227,7 @@ pnpm-lock.yaml
178
227
  package-lock.json
179
228
  yarn.lock
180
229
  `
181
- if (writeIfMissing(prettierIgnorePath, content))
182
- log('Created .prettierignore')
230
+ if (writeIfMissing(prettierIgnorePath, content)) log('Created .prettierignore')
183
231
  }
184
232
 
185
233
  // ─── .hooks ─────────────────────────────────────────────────────────────────
@@ -233,18 +281,15 @@ node node_modules/@archpublicwebsite/eslint-config/tools/git-hooks/pre-push.mjs
233
281
  created = true
234
282
  }
235
283
  })
236
- if (created)
237
- log('Created .hooks/ (pre-commit, prepare-commit-msg, commit-msg, post-commit, pre-push)')
284
+ if (created) log('Created .hooks/ (pre-commit, prepare-commit-msg, commit-msg, post-commit, pre-push)')
238
285
  }
239
286
 
240
287
  function ensureHooksPath(projectRoot) {
241
- if (!existsSync(join(projectRoot, '.git')))
242
- return
288
+ if (!existsSync(join(projectRoot, '.git'))) return
243
289
  try {
244
290
  execSync('git config core.hooksPath .hooks', { cwd: projectRoot, stdio: 'ignore' })
245
291
  log('Set git core.hooksPath → .hooks')
246
- }
247
- catch {
292
+ } catch {
248
293
  // Ignore setup failures in non-git contexts.
249
294
  }
250
295
  }
@@ -284,8 +329,7 @@ function ensurePackageScripts(projectRoot) {
284
329
  let pkg
285
330
  try {
286
331
  pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
287
- }
288
- catch {
332
+ } catch {
289
333
  return
290
334
  }
291
335
 
@@ -299,7 +343,8 @@ function ensurePackageScripts(projectRoot) {
299
343
  precommit: 'node node_modules/@archpublicwebsite/eslint-config/tools/git-hooks/pre-commit.mjs',
300
344
  prepush: 'node node_modules/@archpublicwebsite/eslint-config/tools/git-hooks/pre-push.mjs',
301
345
  'security:global-scan': 'bash ./node_modules/@archpublicwebsite/eslint-config/tools/security/scan-global.sh',
302
- 'security:safe-check': 'bash ./node_modules/@archpublicwebsite/eslint-config/tools/security/safe-reinstall.sh --check-only',
346
+ 'security:safe-check':
347
+ 'bash ./node_modules/@archpublicwebsite/eslint-config/tools/security/safe-reinstall.sh --check-only',
303
348
  'security:pre-push': 'node node_modules/@archpublicwebsite/eslint-config/tools/git-hooks/pre-push.mjs',
304
349
  }
305
350
 
@@ -311,13 +356,26 @@ function ensurePackageScripts(projectRoot) {
311
356
  }
312
357
  }
313
358
 
359
+ const lintStagedConfigPath = join(projectRoot, 'lint-staged.config.mjs')
360
+ const hasLintStagedConfig = existsSync(lintStagedConfigPath)
361
+
362
+ // Auto-heal legacy/broken lint-staged script when it points to a missing file.
363
+ if (
364
+ typeof scripts['lint-staged'] === 'string' &&
365
+ scripts['lint-staged'].includes('--config lint-staged.config.mjs') &&
366
+ !hasLintStagedConfig
367
+ ) {
368
+ scripts['lint-staged'] = 'lint-staged'
369
+ updated = true
370
+ }
371
+
314
372
  if (!updated) {
315
373
  return
316
374
  }
317
375
 
318
376
  pkg.scripts = scripts
319
377
  writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
320
- log('Updated package.json scripts (precommit, security:global-scan, security:safe-check)')
378
+ log('Updated package.json scripts (lint-staged, precommit, security:global-scan, security:safe-check)')
321
379
  }
322
380
 
323
381
  // ─── .vscode/extensions.json ────────────────────────────────────────────────
@@ -328,26 +386,24 @@ function ensureVscodeExtensions(projectRoot) {
328
386
 
329
387
  ensureDir(vscodeDir)
330
388
 
331
- const recommended = [
332
- 'dbaeumer.vscode-eslint',
333
- 'esbenp.prettier-vscode',
334
- 'vue.volar',
335
- ]
389
+ const recommended = ['dbaeumer.vscode-eslint', 'esbenp.prettier-vscode', 'vue.volar']
390
+ const unwanted = ['octref.vetur', 'vue.vscode-typescript-vue-plugin']
336
391
 
337
- let current = { recommendations: [] }
392
+ let current = { recommendations: [], unwantedRecommendations: [] }
338
393
  if (existsSync(extPath)) {
339
394
  try {
340
395
  current = JSON.parse(readFileSync(extPath, 'utf8'))
341
- if (!Array.isArray(current.recommendations))
342
- current.recommendations = []
343
- }
344
- catch {
345
- current = { recommendations: [] }
396
+ if (!Array.isArray(current.recommendations)) current.recommendations = []
397
+ if (!Array.isArray(current.unwantedRecommendations)) current.unwantedRecommendations = []
398
+ } catch {
399
+ current = { recommendations: [], unwantedRecommendations: [] }
346
400
  }
347
401
  }
348
402
 
349
403
  const merged = [...new Set([...current.recommendations, ...recommended])]
404
+ const mergedUnwanted = [...new Set([...current.unwantedRecommendations, ...unwanted])]
350
405
  current.recommendations = merged
406
+ current.unwantedRecommendations = mergedUnwanted
351
407
  writeFileSync(extPath, `${JSON.stringify(current, null, 2)}\n`, 'utf8')
352
408
  log('Created/updated .vscode/extensions.json')
353
409
  }
@@ -356,13 +412,14 @@ function ensureVscodeExtensions(projectRoot) {
356
412
 
357
413
  function main() {
358
414
  const projectRoot = getProjectRoot()
359
- if (!projectRoot)
360
- return
415
+ if (!projectRoot) return
361
416
 
362
417
  log('Setting up project...')
363
418
 
364
419
  ensureEditorConfig(projectRoot)
365
420
  ensureEslintUserIgnore(projectRoot)
421
+ ensureTsConfigBase(projectRoot)
422
+ ensureTsConfig(projectRoot)
366
423
  ensureEslintConfig(projectRoot)
367
424
  ensurePrettierConfig(projectRoot)
368
425
  ensurePrettierIgnore(projectRoot)
@@ -9,20 +9,30 @@ const TAG = '[@archpublicwebsite/eslint-config]'
9
9
  * the correct file types.
10
10
  */
11
11
  const VSCODE_ESLINT_SETTINGS = {
12
+ // Use workspace TypeScript for consistent Volar/TS plugin resolution
13
+ 'typescript.tsdk': 'node_modules/typescript/lib',
14
+ 'typescript.enablePromptUseWorkspaceTsdk': true,
15
+
16
+ // Ensure Vue SFC files are always handled as Vue documents
17
+ 'files.associations': {
18
+ '*.vue': 'vue',
19
+ },
20
+
21
+ // Volar + TS integration for Vue 3 SFC template diagnostics
22
+ 'vue.server.hybridMode': true,
23
+ 'volar.takeOverMode.enabled': true,
24
+
25
+ // Prevent duplicate/broken diagnostics from built-in TS/JS validators
26
+ // (vue-tsc + Volar remain the source of truth for Vue type diagnostics).
27
+ 'typescript.validate.enable': false,
28
+ 'javascript.validate.enable': false,
29
+
12
30
  // Use flat config mode (required for eslint.config.mjs)
13
31
  'eslint.useFlatConfig': true,
32
+ 'eslint.workingDirectories': [{ mode: 'auto' }],
14
33
 
15
34
  // Enable ESLint for these languages
16
- 'eslint.validate': [
17
- 'javascript',
18
- 'javascriptreact',
19
- 'typescript',
20
- 'typescriptreact',
21
- 'vue',
22
- 'json',
23
- 'jsonc',
24
- 'markdown',
25
- ],
35
+ 'eslint.validate': ['javascript', 'javascriptreact', 'typescript', 'typescriptreact', 'vue', 'json', 'jsonc'],
26
36
 
27
37
  // Auto-fix on save via ESLint
28
38
  'editor.codeActionsOnSave': {
@@ -33,7 +43,22 @@ const VSCODE_ESLINT_SETTINGS = {
33
43
  // Let ESLint handle formatting instead of the built-in formatter
34
44
  'editor.formatOnSave': false,
35
45
 
36
- // Disable the default VS Code JSON formatter for files ESLint handles
46
+ // Formatters per language ESLint enforces no-semi, single-quote on save
47
+ '[javascript]': {
48
+ 'editor.defaultFormatter': 'dbaeumer.vscode-eslint',
49
+ },
50
+ '[javascriptreact]': {
51
+ 'editor.defaultFormatter': 'dbaeumer.vscode-eslint',
52
+ },
53
+ '[typescript]': {
54
+ 'editor.defaultFormatter': 'dbaeumer.vscode-eslint',
55
+ },
56
+ '[typescriptreact]': {
57
+ 'editor.defaultFormatter': 'dbaeumer.vscode-eslint',
58
+ },
59
+ '[vue]': {
60
+ 'editor.defaultFormatter': 'Vue.volar',
61
+ },
37
62
  '[json]': {
38
63
  'editor.defaultFormatter': 'dbaeumer.vscode-eslint',
39
64
  },
@@ -71,16 +96,15 @@ function deepMerge(target, source) {
71
96
  const srcVal = source[key]
72
97
  const tgtVal = result[key]
73
98
  if (
74
- srcVal !== null
75
- && typeof srcVal === 'object'
76
- && !Array.isArray(srcVal)
77
- && tgtVal !== null
78
- && typeof tgtVal === 'object'
79
- && !Array.isArray(tgtVal)
99
+ srcVal !== null &&
100
+ typeof srcVal === 'object' &&
101
+ !Array.isArray(srcVal) &&
102
+ tgtVal !== null &&
103
+ typeof tgtVal === 'object' &&
104
+ !Array.isArray(tgtVal)
80
105
  ) {
81
106
  result[key] = deepMerge(tgtVal, srcVal)
82
- }
83
- else {
107
+ } else {
84
108
  result[key] = srcVal
85
109
  }
86
110
  }
@@ -107,8 +131,7 @@ export function ensureVscodeSettings(projectRoot) {
107
131
  try {
108
132
  const raw = readFileSync(settingsPath, 'utf8')
109
133
  current = JSON.parse(raw)
110
- }
111
- catch {
134
+ } catch {
112
135
  // File exists but is not valid JSON – back it up and start fresh
113
136
  const backupPath = `${settingsPath}.backup`
114
137
  writeFileSync(backupPath, readFileSync(settingsPath, 'utf8'), 'utf8')