chore: upgrade to vite 6 (#14363)
Upgrades the frontend toolchain to Vite 6 and tidies up the build config along the way. Behavior is unchanged for end users; this is dev/build infra. ## What changed - `vite` 5.4 → 6.4, `@vitejs/plugin-vue` → 5.2, `vite-plugin-ruby` → 5.2 (with matching `vite_rails`/`vite_ruby` gem bumps). - Dropped the `vite-node` 2.0.1 pnpm override — no longer needed now that vitest 3 runs on Vite 6 directly. - Split the single `vite.config.ts` into: - `vite.config.ts` (app), `vite.lib.config.ts` (SDK), `vite.shared.ts` (aliases / Vue options), `vitest.config.ts` (tests). - `pnpm build:sdk` now selects the SDK config explicitly instead of branching on `BUILD_MODE=library`. SDK output path is unchanged (`public/packs/js/sdk.js`). No changes needed to Docker images, deployment scripts, or CI — Node 24 and pnpm 10 are already past Vite 6's floor, and the rake `assets:precompile` hook still drives the SDK build via `pnpm`. ## How to test - `pnpm dev` and verify the dashboard, widget, and survey routes load and HMR works. - Load a Chatwoot site widget on a test page and confirm `sdk.js` is served and the widget mounts. - `RAILS_ENV=production bundle exec rake assets:precompile` and confirm `public/packs/js/sdk.js` plus the rest of the manifest are produced. - `pnpm test` for the JS suite. --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
This commit is contained in:
co-authored by
Sivin Varghese
Sony Mathew
parent
95cb3a7ad8
commit
170b64d1f1
+5
-3
@@ -996,11 +996,13 @@ GEM
|
|||||||
activemodel (>= 3.2)
|
activemodel (>= 3.2)
|
||||||
mail (~> 2.5)
|
mail (~> 2.5)
|
||||||
version_gem (1.1.4)
|
version_gem (1.1.4)
|
||||||
vite_rails (3.0.17)
|
vite_rails (3.10.0)
|
||||||
railties (>= 5.1, < 8)
|
railties (>= 5.1, < 9)
|
||||||
vite_ruby (~> 3.0, >= 3.2.2)
|
vite_ruby (~> 3.0, >= 3.2.2)
|
||||||
vite_ruby (3.8.0)
|
vite_ruby (3.10.2)
|
||||||
dry-cli (>= 0.7, < 2)
|
dry-cli (>= 0.7, < 2)
|
||||||
|
logger (~> 1.6)
|
||||||
|
mutex_m
|
||||||
rack-proxy (~> 0.6, >= 0.6.1)
|
rack-proxy (~> 0.6, >= 0.6.1)
|
||||||
zeitwerk (~> 2.2)
|
zeitwerk (~> 2.2)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ vi.mock('shared/helpers/mitt', () => ({
|
|||||||
|
|
||||||
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
||||||
const actual = await importOriginal();
|
const actual = await importOriginal();
|
||||||
actual.default = {
|
return {
|
||||||
track: vi.fn(),
|
...actual,
|
||||||
|
default: {
|
||||||
|
track: vi.fn(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return actual;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useTrack', () => {
|
describe('useTrack', () => {
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ vi.mock('vue-i18n');
|
|||||||
vi.mock('dashboard/api/captain/tasks');
|
vi.mock('dashboard/api/captain/tasks');
|
||||||
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
||||||
const actual = await importOriginal();
|
const actual = await importOriginal();
|
||||||
actual.default = {
|
return {
|
||||||
track: vi.fn(),
|
...actual,
|
||||||
|
default: {
|
||||||
|
track: vi.fn(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return actual;
|
|
||||||
});
|
});
|
||||||
vi.mock('dashboard/helper/AnalyticsHelper/events', () => ({
|
vi.mock('dashboard/helper/AnalyticsHelper/events', () => ({
|
||||||
CAPTAIN_EVENTS: {
|
CAPTAIN_EVENTS: {
|
||||||
|
|||||||
+5
-6
@@ -12,7 +12,7 @@
|
|||||||
"start:test": "RAILS_ENV=test foreman start -f ./Procfile.test",
|
"start:test": "RAILS_ENV=test foreman start -f ./Procfile.test",
|
||||||
"dev": "overmind start -f ./Procfile.dev",
|
"dev": "overmind start -f ./Procfile.dev",
|
||||||
"ruby:prettier": "bundle exec rubocop -a",
|
"ruby:prettier": "bundle exec rubocop -a",
|
||||||
"build:sdk": "BUILD_MODE=library vite build",
|
"build:sdk": "vite build --config vite.lib.config.ts",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"size": "size-limit",
|
"size": "size-limit",
|
||||||
"story:dev": "histoire dev",
|
"story:dev": "histoire dev",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tanstack/vue-table": "^8.20.5",
|
"@tanstack/vue-table": "^8.20.5",
|
||||||
"@twilio/voice-sdk": "^2.12.4",
|
"@twilio/voice-sdk": "^2.12.4",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"@vue/compiler-sfc": "^3.5.8",
|
"@vue/compiler-sfc": "^3.5.8",
|
||||||
"@vuelidate/core": "^2.0.3",
|
"@vuelidate/core": "^2.0.3",
|
||||||
"@vuelidate/validators": "^2.0.4",
|
"@vuelidate/validators": "^2.0.4",
|
||||||
@@ -146,8 +146,8 @@
|
|||||||
"prosemirror-model": "^1.22.3",
|
"prosemirror-model": "^1.22.3",
|
||||||
"size-limit": "^8.2.4",
|
"size-limit": "^8.2.4",
|
||||||
"tailwindcss": "^3.4.19",
|
"tailwindcss": "^3.4.19",
|
||||||
"vite": "^5.4.21",
|
"vite": "6.4.2",
|
||||||
"vite-plugin-ruby": "^5.0.0",
|
"vite-plugin-ruby": "^5.2.1",
|
||||||
"vitest": "3.0.5"
|
"vitest": "3.0.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -161,8 +161,7 @@
|
|||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"vite-node": "2.0.1",
|
"vite": "6.4.2",
|
||||||
"vite": "5.4.21",
|
|
||||||
"vitest": "3.0.5",
|
"vitest": "3.0.5",
|
||||||
"minimatch@<4": "3.1.5",
|
"minimatch@<4": "3.1.5",
|
||||||
"minimatch@>=9.0.0 <9.0.7": "9.0.9",
|
"minimatch@>=9.0.0 <9.0.7": "9.0.9",
|
||||||
|
|||||||
Generated
+434
-188
File diff suppressed because it is too large
Load Diff
+3
-111
@@ -1,50 +1,11 @@
|
|||||||
/// <reference types="vitest" />
|
|
||||||
|
|
||||||
/**
|
|
||||||
What's going on with library mode?
|
|
||||||
|
|
||||||
Glad you asked, here's a quick rundown:
|
|
||||||
|
|
||||||
1. vite-plugin-ruby will automatically bring all the entrypoints like dashbord and widget as input to vite.
|
|
||||||
2. vite needs to be in library mode to build the SDK as a single file. (UMD) format and set `inlineDynamicImports` to true.
|
|
||||||
3. But when setting `inlineDynamicImports` to true, vite will not be able to handle mutliple entrypoints.
|
|
||||||
|
|
||||||
This puts us in a deadlock, now there are two ways around this, either add another separate build pipeline to
|
|
||||||
the app using vanilla rollup or rspack or something. The second option is to remove sdk building from the main pipeline
|
|
||||||
and build it separately using Vite itself, toggled by an ENV variable.
|
|
||||||
|
|
||||||
`BUILD_MODE=library bin/vite build` should build only the SDK and save it to `public/packs/js/sdk.js`
|
|
||||||
`bin/vite build` will build the rest of the app as usual. But exclude the SDK.
|
|
||||||
|
|
||||||
We need to edit the `asset:precompile` rake task to include the SDK in the precompile list.
|
|
||||||
*/
|
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import ruby from 'vite-plugin-ruby';
|
import ruby from 'vite-plugin-ruby';
|
||||||
import path from 'path';
|
|
||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import { aliases, vueOptions } from './vite.shared';
|
||||||
import yaml from '@rollup/plugin-yaml';
|
import yaml from '@rollup/plugin-yaml';
|
||||||
|
|
||||||
const isLibraryMode = process.env.BUILD_MODE === 'library';
|
|
||||||
const isTestMode = process.env.TEST === 'true';
|
|
||||||
|
|
||||||
const vueOptions = {
|
|
||||||
template: {
|
|
||||||
compilerOptions: {
|
|
||||||
isCustomElement: tag => ['ninja-keys'].includes(tag),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let plugins = [ruby(), vue(vueOptions), yaml()];
|
|
||||||
|
|
||||||
if (isLibraryMode) {
|
|
||||||
plugins = [];
|
|
||||||
} else if (isTestMode) {
|
|
||||||
plugins = [vue(vueOptions), yaml()];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: plugins,
|
plugins: [ruby(), vue(vueOptions), yaml()],
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
scss: {
|
scss: {
|
||||||
@@ -52,74 +13,5 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
resolve: { alias: aliases },
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
// [NOTE] when not in library mode, no new keys will be addedd or overwritten
|
|
||||||
// setting dir: isLibraryMode ? 'public/packs' : undefined will not work
|
|
||||||
...(isLibraryMode
|
|
||||||
? {
|
|
||||||
dir: 'public/packs',
|
|
||||||
entryFileNames: chunkInfo => {
|
|
||||||
if (chunkInfo.name === 'sdk') {
|
|
||||||
return 'js/sdk.js';
|
|
||||||
}
|
|
||||||
return '[name].js';
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
inlineDynamicImports: isLibraryMode, // Disable code-splitting for SDK
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lib: isLibraryMode
|
|
||||||
? {
|
|
||||||
entry: path.resolve(__dirname, './app/javascript/entrypoints/sdk.js'),
|
|
||||||
formats: ['iife'], // IIFE format for single file
|
|
||||||
name: 'sdk',
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
vue: 'vue/dist/vue.esm-bundler.js',
|
|
||||||
components: path.resolve('./app/javascript/dashboard/components'),
|
|
||||||
next: path.resolve('./app/javascript/dashboard/components-next'),
|
|
||||||
v3: path.resolve('./app/javascript/v3'),
|
|
||||||
dashboard: path.resolve('./app/javascript/dashboard'),
|
|
||||||
helpers: path.resolve('./app/javascript/shared/helpers'),
|
|
||||||
shared: path.resolve('./app/javascript/shared'),
|
|
||||||
survey: path.resolve('./app/javascript/survey'),
|
|
||||||
widget: path.resolve('./app/javascript/widget'),
|
|
||||||
assets: path.resolve('./app/javascript/dashboard/assets'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
environment: 'jsdom',
|
|
||||||
include: ['app/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
|
||||||
coverage: {
|
|
||||||
reporter: ['lcov', 'text'],
|
|
||||||
include: ['app/**/*.js', 'app/**/*.vue'],
|
|
||||||
exclude: [
|
|
||||||
'app/**/*.@(spec|stories|routes).js',
|
|
||||||
'**/specs/**/*',
|
|
||||||
'**/i18n/**/*',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
globals: true,
|
|
||||||
outputFile: 'coverage/sonar-report.xml',
|
|
||||||
pool: 'threads',
|
|
||||||
poolOptions: {
|
|
||||||
threads: {
|
|
||||||
singleThread: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
deps: {
|
|
||||||
inline: ['tinykeys', '@material/mwc-icon'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setupFiles: ['fake-indexeddb/auto', 'vitest.setup.js'],
|
|
||||||
mockReset: true,
|
|
||||||
clearMocks: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* SDK library build.
|
||||||
|
*
|
||||||
|
* vite-plugin-ruby pulls every entrypoint as input, but the SDK needs to ship
|
||||||
|
* as a single IIFE file (`inlineDynamicImports: true`), which is incompatible
|
||||||
|
* with multiple entrypoints. So the SDK gets its own pipeline:
|
||||||
|
*
|
||||||
|
* vite build --config vite.lib.config.ts → public/packs/js/sdk.js
|
||||||
|
*
|
||||||
|
* The `assets:precompile` rake task runs this alongside the main app build.
|
||||||
|
*/
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import path from 'path';
|
||||||
|
import { aliases } from './vite.shared';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
dir: 'public/packs',
|
||||||
|
entryFileNames: chunkInfo =>
|
||||||
|
chunkInfo.name === 'sdk' ? 'js/sdk.js' : '[name].js',
|
||||||
|
inlineDynamicImports: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, './app/javascript/entrypoints/sdk.js'),
|
||||||
|
formats: ['iife'],
|
||||||
|
name: 'sdk',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: { alias: aliases },
|
||||||
|
});
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export const aliases = {
|
||||||
|
vue: 'vue/dist/vue.esm-bundler.js',
|
||||||
|
components: path.resolve('./app/javascript/dashboard/components'),
|
||||||
|
next: path.resolve('./app/javascript/dashboard/components-next'),
|
||||||
|
v3: path.resolve('./app/javascript/v3'),
|
||||||
|
dashboard: path.resolve('./app/javascript/dashboard'),
|
||||||
|
helpers: path.resolve('./app/javascript/shared/helpers'),
|
||||||
|
shared: path.resolve('./app/javascript/shared'),
|
||||||
|
survey: path.resolve('./app/javascript/survey'),
|
||||||
|
widget: path.resolve('./app/javascript/widget'),
|
||||||
|
assets: path.resolve('./app/javascript/dashboard/assets'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const vueOptions = {
|
||||||
|
template: {
|
||||||
|
compilerOptions: {
|
||||||
|
isCustomElement: (tag: string) => ['ninja-keys'].includes(tag),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/// <reference types="vitest" />
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import { aliases, vueOptions } from './vite.shared';
|
||||||
|
import yaml from '@rollup/plugin-yaml';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue(vueOptions), yaml()],
|
||||||
|
resolve: { alias: aliases },
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['app/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
||||||
|
coverage: {
|
||||||
|
reporter: ['lcov', 'text'],
|
||||||
|
include: ['app/**/*.js', 'app/**/*.vue'],
|
||||||
|
exclude: [
|
||||||
|
'app/**/*.@(spec|stories|routes).js',
|
||||||
|
'**/specs/**/*',
|
||||||
|
'**/i18n/**/*',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
globals: true,
|
||||||
|
outputFile: 'coverage/sonar-report.xml',
|
||||||
|
pool: 'threads',
|
||||||
|
poolOptions: {
|
||||||
|
threads: {
|
||||||
|
singleThread: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
deps: {
|
||||||
|
inline: ['tinykeys', '@material/mwc-icon'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setupFiles: ['fake-indexeddb/auto', 'vitest.setup.js'],
|
||||||
|
mockReset: true,
|
||||||
|
clearMocks: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user