Skip to content
Draft

Vite #16379

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
16c8563
Add Vite build config scaffolding alongside webpack
arelra Apr 6, 2026
0d23ad0
Migrate islands to import.meta.glob, remove webpack-specific modules
arelra Apr 6, 2026
5ca79ab
Adapt asset manifest and script regex for Vite format
arelra Apr 6, 2026
81863d5
Finalize Vite server SSR build config
arelra Apr 6, 2026
5a78a58
Add Vite dev server with SSR middleware and CJS compatibility
arelra Apr 7, 2026
0791690
Migrate Storybook to Vite, remove webpack config and dependencies
arelra Apr 9, 2026
63c1127
Fix TypeScript errors in Vite migration code
arelra Apr 9, 2026
4ba9cbf
Update lockfile for Storybook Vite migration
arelra Apr 9, 2026
49cc38e
Enable skipLibCheck to suppress upstream type errors in node_modules
arelra Apr 9, 2026
b75a502
Add AMD require type for curl.js loader in InteractiveBlockComponent
arelra Apr 9, 2026
80fa33a
Fix Jest config for Vite migration
arelra Apr 9, 2026
81ea836
Fix Vite dev server asset loading and remove frameworks.js
arelra Apr 11, 2026
521a3ba
Remove Preact aliases, use React for all builds
arelra Apr 11, 2026
45c4d79
Fix SVG imports to return React components instead of URLs
arelra Apr 11, 2026
58b1916
Fix ssrCjsPlugin leaking Node APIs into client bundles
arelra Apr 11, 2026
90423af
Fix HMR WebSocket by attaching to Express HTTP server
arelra Apr 11, 2026
1ee52ea
Restore ES6 named imports for CJS deps via ssrCjsPlugin
arelra Apr 14, 2026
430017b
Replace @storybook/react-webpack with @storybook/react-vite
arelra Apr 24, 2026
bdc172b
Fix vite.config.client TS error - TODO
arelra Apr 24, 2026
5c47dc9
Replace webpack makefile tasks with vite
arelra Apr 24, 2026
9d7eb36
Add list of dependencies to optimise for the client build
arelra Apr 24, 2026
8d18711
Convert bundles.js to mjs and remove special vite cjs handling
arelra Apr 24, 2026
b893e22
bundles.ts
arelra Apr 24, 2026
aceeeb2
Vite 7 and ESM all scripts.
arelra May 1, 2026
944724c
Vite 7 & Rolldown
arelra May 5, 2026
56318a4
Vite v8.0.11
arelra May 8, 2026
0672319
Update dev-server optimizeDeps
arelra May 14, 2026
c29da8f
use rolldownOptions
arelra May 14, 2026
383969b
Fix chunking. Single frameworks chunks, all others maxSize: 500kb exc…
arelra May 17, 2026
1910c56
Fix dev server SSR hot reloading
arelra May 17, 2026
ce8963f
Add rollup dependency visualiser
arelra Jul 9, 2026
baa5e5f
Update dev server optimised deps after rebase
arelra Jul 9, 2026
3038420
Fix lint after rebase
arelra Jul 9, 2026
3d77cf6
Fix browser-targets.ts
arelra Jul 9, 2026
5b26de9
Update build-check.js to check Vite manifest format and expand checks…
arelra Jul 14, 2026
bd7986a
Update Production.dockerfile to run Vite build
arelra Jul 14, 2026
cfbc86a
Add visualizer plugin to all builds and output all visualisation type…
arelra Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions .github/workflows/bundle-analyser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,30 @@ jobs:
run: make build
working-directory: dotcom-rendering

- name: Archive code coverage results for web bundle
- name: Archive bundle analyser report for server bundle
uses: actions/upload-artifact@v7
with:
name: bundle-analyser-report-web-bundles
path: dotcom-rendering/dist/stats/client.web-bundles.html
name: bundle-analyser-report-server
path: dotcom-rendering/dist/stats/server.treemap.html
if-no-files-found: error

- name: Archive code coverage results for web variant bundle
- name: Archive bundle analyser report for web bundle
uses: actions/upload-artifact@v7
with:
name: bundle-analyser-report-web
path: dotcom-rendering/dist/stats/client.web.variant-bundles.html
path: dotcom-rendering/dist/stats/client.web.treemap.html
if-no-files-found: error

- name: Archive bundle analyser report for web variant bundle
uses: actions/upload-artifact@v7
with:
name: bundle-analyser-report-web-variant
path: dotcom-rendering/dist/stats/client.web.variant.treemap.html
if-no-files-found: warn # Variant bundle only exists when an active experiment is going on

- name: Archive code coverage results for apps bundle
- name: Archive bundle analyser report for apps bundle
uses: actions/upload-artifact@v7
with:
name: bundle-analyser-report-apps
path: dotcom-rendering/dist/stats/client.apps-bundles.html
path: dotcom-rendering/dist/stats/client.apps.treemap.html
if-no-files-found: error
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pnpm-lock.yaml
dotcom-rendering/src/model/*-schema.json
dotcom-rendering/src/frontend/schemas
dotcom-rendering/stories/generated
dotcom-rendering/cdk.out

# specific files
/**/*/curl-with-js-and-domReady.js
Expand Down
5 changes: 3 additions & 2 deletions Production.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ ENV NODE_ENV=production

# Build the application
FROM dependencies AS builder
RUN webpack --config webpack/webpack.config.js --progress
RUN node scripts/islands/island-descriptions.mjs
RUN node vite/build.mts
# TODO VITE FIXME
# RUN node scripts/islands/island-descriptions.mjs

# Finally, create the production image with only the necessary files
FROM dhi.io/node:24-alpine3.23 AS application
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Decorator } from '@storybook/react-webpack5';
import { ConfigProvider } from '../../src/components/ConfigContext';
import type { Config } from '../../src/types/configContext';
import type { Decorator } from '@storybook/react-vite';
import { Config } from '../../src/types/configContext';

const defaultConfig = {
renderingTarget: 'Web',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css } from '@emotion/react';
import { from } from '@guardian/source/foundations';
import type { Decorator } from '@storybook/react-webpack5';
import type { Decorator } from '@storybook/react-vite';
import { grid } from '../../src/grid';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
textSans17,
textSansBold20,
} from '@guardian/source/foundations';
import type { Decorator } from '@storybook/react-webpack5';
import { Decorator } from '@storybook/react-vite';
import { storybookPaletteDeclarations as paletteDeclarations } from '../mocks/paletteDeclarations';
import type { ReactNode } from 'react';
import {
ArticleDesign,
Expand All @@ -15,7 +16,6 @@ import {
Pillar,
} from '../../src/lib/articleFormat';
import type { ArticleFormat } from '../../src/lib/articleFormat';
import { storybookPaletteDeclarations as paletteDeclarations } from '../mocks/paletteDeclarations';

interface Orientation {
orientation?: 'horizontal' | 'vertical';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type Decorator,
type StoryContext,
type StrictArgs,
} from '@storybook/react-webpack5';
} from '@storybook/react-vite';
import type { CSSProperties } from 'react';
import type { ArticleFormat } from '../../src/lib/articleFormat';
import { storybookPaletteDeclarations as paletteDeclarations } from '../mocks/paletteDeclarations';
Expand Down
157 changes: 47 additions & 110 deletions dotcom-rendering/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { createRequire } from 'node:module';
import { defineMain } from '@storybook/react-vite/node';
import path from 'node:path';
import process from 'node:process';
import type { StorybookConfig } from '@storybook/react-webpack5';
import { defineMain } from '@storybook/react-webpack5/node';
import webpack from 'webpack';
import { saveStories } from '../scripts/gen-stories/get-stories.mjs';
import { svgr } from '../webpack/svg.cjs';
// Import CommonJS webpack config in ESM context
const require = createRequire(import.meta.url);
const {
transpileExclude,
getLoaders,
} = require('../webpack/webpack.config.client.js');
import type { StorybookConfig } from '@storybook/react-vite';
import svgr from 'vite-plugin-svgr';

// ESM equivalent of __dirname
const __dirname = import.meta.dirname;

// Generate dynamic Card and Layout stories
saveStories();
Expand All @@ -36,42 +31,49 @@ export default defineMain({
{ from: '../src/static', to: '/static/frontend/' },
],

addons: [
'@storybook/addon-webpack5-compiler-swc',
'@storybook/addon-docs',
'@storybook/addon-a11y',
],
addons: ['@storybook/addon-docs', '@storybook/addon-a11y'],

webpackFinal: async (config) => {
// Get project specific webpack options
const newConfig = webpackConfig(config);

newConfig.resolve ??= {};

// Global options for webpack
newConfig.resolve.extensions?.push('.ts', '.tsx');

newConfig.resolve.fallback ??= {};
// clean-css will try to import these packages
newConfig.resolve.fallback['http'] = false;
newConfig.resolve.fallback['https'] = false;
newConfig.resolve.fallback['os'] = false;

// Required as otherwise 'process' will not be defined when included on its own (without .env)
// e.g process?.env?.SOME_VAR
newConfig.plugins?.push(
new webpack.DefinePlugin({
'process.env': JSON.stringify({
SDC_URL: process.env.SDC_URL,
HOSTNAME: process.env.HOSTNAME,
}),
}),
// We rely on Buffer for our bridget thrift client
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
viteFinal: async (config) => {
config.plugins ??= [];
config.plugins.push(
svgr({
svgrOptions: { svgo: false },
}),
);
return newConfig;

config.define ??= {};
config.define['process.env'] = JSON.stringify({
SDC_URL: process.env.SDC_URL,
HOSTNAME: process.env.HOSTNAME,
});

config.resolve ??= {};
config.resolve.alias = {
...config.resolve.alias,

Buffer: 'buffer',
react: 'react',
'react-dom': 'react-dom',

// Mock JSDOM for storybook - it relies on native node.js packages
jsdom$: path.resolve(__dirname, './mocks/jsdom.ts'),

// log4js tries to call "fs" in storybook -- we can ignore it
[`${path.resolve(__dirname, '../src/server/lib/logging')}$`]:
path.resolve(__dirname, './mocks/log4js.ts'),

// Mock BridgetApi for storybook
[`${path.resolve(__dirname, '../src/lib/bridgetApi')}$`]:
path.resolve(__dirname, './mocks/bridgetApi.ts'),

// Mock identity auth frontend to prevent Storybook components from hanging in Pending
'@guardian/identity-auth-frontend': path.resolve(
__dirname,
'./mocks/identityAuthFrontend.ts',
),
};

return config;
},

env: (config) => ({
Expand All @@ -85,76 +87,11 @@ export default defineMain({
},

framework: {
name: '@storybook/react-webpack5',
name: '@storybook/react-vite',
options: {},
},

typescript: {
reactDocgen: 'react-docgen',
},
});

/** the webpack.Configuration type from Storybook */
type Configuration = Parameters<
NonNullable<StorybookConfig['webpackFinal']>
>[0];

const webpackConfig = (config: Configuration) => {
const rules = config.module?.rules ?? [];

config.resolve ??= {};

config.resolve.alias = {
...config.resolve.alias,

Buffer: 'buffer',
react: 'react',
'react-dom': 'react-dom',

// Mock JSDOM for storybook - it relies on native node.js packages
// Allows us to use enhancers in stories for better testing of components & full articles
jsdom$: path.resolve(import.meta.dirname, './mocks/jsdom.ts'),

// log4js tries to call "fs" in storybook -- we can ignore it
[`${path.resolve(import.meta.dirname, '../src/server/lib/logging')}$`]:
path.resolve(import.meta.dirname, './mocks/log4js.ts'),

// Mock BridgetApi for storybook
[`${path.resolve(import.meta.dirname, '../src/lib/bridgetApi')}$`]:
path.resolve(import.meta.dirname, './mocks/bridgetApi.ts'),

// Mock identity auth frontend to prevent Storybook components from hanging in Pending
'@guardian/identity-auth-frontend': path.resolve(
import.meta.dirname,
'./mocks/identityAuthFrontend.ts',
),
};

const webpackLoaders = getLoaders('client.web');

// https://swc.rs/docs/usage/swc-loader#with-babel-loader
if (webpackLoaders[0].loader.startsWith('swc')) {
webpackLoaders[0].options.parseMap = true;
}

// Support typescript in Storybook
// https://storybook.js.org/docs/configurations/typescript-config/
rules.push({
test: /\.[jt]sx?|mjs$/,
include: [path.resolve(import.meta.dirname, '../')],
exclude: transpileExclude,
use: webpackLoaders,
});

// modify storybook's file-loader rule to avoid conflicts with our svg
const fileLoaderRule = rules.find((rule) => {
return String(rule?.test).includes('svg');
});
if (fileLoaderRule && typeof fileLoaderRule !== 'string') {
fileLoaderRule.exclude = /\.svg$/;
}
rules.push(svgr);
config.resolve.modules = [...(config.resolve.modules || [])];

return config;
};
2 changes: 1 addition & 1 deletion dotcom-rendering/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { resets } from '@guardian/source/foundations';
import { palette as sourcePalette } from '@guardian/source/foundations';
import addonA11y from '@storybook/addon-a11y';
import addonDocs from '@storybook/addon-docs';
import { definePreview } from '@storybook/react-webpack5';
import isChromatic from 'chromatic/isChromatic';
import MockDate from 'mockdate';
import { sb } from 'storybook/test';
Expand All @@ -14,6 +13,7 @@ import { mockFetch } from '../src/lib/mockRESTCalls';
import { ABTests } from '../src/experiments/lib/ab-tests';
import { setABTests } from '../src/lib/useAB';
import { ConfigContextDecorator } from './decorators/configContextDecorator';
import { definePreview } from '@storybook/react-vite';
import {
globalColourScheme,
globalColourSchemeDecorator,
Expand Down
6 changes: 3 additions & 3 deletions dotcom-rendering/.storybook/toolbar/globalColourScheme.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Decorator, Preview } from '@storybook/react-webpack5';
import type { Decorator, Preview } from '@storybook/react-vite';
import { lightDecorator, darkDecorator } from '../decorators/themeDecorator';
import { splitTheme } from '../decorators/splitThemeDecorator';
import {
ArticleDesign,
ArticleDisplay,
type ArticleFormat,
Pillar,
} from '../../src/lib/articleFormat';
import { splitTheme } from '../decorators/splitThemeDecorator';
import { darkDecorator, lightDecorator } from '../decorators/themeDecorator';

const defaultFormat: ArticleFormat = {
display: ArticleDisplay.Standard,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
"runtime": "automatic",
"importSource": "@emotion/react"
}
},
"experimental": {
"plugins": [["@swc/plugin-emotion", {}]]
}
},
"sourceMaps": true
Expand Down
10 changes: 10 additions & 0 deletions dotcom-rendering/__mocks__/islandRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Jest mock for islandRegistry.ts which uses import.meta.glob
* (not supported by Jest/SWC).
*/
export const getIslandModule = (
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- mock
_name: string,
): (() => Promise<Record<string, unknown>>) | undefined => {
return undefined;
};
5 changes: 5 additions & 0 deletions dotcom-rendering/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ declare module 'dynamic-import-polyfill' {
}) => void;
}

declare module '@babel/helper-compilation-targets' {
const getTargets: (opts: { browsers: string[] }) => Record<string, string>;
export default getTargets;
}

// SVG handling
declare module '*.svg' {
const content: any;
Expand Down
3 changes: 2 additions & 1 deletion dotcom-rendering/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const swcConfig = require('./webpack/.swcrc.json');
const swcConfig = require('./.swcrc.jest.json');

const esModules = [
'@guardian/',
Expand Down Expand Up @@ -27,6 +27,7 @@ module.exports = {
moduleNameMapper: {
'^svgs/(.*)$': '<rootDir>/__mocks__/svgMock.tsx',
'^(.*)\\.svg$': '<rootDir>/__mocks__/svgMock.tsx',
'(.*)/islandRegistry': '<rootDir>/__mocks__/islandRegistry.ts',
},
transformIgnorePatterns: [`/node_modules/.pnpm/(?!${esModules})`],
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
Expand Down
Loading
Loading