From: Jérôme Benoit Date: Mon, 11 May 2026 21:09:48 +0000 (+0200) Subject: fix(lint): enable Vue strictTypeChecked and fix config X-Git-Tag: cli@v4.7.3~11 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=b4020a3ad105ce23502428828878010202e6d8a4;p=e-mobility-charging-stations-simulator.git fix(lint): enable Vue strictTypeChecked and fix config --- diff --git a/cspell.config.yaml b/cspell.config.yaml new file mode 100644 index 00000000..d9f74bef --- /dev/null +++ b/cspell.config.yaml @@ -0,0 +1,100 @@ +version: '0.2' +words: + - DECI + - CENTI + - MILLI + - MILLIWATT + - Benoit + - catppuccin + - chargingstations + - ctrlr + - csms + - idtag + - idtags + - iccid + - imsi + - ocpp + - onconnection + - opencode + - evse + - evses + - kvar + - kvarh + - varh + - rfid + - workerset + - worktree + - dedup + - unpushed + - logform + - mnemonist + - poolifier + - measurand + - measurands + - mikro + - neostandard + - recurrency + - shutdowning + - VCAP + - workerd + - yxxx + - cppwm + - heartbeatinterval + - HEARTBEATINTERVAL + - websocketpinginterval + - WEBSOCKETPINGINTERVAL + - connectionurl + - CONNECTIONURL + - chargingstation + - CHARGINGSTATION + - authctrlr + - AUTHCTRLR + - recloser + - deauthorize + - Deauth + - DEAUTHORIZE + - deauthorized + - DEAUTHORIZED + - Deauthorization + - Selftest + - SECC + - Secc + - Overcurrent + - ocsp + - OCSP + - EMAID + - emaid + - IDTOKEN + - idtoken + - issuerkeyhash + - issuernamehash + - SRPC + - CALLRESULT + - CALLERROR + - CALLRESULTERROR + - reservability + - PPTP + - UIMCP + - Streamable + - modelcontextprotocol + - OCMF + - ocmf + - secp + - brainpool + - Eichrecht + - eichrecht + - signingmethod + - SIGNINGMETHOD + - encodingmethod + - ENCODINGMETHOD + - signedmeterdata + - SIGNEDMETERDATA + - fiscalmetering + - FISCALMETERING + - publickeywithsignedmetervalue + - PUBLICKEYWITHSIGNEDMETERVALUE + - sampleddatasignreadings + - SAMPLEDDATASIGNREADINGS + - focusables + - Focusables + - secret diff --git a/eslint.config.js b/eslint.config.js index 0a32bbc9..601875cf 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,129 +6,23 @@ import perfectionist from 'eslint-plugin-perfectionist' import pluginVue from 'eslint-plugin-vue' import { defineConfig } from 'eslint/config' import neostandard, { plugins } from 'neostandard' +import vueParser from 'vue-eslint-parser' + +const GLOB_TS = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'] +const GLOB_TS_VUE = [...GLOB_TS, '**/*.vue'] +const GLOB_JS = ['**/*.js', '**/*.mjs', '**/*.cjs'] export default defineConfig([ { ignores: ['**/dist/**'], }, + + // Base configs + cspellConfigs.recommended, { rules: { - '@cspell/spellchecker': [ - 'warn', - { - autoFix: true, - cspell: { - words: [ - 'DECI', - 'CENTI', - 'MILLI', - 'MILLIWATT', - 'Benoit', - 'catppuccin', - 'chargingstations', - 'ctrlr', - 'csms', - 'idtag', - 'idtags', - 'iccid', - 'imsi', - 'ocpp', - 'onconnection', - 'opencode', - 'evse', - 'evses', - 'kvar', - 'kvarh', - 'varh', - 'rfid', - 'workerset', - 'worktree', - 'dedup', - 'unpushed', - 'logform', - 'mnemonist', - 'poolifier', - 'measurand', - 'measurands', - 'mikro', - 'neostandard', - 'recurrency', - 'shutdowning', - 'VCAP', - 'workerd', - 'yxxx', - // OCPP 2.0.x domain terms - 'cppwm', - 'heartbeatinterval', - 'HEARTBEATINTERVAL', - 'websocketpinginterval', - 'WEBSOCKETPINGINTERVAL', - 'connectionurl', - 'CONNECTIONURL', - 'chargingstation', - 'CHARGINGSTATION', - 'authctrlr', - 'AUTHCTRLR', - 'recloser', - 'deauthorize', - 'Deauth', - 'DEAUTHORIZE', - 'deauthorized', - 'DEAUTHORIZED', - 'Deauthorization', - 'Selftest', - 'SECC', - 'Secc', - 'Overcurrent', - 'ocsp', - 'OCSP', - 'EMAID', - 'emaid', - 'IDTOKEN', - 'idtoken', - 'issuerkeyhash', - 'issuernamehash', - // OCPP SRPC - 'SRPC', - 'CALLRESULT', - 'CALLERROR', - 'CALLRESULTERROR', - 'reservability', - // VPN protocol acronyms - 'PPTP', - // UI server protocol acronyms - 'UIMCP', - 'Streamable', - 'modelcontextprotocol', - // Signed meter values - 'OCMF', - 'ocmf', - 'secp', - 'brainpool', - 'Eichrecht', - 'eichrecht', - 'signingmethod', - 'SIGNINGMETHOD', - 'encodingmethod', - 'ENCODINGMETHOD', - 'signedmeterdata', - 'SIGNEDMETERDATA', - 'fiscalmetering', - 'FISCALMETERING', - 'publickeywithsignedmetervalue', - 'PUBLICKEYWITHSIGNEDMETERVALUE', - 'sampleddatasignreadings', - 'SAMPLEDDATASIGNREADINGS', - // UI component terms - 'focusables', - 'Focusables', - // Test credential fragments - 'secret', - ], - }, - }, - ], + '@cspell/spellchecker': ['warn', { autoFix: true }], }, }, js.configs.recommended, @@ -146,27 +40,27 @@ export default defineConfig([ ], }, }, + + // neostandard + + ...neostandard({ ts: true }), + + // Vue + ...pluginVue.configs['flat/recommended'], - { - files: ['**/*.vue'], - languageOptions: { - globals: { - localStorage: 'readonly', - }, - parserOptions: { - parser: '@typescript-eslint/parser', - }, - }, - }, + + // TypeScript + ...plugins['typescript-eslint'].config( { extends: [ ...plugins['typescript-eslint'].configs.strictTypeChecked, ...plugins['typescript-eslint'].configs.stylisticTypeChecked, ], - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts', '*/**.vue'], + files: GLOB_TS_VUE, languageOptions: { parserOptions: { + extraFileExtensions: ['.vue'], projectService: true, // eslint-disable-next-line n/no-unsupported-features/node-builtins tsconfigRootDir: import.meta.dirname, @@ -186,10 +80,32 @@ export default defineConfig([ }, }, { - files: ['**/*.js', '**/*.mjs', '**/*.cjs'], + files: GLOB_JS, ...plugins['typescript-eslint'].configs.disableTypeChecked, } ), + + // Vue parser restoration + + { + files: ['**/*.vue'], + languageOptions: { + globals: { + localStorage: 'readonly', + }, + parser: vueParser, + parserOptions: { + extraFileExtensions: ['.vue'], + parser: plugins['typescript-eslint'].parser, + projectService: true, + // eslint-disable-next-line n/no-unsupported-features/node-builtins + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + + // Perfectionist + perfectionist.configs['recommended-natural'], { files: ['**/*.vue'], @@ -197,11 +113,11 @@ export default defineConfig([ 'perfectionist/sort-vue-attributes': 'off', }, }, - ...neostandard({ - ts: true, - }), + + // Rule overrides + { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts', '**/*.vue'], + files: GLOB_TS_VUE, rules: { '@typescript-eslint/no-unused-vars': [ 'error', diff --git a/ui/web/src/App.vue b/ui/web/src/App.vue index 8b7476f4..2a5c0e23 100644 --- a/ui/web/src/App.vue +++ b/ui/web/src/App.vue @@ -21,7 +21,7 @@ diff --git a/ui/web/src/skins/classic/components/actions/StartTransaction.vue b/ui/web/src/skins/classic/components/actions/StartTransaction.vue index 4c51be8f..6b5720a9 100644 --- a/ui/web/src/skins/classic/components/actions/StartTransaction.vue +++ b/ui/web/src/skins/classic/components/actions/StartTransaction.vue @@ -82,7 +82,7 @@ const { formState, submitForm } = useStartTxForm({ const handleStartTransaction = async (): Promise => { await submitForm() - $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }) + $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined) } diff --git a/ui/web/src/skins/classic/components/buttons/ToggleButton.vue b/ui/web/src/skins/classic/components/buttons/ToggleButton.vue index 123b11d7..de57002e 100644 --- a/ui/web/src/skins/classic/components/buttons/ToggleButton.vue +++ b/ui/web/src/skins/classic/components/buttons/ToggleButton.vue @@ -30,17 +30,16 @@ const props = defineProps<{ const emit = defineEmits<{ clicked: [status: boolean] }>() -const id = - props.shared === true - ? `${SHARED_TOGGLE_BUTTON_KEY_PREFIX}${props.id}` - : `${TOGGLE_BUTTON_KEY_PREFIX}${props.id}` +const id = props.shared + ? `${SHARED_TOGGLE_BUTTON_KEY_PREFIX}${props.id}` + : `${TOGGLE_BUTTON_KEY_PREFIX}${props.id}` const state = ref<{ status: boolean }>({ - status: getFromLocalStorage(id, props.status ?? false), + status: getFromLocalStorage(id, props.status || false), }) const click = (): void => { - if (props.shared === true) { + if (props.shared) { try { const keys = Object.keys(getLocalStorage()).filter( key => key !== id && key.startsWith(SHARED_TOGGLE_BUTTON_KEY_PREFIX) @@ -54,7 +53,7 @@ const click = (): void => { } } } - const current = getFromLocalStorage(id, props.status ?? false) + const current = getFromLocalStorage(id, props.status || false) const newStatus = !current setToLocalStorage(id, newStatus) state.value.status = newStatus diff --git a/ui/web/src/skins/classic/components/charging-stations/CSConnector.vue b/ui/web/src/skins/classic/components/charging-stations/CSConnector.vue index 4c4e292f..e2779eac 100644 --- a/ui/web/src/skins/classic/components/charging-stations/CSConnector.vue +++ b/ui/web/src/skins/classic/components/charging-stations/CSConnector.vue @@ -54,7 +54,7 @@ :id="`${hashId}-${evseId ?? 0}-${connectorId}-start-transaction`" :off=" () => { - $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }) + $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined) } " :on=" @@ -66,7 +66,7 @@ ...(evseId != null ? { evseId: String(evseId) } : {}), ...(ocppVersion != null ? { ocppVersion } : {}), }, - }) + }).catch(() => undefined) } " :shared="true" diff --git a/ui/web/src/skins/classic/components/charging-stations/CSData.vue b/ui/web/src/skins/classic/components/charging-stations/CSData.vue index 4c1bd2ff..a2dbfa00 100644 --- a/ui/web/src/skins/classic/components/charging-stations/CSData.vue +++ b/ui/web/src/skins/classic/components/charging-stations/CSData.vue @@ -49,7 +49,7 @@ :id="`${chargingStation.stationInfo.hashId}-set-supervision-url`" :off=" () => { - $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }) + $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined) } " :on=" @@ -60,7 +60,7 @@ hashId: chargingStation.stationInfo.hashId, chargingStationId: chargingStation.stationInfo.chargingStationId, }, - }) + }).catch(() => undefined) } " :shared="true" @@ -159,7 +159,11 @@ const { openConnection, startStation: startChargingStation, stopStation: stopChargingStation, -} = useStationActions({ onRefresh: () => emit('need-refresh') }) +} = useStationActions({ + onRefresh: () => { + emit('need-refresh') + }, +}) const hashId = computed(() => props.chargingStation.stationInfo.hashId) diff --git a/ui/web/src/skins/modern/ModernLayout.vue b/ui/web/src/skins/modern/ModernLayout.vue index a3a501d2..5d2c300b 100644 --- a/ui/web/src/skins/modern/ModernLayout.vue +++ b/ui/web/src/skins/modern/ModernLayout.vue @@ -75,9 +75,8 @@ diff --git a/ui/web/src/skins/modern/components/SimulatorBar.vue b/ui/web/src/skins/modern/components/SimulatorBar.vue index 7f187614..158a88aa 100644 --- a/ui/web/src/skins/modern/components/SimulatorBar.vue +++ b/ui/web/src/skins/modern/components/SimulatorBar.vue @@ -101,7 +101,6 @@ import StatePill from './StatePill.vue' * @returns The selected option's index */ function getSelectIndex (e: Event): number { - // eslint-disable-next-line no-undef return (e.target as HTMLSelectElement).selectedIndex } @@ -131,7 +130,7 @@ const simulatorVariant = computed<'err' | 'idle' | 'ok'>(() => { const simulatorLabel = computed(() => { if (props.simulatorState == null) return 'Disconnected' - const version = props.simulatorState.version != null ? ` (${props.simulatorState.version})` : '' + const version = props.simulatorState.version ? ` (${props.simulatorState.version})` : '' return `${simulatorStarted.value ? 'Running' : 'Stopped'}${version}` }) diff --git a/ui/web/src/skins/modern/components/StationCard.vue b/ui/web/src/skins/modern/components/StationCard.vue index 3fd17d98..88c37bb2 100644 --- a/ui/web/src/skins/modern/components/StationCard.vue +++ b/ui/web/src/skins/modern/components/StationCard.vue @@ -206,9 +206,7 @@ const { closeConnection, deleteStation, openConnection, pending, startStation, s const wsOpen = computed(() => props.chargingStation.wsState === WebSocketReadyState.OPEN) -const startedVariant = computed<'err' | 'ok'>(() => - props.chargingStation.started === true ? 'ok' : 'err' -) +const startedVariant = computed<'err' | 'ok'>(() => (props.chargingStation.started ? 'ok' : 'err')) const wsVariant = computed(() => getWebSocketStateVariant(props.chargingStation.wsState)) @@ -226,7 +224,7 @@ const getATGStatusForConnector = (connectorId: number): Status | undefined => const toggleStation = (): void => { const hashId = props.chargingStation.stationInfo.hashId - if (props.chargingStation.started === true) { + if (props.chargingStation.started) { stopStation(hashId) } else { startStation(hashId) diff --git a/ui/web/src/skins/modern/components/dialogs/SetSupervisionUrlDialog.vue b/ui/web/src/skins/modern/components/dialogs/SetSupervisionUrlDialog.vue index d57764d9..e173305f 100644 --- a/ui/web/src/skins/modern/components/dialogs/SetSupervisionUrlDialog.vue +++ b/ui/web/src/skins/modern/components/dialogs/SetSupervisionUrlDialog.vue @@ -112,8 +112,8 @@ watch( station => { if (station != null) { formState.value.supervisionUrl = stripStationId( - station.supervisionUrl ?? '', - station.stationInfo.chargingStationId ?? '' + station.supervisionUrl, + station.stationInfo.chargingStationId ) formState.value.supervisionUser = station.stationInfo.supervisionUser ?? '' formState.value.supervisionPassword = station.stationInfo.supervisionPassword ?? ''