diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 0dac6c55..580ea7b6 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -4,12 +4,14 @@ on: [push, pull_request] jobs: build: - name: Vite Plugin Ruby + name: ${{ matrix.package }} strategy: + fail-fast: false matrix: os: [ubuntu-latest] node: [22, 24] + package: [vite-plugin-ruby, vite-plugin-rails] runs-on: ${{ matrix.os }} @@ -31,4 +33,4 @@ jobs: run: pnpm -r build - name: Test - run: pnpm -C vite-plugin-ruby test + run: pnpm -C ${{ matrix.package }} test diff --git a/vite-plugin-rails/example/package.json b/vite-plugin-rails/example/package.json index 70f0b5c0..b6ea2853 100644 --- a/vite-plugin-rails/example/package.json +++ b/vite-plugin-rails/example/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "dev": "cross-env DEBUG=vite-plugin-ruby:* vite --open", - "build": "cross-env DEBUG=vite-plugin-ruby:* vite build --mode production --emptyOutDir", + "build": "cross-env NODE_ENV=production DEBUG=vite-plugin-ruby:* vite build --mode production --emptyOutDir", "preview": "cross-env RAILS_ENV=production DEBUG=vite-plugin-ruby:* vite preview --open" }, "devDependencies": { diff --git a/vite-plugin-rails/tests/__snapshots__/build.spec.ts.snap b/vite-plugin-rails/tests/__snapshots__/build.spec.ts.snap index 8911e9d4..01f1fc6d 100644 --- a/vite-plugin-rails/tests/__snapshots__/build.spec.ts.snap +++ b/vite-plugin-rails/tests/__snapshots__/build.spec.ts.snap @@ -3,20 +3,20 @@ exports[`config > generated files 1`] = ` { "../assets/external.js": { - "file": "assets/external-BwssHmjP.js", - "integrity": "sha384-U9qU8CtmZAV7LLzMBzP9NVBCp+XAYYx03gCzvQZYfQuRJZvsWa1L9wEDoHtYQqVd", + "file": "assets/external.js", + "integrity": "", "isEntry": true, "name": "app/assets/external.js", "src": "../assets/external.js", }, "../assets/logo.png": { - "file": "assets/logo-CPmPqqKk.png", - "integrity": "sha384-IGw3tfo4Xt0lp6sO9t5mYtILwfBxDFR8sRCtroxGjhE3r9UxNYgtimkCNzsJU9AP", + "file": "assets/logo.png", + "integrity": "", "src": "app/assets/logo.png", }, "../assets/theme.css": { - "file": "assets/theme-C09nxUps.css", - "integrity": "sha384-gOxt2ZzkwfdFXE6YALyPXRfW9ox13B5dw1j6hciZMSpv4FW117dQCGJYWuX7gHhV", + "file": "assets/theme.css", + "integrity": "", "isEntry": true, "name": "app/assets/theme.css", "names": [ @@ -24,19 +24,19 @@ exports[`config > generated files 1`] = ` ], "src": "../assets/theme.css", }, - "_logo-CPmPqqKk.png": { - "file": "assets/logo-CPmPqqKk.png", - "integrity": "sha384-IGw3tfo4Xt0lp6sO9t5mYtILwfBxDFR8sRCtroxGjhE3r9UxNYgtimkCNzsJU9AP", - "src": "_logo-CPmPqqKk.png", + "_logo.png": { + "file": "assets/logo.png", + "integrity": "", + "src": "_logo.png", }, - "_logo-Nuo4H8F_.svg": { - "file": "assets/logo-Nuo4H8F_.svg", - "integrity": "sha384-qQuFPGf9f7DHitjACIjHvY2GwlcFjg+HatTRP3bwG7iAX8MzOW4Sr2dfca6uZlDA", - "src": "_logo-Nuo4H8F_.svg", + "_logo.svg": { + "file": "assets/logo.svg", + "integrity": "", + "src": "_logo.svg", }, "entrypoints/app.css": { - "file": "assets/app-OK6MABcH.css", - "integrity": "sha384-T/ai70rXe9JcUyfq8yDHJEpj5Dfyf5USrp8lFAA6zerwRx6nd3BV/69pA6FhMKv/", + "file": "assets/app.css", + "integrity": "", "isEntry": true, "name": "entrypoints/app.css", "names": [ @@ -46,31 +46,31 @@ exports[`config > generated files 1`] = ` }, "entrypoints/frameworks/vue.js": { "css": [ - "assets/vue-BepfPMzO.css", + "assets/vue.css", ], - "file": "assets/vue-CoJ_KGkH.js", - "integrity": "sha384-z6zaKxiS0m3H8sWN92KZHtoafYXYCKYqAhtd2nRnds+tgO1WnE+Wm8NfcCuf5LTC", + "file": "assets/vue.js", + "integrity": "", "isEntry": true, "name": "entrypoints/frameworks/vue.js", "src": "entrypoints/frameworks/vue.js", }, "entrypoints/main.ts": { "css": [ - "assets/theme-C09nxUps.css", - "assets/app-OK6MABcH.css", + "assets/theme.css", + "assets/app.css", ], - "file": "assets/main-Ddvw3iap.js", + "file": "assets/main.js", "imports": [ "entrypoints/frameworks/vue.js", ], - "integrity": "sha384-Ep2dj4XV6THhLdkgG0rqwUg4g9CFZLQVH+a+KAvALAEQhhY+grtX0Hj2ZdQK3LRZ", + "integrity": "", "isEntry": true, "name": "entrypoints/main.ts", "src": "entrypoints/main.ts", }, "entrypoints/sassy.scss": { - "file": "assets/sassy-D5kz_As0.css", - "integrity": "sha384-4tkn835gOI7C29uZzlN/PGxd+XyQTG1/9AHpbWYbYLbgWZ1UmDDBFW1iq43MC70L", + "file": "assets/sassy.css", + "integrity": "", "isEntry": true, "name": "entrypoints/sassy.scss", "names": [ @@ -79,29 +79,29 @@ exports[`config > generated files 1`] = ` "src": "entrypoints/sassy.scss", }, "images/logo.png": { - "file": "assets/logo-Bh_XL1Xl.png", - "integrity": "sha384-zHCg6OQ1T1xX21KMsW9Bb2ECIIMeec41+3KtT0gygdsrmm/Z5qD5TVtTPhyJNGot", + "file": "assets/logo.png", + "integrity": "", "src": "images/logo.png", }, "images/logo.svg": { - "file": "assets/logo-Nuo4H8F_.svg", - "integrity": "sha384-qQuFPGf9f7DHitjACIjHvY2GwlcFjg+HatTRP3bwG7iAX8MzOW4Sr2dfca6uZlDA", + "file": "assets/logo.svg", + "integrity": "", "src": "images/logo.svg", }, "index.html": { "assets": [ - "assets/logo-Bh_XL1Xl.png", + "assets/logo.png", ], "css": [ - "assets/app-OK6MABcH.css", - "assets/sassy-D5kz_As0.css", + "assets/app.css", + "assets/sassy.css", ], - "file": "assets/index-qTzjl5TV.js", + "file": "assets/index.js", "imports": [ "entrypoints/main.ts", "entrypoints/main.ts", ], - "integrity": "sha384-o33u4LIOheMYUEftMVfRyLb7iOMoCQuLr2MpmuYmRcQEzC5rRF9y6e/rD6f/vExA", + "integrity": "", "isEntry": true, "name": "index.html", "src": "index.html", diff --git a/vite-plugin-rails/tests/build.spec.ts b/vite-plugin-rails/tests/build.spec.ts index 4373364d..0fc8f51f 100644 --- a/vite-plugin-rails/tests/build.spec.ts +++ b/vite-plugin-rails/tests/build.spec.ts @@ -7,6 +7,25 @@ import glob from 'fast-glob' const projectRoot = resolve(__dirname, '../') const exampleDir = `${projectRoot}/example` +// Strip 8-char content hashes Vite/Rolldown injects into asset filenames so +// the snapshot stays stable across routine dependency bumps (Vite, Rolldown, +// Vue, sass, etc.) that change chunk bytes without changing manifest shape. +const stripHash = (s: string): string => s.replace(/-[A-Za-z0-9_-]{8}(?=\.)/g, '') + +const normalize = (value: unknown): unknown => { + if (typeof value === 'string') return stripHash(value) + if (Array.isArray(value)) return value.map(normalize) + if (value && typeof value === 'object') { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [ + stripHash(k), + k === 'integrity' ? '' : normalize(v), + ]), + ) + } + return value +} + describe('config', () => { beforeAll(async () => { await execa('npm', ['run', 'build'], { stdio: process.env.DEBUG ? 'inherit' : undefined, cwd: exampleDir }) @@ -15,39 +34,38 @@ describe('config', () => { test('generated files', async () => { const outDir = `${exampleDir}/public/vite` const files = await glob('**/*', { cwd: outDir, onlyFiles: true }) - expect(files.sort()).toEqual(expect.arrayContaining([ - 'assets/app-OK6MABcH.css', - 'assets/app-OK6MABcH.css.br', - 'assets/app-OK6MABcH.css.gz', - 'assets/external-BwssHmjP.js', - 'assets/external-BwssHmjP.js.br', - 'assets/external-BwssHmjP.js.gz', - 'assets/external-BwssHmjP.js.map', - 'assets/index-qTzjl5TV.js', - 'assets/index-qTzjl5TV.js.br', - 'assets/index-qTzjl5TV.js.gz', - 'assets/logo-Bh_XL1Xl.png', - 'assets/logo-CPmPqqKk.png', - 'assets/logo-Nuo4H8F_.svg', - 'assets/logo-Nuo4H8F_.svg.br', - 'assets/logo-Nuo4H8F_.svg.gz', - 'assets/main-Ddvw3iap.js', - 'assets/main-Ddvw3iap.js.br', - 'assets/main-Ddvw3iap.js.gz', - 'assets/main-Ddvw3iap.js.map', - 'assets/sassy-D5kz_As0.css', - 'assets/sassy-D5kz_As0.css.br', - 'assets/sassy-D5kz_As0.css.gz', - 'assets/theme-C09nxUps.css', - 'assets/theme-C09nxUps.css.br', - 'assets/theme-C09nxUps.css.gz', - 'assets/vue-BepfPMzO.css', - 'assets/vue-BepfPMzO.css.br', - 'assets/vue-BepfPMzO.css.gz', - 'assets/vue-CoJ_KGkH.js', - 'assets/vue-CoJ_KGkH.js.br', - 'assets/vue-CoJ_KGkH.js.gz', - 'assets/vue-CoJ_KGkH.js.map', + expect(files.map(stripHash).sort()).toEqual(expect.arrayContaining([ + 'assets/app.css', + 'assets/app.css.br', + 'assets/app.css.gz', + 'assets/external.js', + 'assets/external.js.br', + 'assets/external.js.gz', + 'assets/external.js.map', + 'assets/index.js', + 'assets/index.js.br', + 'assets/index.js.gz', + 'assets/logo.png', + 'assets/logo.svg', + 'assets/logo.svg.br', + 'assets/logo.svg.gz', + 'assets/main.js', + 'assets/main.js.br', + 'assets/main.js.gz', + 'assets/main.js.map', + 'assets/sassy.css', + 'assets/sassy.css.br', + 'assets/sassy.css.gz', + 'assets/theme.css', + 'assets/theme.css.br', + 'assets/theme.css.gz', + 'assets/vue.css', + 'assets/vue.css.br', + 'assets/vue.css.gz', + 'assets/vue.js', + 'assets/vue.js.br', + 'assets/vue.js.gz', + 'assets/vue.js.map', 'index.html', 'index.html.br', 'index.html.gz', @@ -57,6 +75,6 @@ describe('config', () => { const manifest = parseManifest('manifest.json') const manifestAssets = parseManifest('manifest-assets.json') - expect({ ...manifest, ...manifestAssets }).toMatchSnapshot() + expect(normalize({ ...manifest, ...manifestAssets })).toMatchSnapshot() }) })