From d07a0741ecb8481ad7a7405bcf34664703950981 Mon Sep 17 00:00:00 2001 From: Maschga <88616799+Maschga@users.noreply.github.com> Date: Fri, 31 Jul 2026 11:43:57 +0200 Subject: [PATCH] chore: use vite+ (#32297) Co-authored-by: Michael Geers --- .github/actions/build-toolchain/action.yml | 4 +- .github/workflows/cli-docs.yml | 7 +- .github/workflows/default.yml | 16 +- .prettierignore | 1 - .vscode/extensions.json | 1 + AGENTS.md | 36 +- CONTRIBUTING.md | 13 +- Dockerfile | 2 +- Makefile | 10 +- assets/js/colors.test.ts | 2 +- .../Battery/BatteryUsageSettings.vue | 2 +- assets/js/components/Battery/history.test.ts | 2 +- .../components/ChargingPlans/Preview.test.ts | 2 +- .../js/components/ChargingPlans/Preview.vue | 2 +- .../Config/DeviceModal/index.test.ts | 2 +- assets/js/components/Config/EebusModal.vue | 2 +- .../Config/Messaging/MessengerModal.vue | 2 +- assets/js/components/Config/PropertyEntry.vue | 2 +- .../js/components/Energyflow/Energyflow.vue | 2 +- assets/js/components/Forecast/Chart.vue | 2 +- assets/js/components/Forecast/PriceChart.vue | 2 +- assets/js/components/Forecast/ValueChart.vue | 2 +- assets/js/components/History/GroupChart.vue | 31 +- assets/js/components/Issue/format.test.ts | 2 +- assets/js/components/Issue/template.test.ts | 2 +- .../js/components/Loadpoints/Loadpoints.vue | 21 +- assets/js/components/Savings/Savings.vue | 10 +- .../components/Sessions/CostHistoryChart.vue | 2 +- .../Sessions/EnergyHistoryChart.vue | 2 +- assets/js/components/Top/Header.vue | 3 +- assets/js/components/Vehicles/Status.test.ts | 2 +- assets/js/components/Vehicles/Vehicle.vue | 3 +- assets/js/configModal.test.ts | 2 +- assets/js/mixins/breakpoint.ts | 2 +- assets/js/mixins/formatter.test.ts | 2 +- assets/js/router.test.ts | 2 +- assets/js/types/evcc.ts | 8 +- assets/js/types/vue.d.ts | 2 +- assets/js/utils/circuits.test.ts | 2 +- assets/js/utils/cleanYaml.test.ts | 2 +- assets/js/utils/extractDomain.test.ts | 2 +- assets/js/utils/forecast.test.ts | 2 +- assets/js/utils/placeholder.test.ts | 2 +- assets/js/utils/tariffSlots.test.ts | 2 +- env.d.ts | 6 + eslint.config.mts | 39 - oxfmt.config.ts | 6 + package-lock.json | 4712 +++++++---------- package.json | 46 +- prettier.config.js | 11 - tests/simulator.ts | 2 +- tests/simulator/vite.config.ts | 2 +- tsconfig.json | 4 +- vite.config.ts | 52 +- vitest.config.ts | 4 +- 55 files changed, 2176 insertions(+), 2932 deletions(-) delete mode 100644 .prettierignore delete mode 100644 eslint.config.mts create mode 100644 oxfmt.config.ts delete mode 100644 prettier.config.js diff --git a/.github/actions/build-toolchain/action.yml b/.github/actions/build-toolchain/action.yml index 032cdad61..7d0f17453 100644 --- a/.github/actions/build-toolchain/action.yml +++ b/.github/actions/build-toolchain/action.yml @@ -18,10 +18,10 @@ runs: go-version-file: go.mod cache: false # managed explicitly below to keep the scoped namespaces - - uses: actions/setup-node@v6 + - uses: voidzero-dev/setup-vp@v1 with: node-version: "26" - cache: "npm" + cache: true # Writes are namespaced by cache-scope so a PR build cannot poison the # shared "main" cache; PRs still read it through the fallback restore-key. diff --git a/.github/workflows/cli-docs.yml b/.github/workflows/cli-docs.yml index 981f424d1..a55b077f7 100644 --- a/.github/workflows/cli-docs.yml +++ b/.github/workflows/cli-docs.yml @@ -26,11 +26,16 @@ jobs: with: go-version-file: go.mod + - uses: voidzero-dev/setup-vp@v1 + with: + node-version: "26" + cache: true + - name: Generate CLI docs run: mkdir dist && touch dist/empty && go run main.go gendoc ./cli-docs - name: Format - run: npx prettier@3.9.4 --no-config --write "./cli-docs/**/*.md" + run: vp fmt "./cli-docs/**/*.md" - name: Deploy CLI docs to docs repo uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4 diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 151a2281c..0584a8fcf 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -76,10 +76,10 @@ jobs: ${{ runner.os }}-go-build- ${{ runner.os }}-go- - - uses: actions/setup-node@v6 + - uses: voidzero-dev/setup-vp@v1 with: node-version: "26" - cache: "npm" + cache: true - run: mkdir dist && touch dist/empty @@ -168,10 +168,10 @@ jobs: with: persist-credentials: false - - uses: actions/setup-node@v6 + - uses: voidzero-dev/setup-vp@v1 with: node-version: "26" - cache: "npm" + cache: true - name: Install run: make install-ui @@ -230,10 +230,10 @@ jobs: ${{ runner.os }}-go-integration- ${{ runner.os }}-go- - - uses: actions/setup-node@v6 + - uses: voidzero-dev/setup-vp@v1 with: node-version: "26" - cache: "npm" + cache: true - name: Build UI run: make install-ui ui @@ -254,11 +254,11 @@ jobs: - name: Install Playwright (browsers + deps) if: steps.playwright-cache.outputs.cache-hit != 'true' - run: npx playwright install --with-deps chromium + run: vpx playwright install --with-deps chromium timeout-minutes: 5 - name: Run tests - run: npx playwright test + run: vpx playwright test timeout-minutes: 20 env: TZ: Europe/Berlin diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 7020916a6..000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -tests/custom-css.css \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7e9078b78..ec190d867 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,5 +5,6 @@ "golang.go", "esbenp.prettier-vscode", "yoavbls.pretty-ts-errors", + "VoidZero.vite-plus-extension-pack" ] } diff --git a/AGENTS.md b/AGENTS.md index 9ab6288d6..04919edff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,13 +14,13 @@ This file provides guidance to AI coding agents when working with code in this r - `make build` - build Go binary only - `make ui` - build UI assets only - `make install` - install Go tools and dependencies -- `make install-ui` - install Node.js dependencies (`npm ci`) +- `make install-ui` - install Node.js dependencies (`vp install`) - `make test` - run Go tests - `make test-ui` - run frontend tests - `make lint` - run Go linting (golangci-lint) - `make lint-ui` - run frontend linting -- `npm run dev` - start Vue dev server (http://127.0.0.1:7071) -- `npm run playwright` - run integration tests +- `vp run dev` - start Vue dev server (http://127.0.0.1:7071) +- `vp run playwright` - run integration tests - `evcc --template-type [type] --template [file]` - test device templates - `make docs` - generate template documentation @@ -28,15 +28,15 @@ This file provides guidance to AI coding agents when working with code in this r Deep documentation on specific subsystems is available in `docs/agents/`. Load what you need based on the task: -| File | When to load | -|------|-------------| -| [Core Domain](docs/agents/core-domain.md) | Control loop, loadpoint logic, PV surplus, charge modes, tariffs, interfaces | -| [Hardware Integrations](docs/agents/hardware-integrations.md) | Charger/meter/vehicle implementations, adding new devices | -| [Easee Architecture](docs/agents/easee-architecture.md) | Easee charger (REST+SignalR, async correlation, concurrency) | -| [OCPP Forwarder](docs/agents/ocpp-forwarder.md) | OCPP proxy/forwarder (sidecar relay to upstream OCPP server, read-only mode) | -| [Plugin System](docs/agents/plugin-system.md) | Plugin layer (HTTP, MQTT, Modbus, SunSpec, JS) | -| [Web UI & API](docs/agents/web-ui-api.md) | REST API, WebSocket, Vue frontend, authentication | -| [API Security](docs/agents/api-security.md) | Auth modes, JWT/API key/session, two-tier checks, credential storage | +| File | When to load | +| ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [Core Domain](docs/agents/core-domain.md) | Control loop, loadpoint logic, PV surplus, charge modes, tariffs, interfaces | +| [Hardware Integrations](docs/agents/hardware-integrations.md) | Charger/meter/vehicle implementations, adding new devices | +| [Easee Architecture](docs/agents/easee-architecture.md) | Easee charger (REST+SignalR, async correlation, concurrency) | +| [OCPP Forwarder](docs/agents/ocpp-forwarder.md) | OCPP proxy/forwarder (sidecar relay to upstream OCPP server, read-only mode) | +| [Plugin System](docs/agents/plugin-system.md) | Plugin layer (HTTP, MQTT, Modbus, SunSpec, JS) | +| [Web UI & API](docs/agents/web-ui-api.md) | REST API, WebSocket, Vue frontend, authentication | +| [API Security](docs/agents/api-security.md) | Auth modes, JWT/API key/session, two-tier checks, credential storage | ### Loading guide by task type @@ -98,7 +98,7 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w ## Comment Style -- Prefer self-documenting code over comments; comment the *why*, not the *what* +- Prefer self-documenting code over comments; comment the _why_, not the _what_ - Default to no comment. Only add one for a non-obvious constraint, invariant, workaround, or surprising behavior. Keep it to one line, two if necessary - Skip refs to the current task, PR, issue, or caller ("added for X flow", "see #1234"). Git history covers that - Exception: Go exported identifiers follow godoc convention. Short `// FuncName does X` summary starting with the identifier name @@ -122,7 +122,7 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w - `_enumer.go` - generated enum code - `*_decorators.go` - generated decorator pattern implementations - Validate interface implementations: `var _ Interface = (*Type)(nil)` -- Capabilities: register via `implement.Has`/`May` only when a capability is *conditional* (runtime/config detection, e.g. `if cp.PhaseSwitching { implement.Has(...) }`). For capabilities present on every code path, declare a plain exported method plus `var _ api.Interface = (*Type)(nil)` instead. `api.Cap` resolves static methods via direct type assertion, so unconditional `implement.Has` is redundant. A type with no conditional capabilities needs neither the `implement.Caps` embed nor `implement.New()` +- Capabilities: register via `implement.Has`/`May` only when a capability is _conditional_ (runtime/config detection, e.g. `if cp.PhaseSwitching { implement.Has(...) }`). For capabilities present on every code path, declare a plain exported method plus `var _ api.Interface = (*Type)(nil)` instead. `api.Cap` resolves static methods via direct type assertion, so unconditional `implement.Has` is redundant. A type with no conditional capabilities needs neither the `implement.Caps` embed nor `implement.New()` ### Error Handling @@ -236,9 +236,9 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w ### Essential Commands - Must build before testing executing playwright `make ui build` since it uses the binary. For manual testing assets are build and reloaded automatically (vite dev). -- Run tests: `npm run playwright` or `npx playwright test` -- Debug: `npx playwright test --debug` -- Specific test: `npx playwright test tests/config-loadpoint.spec.ts` +- Run tests: `vp run playwright` or `vpx playwright test` +- Debug: `vpx playwright test --debug` +- Specific test: `vpx playwright test tests/config-loadpoint.spec.ts` ### Selector Strategy @@ -270,7 +270,7 @@ Deep documentation on specific subsystems is available in `docs/agents/`. Load w - Device types: chargers, meters, vehicles, tariffs - Plugin protocols: Modbus, HTTP, MQTT, JavaScript, Go - Define device capabilities and configuration in templates at `templates/definition/[type]/` -- Don't restate param properties that `util/templates/defaults.yaml` already defines for that param name. Properties (description, help, type, unit, default, example, required, advanced, mask, private, usages, …) are inherited from defaults; only specify a property in a template to give it a *different* value. Restating the same value is redundant duplication: reference the param by `name` alone. +- Don't restate param properties that `util/templates/defaults.yaml` already defines for that param name. Properties (description, help, type, unit, default, example, required, advanced, mask, private, usages, …) are inherited from defaults; only specify a property in a template to give it a _different_ value. Restating the same value is redundant duplication: reference the param by `name` alone. - Test templates: `evcc --template-type [type] --template [file]` - Update docs after template changes: `make docs` - When implementing or debugging against a third-party device library (eebus-go/ship/spine-go, ocpp-go, modbus/SunSpec), consult the library's current upstream documentation before coding rather than relying on recalled API details diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a04cfe985..4c37bf915 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ ### Development environment -Developing evcc requires [Go][1] and [Node][2]. We recommend VSCode with the [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go), [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) and [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) extensions. +Developing evcc requires [Go][1] [Node][2] and [Vite+][3]. We recommend VSCode with the [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go), [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) and [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) extensions. Alternatively, if you use VS Code and [devcontainers](https://code.visualstudio.com/docs/devcontainers/containers), you can use the "Dev containers: Clone repository in container volume" action. This will create a devcontainer with the required toolchain and install the prerequisites as explained below. Wait until the startup log says "Done. Press any key to close the terminal." and check for any errors. @@ -98,8 +98,8 @@ You can adjust the referred configuration as needed to e.g. use your live config For frontend development start the Vue toolchain in dev-mode. Open http://127.0.0.1:7071/ to get to the live reloading development server. It pulls its data from port 7070 (see above). ```sh -npm install -npm run dev +vp install +vp run dev ``` ### Storybook @@ -107,7 +107,7 @@ npm run dev We're using storybook to develop and visualize UI components in different states. Running the command below will open your browser at http://127.0.0.1:6006/. ```sh -npm run storybook +vp run storybook ``` ### Integration testing @@ -116,7 +116,7 @@ We use Playwright for end-to-end integration tests. They start a local evcc inst ```sh make ui build -npm run playwright +vp run playwright ``` ### Simulating device state @@ -124,7 +124,7 @@ npm run playwright Since we don't want to run tests against real devices or cloud services, we've build a simple simulator that lets you emulated meters, vehicles and loadpoints. The simulators web interface runs on http://localhost:7072. ``` -npm run simulator +vp run simulator ``` Run an evcc instance that uses simulator data. This configuration runs with a very high refresh interval to speed up testing. @@ -185,6 +185,7 @@ Note: To ensure the build succeeds after creating new translations, make sure to [1]: https://go.dev [2]: https://nodejs.org/ +[3]: https://viteplus.dev/guide/#install-vp ## Documentation, Website and iOS/Android App diff --git a/Dockerfile b/Dockerfile index 23c079cdf..0b88e958a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # STEP 1 build ui FROM --platform=$BUILDPLATFORM node:26-alpine AS node -RUN apk update && apk add --no-cache make +RUN apk update && apk add --no-cache make && curl -fsSL https://vite.plus | bash WORKDIR /build diff --git a/Makefile b/Makefile index 46af8a6fd..736483078 100644 --- a/Makefile +++ b/Makefile @@ -40,10 +40,10 @@ install:: go install tool install-ui:: - npm ci + vp install ui:: - npm run build + vp run build assets:: go generate ./... @@ -59,7 +59,7 @@ modernize: go tool modernize -test -fix -stringsbuilder=false -omitzero=false ./... lint-ui:: - npm run lint + vp run lint license:: go run github.com/google/go-licenses/v2@latest check \ @@ -74,10 +74,10 @@ license:: ./... license-ui:: - npm run license + vp run license test-ui:: - npm test + vp run test test:: @echo "Running testsuite" diff --git a/assets/js/colors.test.ts b/assets/js/colors.test.ts index b90da7864..17973155b 100644 --- a/assets/js/colors.test.ts +++ b/assets/js/colors.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect } from "vite-plus/test"; import colors, { dimColor, lighterColor, diff --git a/assets/js/components/Battery/BatteryUsageSettings.vue b/assets/js/components/Battery/BatteryUsageSettings.vue index 40139f147..c40f8067c 100644 --- a/assets/js/components/Battery/BatteryUsageSettings.vue +++ b/assets/js/components/Battery/BatteryUsageSettings.vue @@ -313,7 +313,7 @@ export default defineComponent({ }, batteryDetails() { if (!this.batteryDevices.length) { - return; + return ""; } const multipleBatteries = this.batteryDevices.length > 1; return this.batteryDevices diff --git a/assets/js/components/Battery/history.test.ts b/assets/js/components/Battery/history.test.ts index 07ec65001..82c708c5d 100644 --- a/assets/js/components/Battery/history.test.ts +++ b/assets/js/components/Battery/history.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect } from "vite-plus/test"; import { historyToSeries, forecastToSeries, buildChartBatteries } from "./history"; import type { BatteryMeter, EvOpt } from "@/types/evcc"; import type { BatteryHistorySeries } from "./types"; diff --git a/assets/js/components/ChargingPlans/Preview.test.ts b/assets/js/components/ChargingPlans/Preview.test.ts index edbe1c5dc..ab724aa69 100644 --- a/assets/js/components/ChargingPlans/Preview.test.ts +++ b/assets/js/components/ChargingPlans/Preview.test.ts @@ -1,5 +1,5 @@ import { mount, config } from "@vue/test-utils"; -import { beforeAll, describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vite-plus/test"; import Preview from "./Preview.vue"; import type { Slot } from "@/types/evcc"; diff --git a/assets/js/components/ChargingPlans/Preview.vue b/assets/js/components/ChargingPlans/Preview.vue index 1c3e5820a..c6994da2a 100644 --- a/assets/js/components/ChargingPlans/Preview.vue +++ b/assets/js/components/ChargingPlans/Preview.vue @@ -126,7 +126,7 @@ export default defineComponent({ return null; }, targetOffset(): number | undefined { - if (!this.targetTime) return; + if (!this.targetTime) return undefined; const start = new Date(this.startTime); start.setMinutes(start.getMinutes() - (start.getMinutes() % 15)); start.setSeconds(0); diff --git a/assets/js/components/Config/DeviceModal/index.test.ts b/assets/js/components/Config/DeviceModal/index.test.ts index 27585a31a..3d15a86f1 100644 --- a/assets/js/components/Config/DeviceModal/index.test.ts +++ b/assets/js/components/Config/DeviceModal/index.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it } from "vite-plus/test"; import { createServiceEndpoints, type TemplateParam } from "./index"; const buildParam = (name: string, service?: string): TemplateParam => ({ diff --git a/assets/js/components/Config/EebusModal.vue b/assets/js/components/Config/EebusModal.vue index ef8b601d0..b36745922 100644 --- a/assets/js/components/Config/EebusModal.vue +++ b/assets/js/components/Config/EebusModal.vue @@ -186,7 +186,7 @@ import type { PropType } from "vue"; import QRCode from "qrcode"; import "@h2d2/shopicons/es/regular/trash"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars +// oxlint-disable-next-line @typescript-eslint/no-unused-vars import type { EebusConfig, EebusPairing, EebusStatus, YamlSource } from "@/types/evcc"; import api from "@/api"; import JsonModal from "./JsonModal.vue"; diff --git a/assets/js/components/Config/Messaging/MessengerModal.vue b/assets/js/components/Config/Messaging/MessengerModal.vue index 3627d5fef..78b7d349a 100644 --- a/assets/js/components/Config/Messaging/MessengerModal.vue +++ b/assets/js/components/Config/Messaging/MessengerModal.vue @@ -58,7 +58,7 @@ export default defineComponent({ }, { label: "primary", - options: [...products.filter((p: Product) => p.group !== "generic")], + options: products.filter((p: Product) => p.group !== "generic"), }, ]; }, diff --git a/assets/js/components/Config/PropertyEntry.vue b/assets/js/components/Config/PropertyEntry.vue index dfbc1cfcf..7ef34e012 100644 --- a/assets/js/components/Config/PropertyEntry.vue +++ b/assets/js/components/Config/PropertyEntry.vue @@ -25,7 +25,7 @@