diff --git a/web/.storybook/css-import-maps.ts b/web/.storybook/css-import-maps.ts new file mode 100644 index 000000000..13b55284e --- /dev/null +++ b/web/.storybook/css-import-maps.ts @@ -0,0 +1,129 @@ +// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND. +// +// This file is generated by the build-storybook-import-maps script in the UI's base directory. +// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook +// import CSS modules. +// +// Sometime around 2030 or so, the Javascript community may finally get its collective act together +// and we'll have one unified way of doing this. I can only hope. + +export const cssImportMaps = { + 'import AKGlobal from "@goauthentik/common/styles/authentik.css";': + 'import AKGlobal from "@goauthentik/common/styles/authentik.css?inline";', + 'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";': + 'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css?inline";', + 'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";': + 'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css?inline";', + 'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";': + 'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css?inline";', + 'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";': + 'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css?inline";', + 'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";': + 'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css?inline";', + 'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";': + 'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css?inline";', + 'import PFBase from "@patternfly/patternfly/patternfly-base.css";': + 'import PFBase from "@patternfly/patternfly/patternfly-base.css?inline";', + 'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";': + 'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css?inline";', + 'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";': + 'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css?inline";', + 'import PFButton from "@patternfly/patternfly/components/Button/button.css";': + 'import PFButton from "@patternfly/patternfly/components/Button/button.css?inline";', + 'import PFCard from "@patternfly/patternfly/components/Card/card.css";': + 'import PFCard from "@patternfly/patternfly/components/Card/card.css?inline";', + 'import PFCheck from "@patternfly/patternfly/components/Check/check.css";': + 'import PFCheck from "@patternfly/patternfly/components/Check/check.css?inline";', + 'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";': + 'import PFChip from "@patternfly/patternfly/components/Chip/chip.css?inline";', + 'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";': + 'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css?inline";', + 'import PFContent from "@patternfly/patternfly/components/Content/content.css";': + 'import PFContent from "@patternfly/patternfly/components/Content/content.css?inline";', + 'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";': + 'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css?inline";', + 'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";': + 'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css?inline";', + 'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";': + 'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css?inline";', + 'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";': + 'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css?inline";', + 'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";': + 'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css?inline";', + 'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";': + 'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css?inline";', + 'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";': + 'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css?inline";', + 'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";': + 'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css?inline";', + 'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";': + 'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css?inline";', + 'import PFForm from "@patternfly/patternfly/components/Form/form.css";': + 'import PFForm from "@patternfly/patternfly/components/Form/form.css?inline";', + 'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";': + 'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css?inline";', + 'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";': + 'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css?inline";', + 'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";': + 'import PFGlobal from "@patternfly/patternfly/patternfly-base.css?inline";', + 'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";': + 'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css?inline";', + 'import PFHint from "@patternfly/patternfly/components/Hint/hint.css";': + 'import PFHint from "@patternfly/patternfly/components/Hint/hint.css?inline";', + 'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";': + 'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css?inline";', + 'import PFLabel from "@patternfly/patternfly/components/Label/label.css";': + 'import PFLabel from "@patternfly/patternfly/components/Label/label.css?inline";', + 'import PFList from "@patternfly/patternfly/components/List/list.css";': + 'import PFList from "@patternfly/patternfly/components/List/list.css?inline";', + 'import PFLogin from "@patternfly/patternfly/components/Login/login.css";': + 'import PFLogin from "@patternfly/patternfly/components/Login/login.css?inline";', + 'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";': + 'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css?inline";', + 'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";': + 'import PFNav from "@patternfly/patternfly/components/Nav/nav.css?inline";', + 'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";': + 'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css?inline";', + 'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";': + 'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css?inline";', + 'import PFPage from "@patternfly/patternfly/components/Page/page.css";': + 'import PFPage from "@patternfly/patternfly/components/Page/page.css?inline";', + 'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";': + 'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css?inline";', + 'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";': + 'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css?inline";', + 'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";': + 'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css?inline";', + 'import PFSelect from "@patternfly/patternfly/components/Select/select.css";': + 'import PFSelect from "@patternfly/patternfly/components/Select/select.css?inline";', + 'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";': + 'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css?inline";', + 'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";': + 'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css?inline";', + 'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";': + 'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css?inline";', + 'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";': + 'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css?inline";', + 'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";': + 'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css?inline";', + 'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";': + 'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css?inline";', + 'import PFTable from "@patternfly/patternfly/components/Table/table.css";': + 'import PFTable from "@patternfly/patternfly/components/Table/table.css?inline";', + 'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";': + 'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css?inline";', + 'import PFTitle from "@patternfly/patternfly/components/Title/title.css";': + 'import PFTitle from "@patternfly/patternfly/components/Title/title.css?inline";', + 'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";': + 'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css?inline";', + 'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";': + 'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css?inline";', + 'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";': + 'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css?inline";', + 'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";': + 'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css?inline";', + 'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";': + 'import ThemeDark from "@goauthentik/common/styles/theme-dark.css?inline";', + 'import styles from "./LibraryPageImpl.css";': + 'import styles from "./LibraryPageImpl.css?inline";', +}; diff --git a/web/.storybook/main.ts b/web/.storybook/main.ts index e6583e476..c3f042282 100644 --- a/web/.storybook/main.ts +++ b/web/.storybook/main.ts @@ -1,9 +1,12 @@ import replace from "@rollup/plugin-replace"; import type { StorybookConfig } from "@storybook/web-components-vite"; import { cwd } from "process"; +import modify from "rollup-plugin-modify"; import postcssLit from "rollup-plugin-postcss-lit"; import tsconfigPaths from "vite-tsconfig-paths"; +import { cssImportMaps } from "./css-import-maps"; + export const isProdBuild = process.env.NODE_ENV === "production"; export const apiBasePath = process.env.AK_API_BASE_PATH || ""; @@ -27,9 +30,7 @@ const config: StorybookConfig = { return { ...config, plugins: [ - ...config.plugins, - postcssLit(), - tsconfigPaths(), + modify(cssImportMaps), replace({ "process.env.NODE_ENV": JSON.stringify( isProdBuild ? "production" : "development", @@ -38,6 +39,9 @@ const config: StorybookConfig = { "process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath), "preventAssignment": true, }), + ...config.plugins, + postcssLit(), + tsconfigPaths(), ], }; }, diff --git a/web/package-lock.json b/web/package-lock.json index 982d2090f..08a019f30 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -93,6 +93,7 @@ "rollup": "^4.9.1", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", + "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", "storybook": "^7.6.5", "storybook-addon-mock": "^4.3.0", @@ -14942,6 +14943,38 @@ "node": ">=8" } }, + "node_modules/ospec": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ospec/-/ospec-3.1.0.tgz", + "integrity": "sha512-+nGtjV3vlADp+UGfL51miAh/hB4awPBkQrArhcgG4trAaoA2gKt5bf9w0m9ch9zOr555cHWaCHZEDiBOkNZSxw==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "ospec": "bin/ospec" + } + }, + "node_modules/ospec/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -16376,6 +16409,25 @@ "node": ">= 8.0.0" } }, + "node_modules/rollup-plugin-modify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-modify/-/rollup-plugin-modify-3.0.0.tgz", + "integrity": "sha512-p/ffs0Y2jz2dEnWjq1oVC7SY37tuS+aP7whoNaQz1EAAOPg+k3vKJo8cMMWx6xpdd0NzhX4y2YF9o/NPu5YR0Q==", + "dev": true, + "dependencies": { + "magic-string": "0.25.2", + "ospec": "3.1.0" + } + }, + "node_modules/rollup-plugin-modify/node_modules/magic-string": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz", + "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, "node_modules/rollup-plugin-postcss-lit": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/rollup-plugin-postcss-lit/-/rollup-plugin-postcss-lit-2.1.0.tgz", diff --git a/web/package.json b/web/package.json index 39fac3dcc..666077e8f 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,10 @@ "tsc:execute": "tsc --noEmit -p .", "tsc": "run-s build-locales tsc:execute", "storybook": "storybook dev -p 6006", - "storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' storybook build" + "storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' storybook build", + "storybook:build-import-map": "run-s storybook:build-import-map-script storybook:run-import-map-script", + "storybook:build-import-map-script": "cd scripts && tsc --esModuleInterop --module es2020 --target es2020 --moduleResolution 'node' build-storybook-import-maps.ts && mv build-storybook-import-maps.js build-storybook-import-maps.mjs", + "storybook:run-import-map-script": "node scripts/build-storybook-import-maps.mjs" }, "dependencies": { "@codemirror/lang-html": "^6.4.7", @@ -115,6 +118,7 @@ "rollup": "^4.9.1", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", + "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", "storybook": "^7.6.5", "storybook-addon-mock": "^4.3.0", diff --git a/web/scripts/build-storybook-import-maps.ts b/web/scripts/build-storybook-import-maps.ts new file mode 100644 index 000000000..121ad105f --- /dev/null +++ b/web/scripts/build-storybook-import-maps.ts @@ -0,0 +1,84 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __dirname = fileURLToPath(new URL(".", import.meta.url)); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function* walkFilesystem(dir: string): Generator { + const openeddir = fs.opendirSync(dir); + if (!openeddir) { + return; + } + + let d: fs.Dirent | null; + while ((d = openeddir?.readSync())) { + if (!d) { + break; + } + const entry = path.join(dir, d.name); + if (d.isDirectory()) yield* walkFilesystem(entry); + else if (d.isFile()) yield entry; + } + openeddir.close(); +} + +const import_re = /^(import \w+ from .*\.css)";/; +function extractImportLinesFromFile(path: string) { + const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" }); + const lines = source?.split("\n") ?? []; + return lines.filter((l) => import_re.test(l)); +} + +function createOneImportLine(line: string) { + const importMatch = import_re.exec(line); + if (!importMatch) { + throw new Error("How did an unmatchable line get here?"); + } + const importContent = importMatch[1]; + if (!importContent) { + throw new Error("How did an unmatchable line get here!?"); + } + return `'${importContent}";': '${importContent}?inline";',`; +} + +const isSourceFile = /\.ts$/; +function getTheSourceFiles() { + return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) => + isSourceFile.test(path), + ); +} + +function getTheImportLines(importPaths: string[]) { + const importLines: string[] = importPaths.reduce( + (acc: string[], path) => [...acc, extractImportLinesFromFile(path)].flat(), + [], + ); + const uniqueImportLines = new Set(importLines); + const sortedImportLines = Array.from(uniqueImportLines.keys()); + sortedImportLines.sort(); + return sortedImportLines; +} + +const importPaths = getTheSourceFiles(); +const importLines = getTheImportLines(importPaths); + +const outputFile = ` +// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND. +// +// This file is generated by the build-storybook-import-maps script in the UI's base directory. +// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook +// import CSS modules. +// +// Sometime around 2030 or so, the Javascript community may finally get its collective act together +// and we'll have one unified way of doing this. I can only hope. + +export const cssImportMaps = { +${importLines.map(createOneImportLine).join("\n")} +}; +`; + +fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, { + encoding: "utf8", + flag: "w", +});