Docker / Docker Buildx (push) Has been cancelled
Build Debian package / Build package (release) Has been cancelled
Build and Deploy / prepare (release) Has been cancelled
Deploy release / Deploy to Cloudflare Pages (release) Has been cancelled
Build and Deploy / Trigger Pro pipeline (release) Has been cancelled
Build and Deploy / Windows arm64 (release) Has been cancelled
Build and Deploy / Windows x64 (release) Has been cancelled
Build and Deploy / macOS (release) Has been cancelled
Build and Deploy / Linux amd64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / Linux arm64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }} (release) Has been cancelled
Build and Deploy / Deploy builds to ESS (release) Has been cancelled
129 lines
5.0 KiB
TypeScript
129 lines
5.0 KiB
TypeScript
/*
|
|
Copyright 2025 New Vector Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import type { StorybookConfig } from "@storybook/react-vite";
|
|
import fs from "node:fs";
|
|
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
|
import { mergeConfig, normalizePath, type Plugin } from "vite";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const srcRoot = normalizePath(join(__dirname, "..", "src"));
|
|
const sharedComponentsLayer = "shared-components";
|
|
|
|
// Get a list of available languages so the language selector can display them at runtime
|
|
const languageFiles = fs.readdirSync(join(__dirname, "..", "src", "i18n", "strings")).map((f) => f.slice(0, -5));
|
|
|
|
const languages: Record<string, string> = {};
|
|
for (const lang of languageFiles) {
|
|
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
|
|
const languageParts = normalizedLanguage.split("-");
|
|
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
|
|
languages[languageParts[0]] = `${lang}.json`;
|
|
} else {
|
|
languages[normalizedLanguage] = `${lang}.json`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function is used to resolve the absolute path of a package.
|
|
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
|
*/
|
|
function getAbsolutePath(value: string): any {
|
|
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
|
|
}
|
|
|
|
function layerSharedComponentCssModules(): Plugin {
|
|
return {
|
|
name: "element-web-shared-components-storybook-css-layer",
|
|
enforce: "pre",
|
|
transform(code, id) {
|
|
const cssPath = normalizePath(id.split("?")[0]);
|
|
if (!cssPath.startsWith(srcRoot) || !cssPath.endsWith(".module.css")) {
|
|
return;
|
|
}
|
|
|
|
return {
|
|
code: `@layer ${sharedComponentsLayer} {\n${code}\n}\n`,
|
|
map: null,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
const config: StorybookConfig = {
|
|
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
|
addons: [
|
|
"@storybook/addon-docs",
|
|
"@storybook/addon-designs",
|
|
"@storybook/addon-a11y",
|
|
"@storybook/addon-vitest",
|
|
getAbsolutePath("storybook-addon-vis"),
|
|
],
|
|
framework: "@storybook/react-vite",
|
|
core: {
|
|
disableTelemetry: true,
|
|
},
|
|
typescript: {
|
|
reactDocgen: "react-docgen-typescript",
|
|
reactDocgenTypescriptOptions: {
|
|
// The default exclude is ["**/**.stories.tsx"] which prevents
|
|
// docgen from extracting snapshot field descriptions from wrapper
|
|
// components defined in story files.
|
|
exclude: [],
|
|
},
|
|
},
|
|
async viteFinal(config) {
|
|
return mergeConfig(config, {
|
|
plugins: [
|
|
layerSharedComponentCssModules(),
|
|
// Needed for counterpart to work
|
|
nodePolyfills({ include: ["util"], globals: { global: false } }),
|
|
{
|
|
name: "language-middleware",
|
|
configureServer(server) {
|
|
server.middlewares.use((req, res, next) => {
|
|
if (req.url === "/i18n/languages.json") {
|
|
// Dynamically generate a languages.json file based on what files are available
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.end(JSON.stringify(languages));
|
|
} else if (req.url === "/usercontent/" || req.url === "/usercontent") {
|
|
// Mock usercontent endpoint used by encrypted download iframes.
|
|
res.end("This is where /usercontent/ is loaded.");
|
|
} else if (req.url?.startsWith("/i18n/")) {
|
|
// Serve the individual language files, which annoyingly can't be a simple
|
|
// static dir because the directory structure in src doesn't match what
|
|
// the app requests.
|
|
const langFile = req.url.split("/").pop();
|
|
res.setHeader("Content-Type", "application/json");
|
|
fs.createReadStream(`src/i18n/strings/${langFile}`).pipe(res);
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
},
|
|
},
|
|
],
|
|
server: {
|
|
allowedHosts: ["localhost", ".docker.internal"],
|
|
},
|
|
});
|
|
},
|
|
refs: {
|
|
"compound-web": {
|
|
title: "Compound Web",
|
|
url: "https://element-hq.github.io/compound-web/",
|
|
},
|
|
},
|
|
env: (config) => ({
|
|
...config,
|
|
STORYBOOK_LANGUAGES: JSON.stringify(Object.keys(languages)),
|
|
}),
|
|
};
|
|
export default config;
|