web: fix storybook build after npm update (#7855)

* web: fix storybookbuild build after npm update

This commit follows the [patch for Turnstile](https://github.com/goauthentik/authentik/pull/7854) and
performs a similar operation for the Storybook build, which failed after the latest `npm audit` and
`npm update` passes.

[This patch to Vite](https://github.com/vitejs/vite/pull/10762) fixes a problem with the Vite build
in that Vite could not resolve if a CSS import was strictly at the module level or if it was
necessary to include the imported CSS at the document level.  The fix is to hack a query, `?inline`,
to the end of the import string, to indicate that it's a module-only import.

The Storybook for Web Components build recommended by the Open Webcomponent Consortium is a
Storybook-Vite implementation.  The latest update fully deprecated undecorated CSS imports, and
Storybook broke, unable to reconcile the CSS imports.

This patch inlines the inlining of the CSS automatically for Storybook by using the Rollup
`modify()` plug-in which performs string substitutions on the source code before it's presented to
the compiler and bundler; it recognizes the strings that require inlining, those that match the
regex:

``` JavaScript
/^(import \w+ from .*\.css)";/
```

... and replaces them with a version ending in `.css?inline`.  Because the actual recognizer inside
`modify()` recognizes strings and not regular expressions, a script to build the strings has been
added to the `scripts` folder.

Just like locales, you will have to re-run and re-build `build-storybook-import-maps` script if you
add a new CSS file to the source tree.

* web: prettier had opinions

* web: apply eslint + sonarjs check to the scripts folder.

* Google recaptcha (aka Turnstile) doesn't understand the "invisible" setting; that's purely
an HCaptcha thing.

* web: removing the typecast means I no longer need the type.

* web: prettier is still having opinions, dammit.
This commit is contained in:
Ken Sternberg 2023-12-18 06:57:39 -08:00 committed by GitHub
parent 4a8fb8e14d
commit d3cbe26106
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 277 additions and 4 deletions

View File

@ -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";',
};

View File

@ -1,9 +1,12 @@
import replace from "@rollup/plugin-replace"; import replace from "@rollup/plugin-replace";
import type { StorybookConfig } from "@storybook/web-components-vite"; import type { StorybookConfig } from "@storybook/web-components-vite";
import { cwd } from "process"; import { cwd } from "process";
import modify from "rollup-plugin-modify";
import postcssLit from "rollup-plugin-postcss-lit"; import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
import { cssImportMaps } from "./css-import-maps";
export const isProdBuild = process.env.NODE_ENV === "production"; export const isProdBuild = process.env.NODE_ENV === "production";
export const apiBasePath = process.env.AK_API_BASE_PATH || ""; export const apiBasePath = process.env.AK_API_BASE_PATH || "";
@ -27,9 +30,7 @@ const config: StorybookConfig = {
return { return {
...config, ...config,
plugins: [ plugins: [
...config.plugins, modify(cssImportMaps),
postcssLit(),
tsconfigPaths(),
replace({ replace({
"process.env.NODE_ENV": JSON.stringify( "process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development", isProdBuild ? "production" : "development",
@ -38,6 +39,9 @@ const config: StorybookConfig = {
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath), "process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true, "preventAssignment": true,
}), }),
...config.plugins,
postcssLit(),
tsconfigPaths(),
], ],
}; };
}, },

52
web/package-lock.json generated
View File

@ -93,6 +93,7 @@
"rollup": "^4.9.1", "rollup": "^4.9.1",
"rollup-plugin-copy": "^3.5.0", "rollup-plugin-copy": "^3.5.0",
"rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-modify": "^3.0.0",
"rollup-plugin-postcss-lit": "^2.1.0", "rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^7.6.5", "storybook": "^7.6.5",
"storybook-addon-mock": "^4.3.0", "storybook-addon-mock": "^4.3.0",
@ -14942,6 +14943,38 @@
"node": ">=8" "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": { "node_modules/p-limit": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -16376,6 +16409,25 @@
"node": ">= 8.0.0" "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": { "node_modules/rollup-plugin-postcss-lit": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-postcss-lit/-/rollup-plugin-postcss-lit-2.1.0.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-postcss-lit/-/rollup-plugin-postcss-lit-2.1.0.tgz",

View File

@ -28,7 +28,10 @@
"tsc:execute": "tsc --noEmit -p .", "tsc:execute": "tsc --noEmit -p .",
"tsc": "run-s build-locales tsc:execute", "tsc": "run-s build-locales tsc:execute",
"storybook": "storybook dev -p 6006", "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": { "dependencies": {
"@codemirror/lang-html": "^6.4.7", "@codemirror/lang-html": "^6.4.7",
@ -115,6 +118,7 @@
"rollup": "^4.9.1", "rollup": "^4.9.1",
"rollup-plugin-copy": "^3.5.0", "rollup-plugin-copy": "^3.5.0",
"rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-modify": "^3.0.0",
"rollup-plugin-postcss-lit": "^2.1.0", "rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^7.6.5", "storybook": "^7.6.5",
"storybook-addon-mock": "^4.3.0", "storybook-addon-mock": "^4.3.0",

View File

@ -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<string, undefined, any> {
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",
});