diff --git a/src/Plugin/ExperienceBuilder/ComponentSource/JsComponent.php b/src/Plugin/ExperienceBuilder/ComponentSource/JsComponent.php index ba5ce12f5ce46d190f094e1b50afe2788eba31ae..4a42e40534835cc6886d924af998906e445a3327 100644 --- a/src/Plugin/ExperienceBuilder/ComponentSource/JsComponent.php +++ b/src/Plugin/ExperienceBuilder/ComponentSource/JsComponent.php @@ -115,6 +115,12 @@ final class JsComponent extends GeneratedFieldExplicitInputUxComponentSourceBase 'preact' => \sprintf('%s%s/ui/lib/astro-hydration/dist/preact.module.js', $base_path, $xb_path), 'preact/hooks' => \sprintf('%s%s/ui/lib/astro-hydration/dist/hooks.module.js', $base_path, $xb_path), 'react/jsx-runtime' => \sprintf('%s%s/ui/lib/astro-hydration/dist/jsxRuntime.module.js', $base_path, $xb_path), + // @todo Remove this hard-coding and calculate it on a per component + // basis - see https://drupal.org/i/3500761. + 'clsx' => \sprintf('%s%s/ui/lib/astro-hydration/dist/clsx.js', $base_path, $xb_path), + 'class-variance-authority' => \sprintf('%s%s/ui/lib/astro-hydration/dist/class-variance-authority.js', $base_path, $xb_path), + 'tailwind-merge' => \sprintf('%s%s/ui/lib/astro-hydration/dist/tailwind-merge.js', $base_path, $xb_path), + '@/lib/utils' => \sprintf('%s%s/ui/lib/astro-hydration/dist/utils.js', $base_path, $xb_path), ], // @todo Rename plugin ID in https://www.drupal.org/project/experience_builder/issues/3502982 '#component' => $this->configuration['plugin_id'], diff --git a/tests/src/Kernel/DataType/ComponentTreeHydratedTest.php b/tests/src/Kernel/DataType/ComponentTreeHydratedTest.php index 273ad589b12ae4fa8f18a5791466ae5cb3b596c9..5f5dce771fe03e50a19d11d92765d787bf3d158e 100644 --- a/tests/src/Kernel/DataType/ComponentTreeHydratedTest.php +++ b/tests/src/Kernel/DataType/ComponentTreeHydratedTest.php @@ -804,6 +804,10 @@ HTML, 'preact' => \sprintf('%s/ui/lib/astro-hydration/dist/preact.module.js', $path), 'preact/hooks' => \sprintf('%s/ui/lib/astro-hydration/dist/hooks.module.js', $path), 'react/jsx-runtime' => \sprintf('%s/ui/lib/astro-hydration/dist/jsxRuntime.module.js', $path), + 'clsx' => \sprintf('%s/ui/lib/astro-hydration/dist/clsx.js', $path), + 'class-variance-authority' => \sprintf('%s/ui/lib/astro-hydration/dist/class-variance-authority.js', $path), + 'tailwind-merge' => \sprintf('%s/ui/lib/astro-hydration/dist/tailwind-merge.js', $path), + '@/lib/utils' => \sprintf('%s/ui/lib/astro-hydration/dist/utils.js', $path), ], '#component' => 'my-cta', '#props' => [ diff --git a/ui/lib/astro-hydration/astro.config.mjs b/ui/lib/astro-hydration/astro.config.mjs index 344ae4cbc1789807cc56f6f85f7c26c71feb5c3b..fee0f554fee34b793167222ae6d3add43cce2c75 100644 --- a/ui/lib/astro-hydration/astro.config.mjs +++ b/ui/lib/astro-hydration/astro.config.mjs @@ -1,17 +1,42 @@ import { defineConfig } from 'astro/config'; import preact from '@astrojs/preact'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); // https://astro.build/config export default defineConfig({ // Enable Preact to support Preact JSX components. integrations: [preact()], vite: { + resolve: { + alias: { + '@': path.resolve(__dirname, 'src/'), + }, + }, build: { rollupOptions: { output: { // Filename pattern for the output files entryFileNames: '[name].js', - chunkFileNames: '[name].js', + chunkFileNames: (chunkInfo) => { + // Make sure the output chunks for dependencies have useful file + // names so we can easily distinguish between them. + const matches = { + clsx: 'clsx.js', + 'class-variance-authority': 'class-variance-authority.js', + 'tailwind-merge': 'tailwind-merge.js', + 'lib/astro-hydration/src/lib/utils.ts': 'util.js', + }; + return Object.entries(matches).reduce((carry, [key, value]) => { + if (chunkInfo.facadeModuleId?.includes(`node_modules/${key}`)) { + return value; + } + return carry; + }, '[name].js'); + }, assetFileNames: '[name][extname]', }, }, diff --git a/ui/lib/astro-hydration/package.json b/ui/lib/astro-hydration/package.json index 429a2cb5907e3526e8d19c65957ec417b087c6b8..24818c95f029fecb02ce72806ef85e806cfa3174 100644 --- a/ui/lib/astro-hydration/package.json +++ b/ui/lib/astro-hydration/package.json @@ -8,6 +8,9 @@ "dependencies": { "@astrojs/preact": "^4.0.3", "astro": "^5.2.2", - "preact": "^10.25.4" + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "preact": "^10.25.4", + "tailwind-merge": "^3.0.2" } } diff --git a/ui/lib/astro-hydration/src/components/Stub.jsx b/ui/lib/astro-hydration/src/components/Stub.jsx index a49cc7d38553610399d9f095fdf6157cbb3ef105..c9936f417376b5ddbdb1c1ffd4a99a11aa583110 100644 --- a/ui/lib/astro-hydration/src/components/Stub.jsx +++ b/ui/lib/astro-hydration/src/components/Stub.jsx @@ -8,5 +8,9 @@ const { ...preactHooks } = await import('preact/hooks'); const { jsx, jsxs, Fragment } = await import('preact/jsx-runtime'); +const { default: clsx } = await import('clsx'); +const { ...tailwindMerge } = await import('tailwind-merge'); +const { cva } = await import('class-variance-authority'); +const { cn } = await import('@/lib/utils'); export default function () {} diff --git a/ui/lib/astro-hydration/src/lib/utils.ts b/ui/lib/astro-hydration/src/lib/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1ca34a146a3fae4ed54a0099f2ee64aee690255 --- /dev/null +++ b/ui/lib/astro-hydration/src/lib/utils.ts @@ -0,0 +1,7 @@ +import { clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; +import type { ClassValue } from 'clsx'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/ui/lib/astro-hydration/tsconfig.json b/ui/lib/astro-hydration/tsconfig.json index eb8b4ca6c22d79d30f606c9aab4da51330024f24..e8e64a216ca716f9b4e3c4f3bcc4a803bbf388ca 100644 --- a/ui/lib/astro-hydration/tsconfig.json +++ b/ui/lib/astro-hydration/tsconfig.json @@ -10,6 +10,9 @@ ], "compilerOptions": { "jsx": "react-jsx", - "jsxImportSource": "preact" + "jsxImportSource": "preact", + "paths": { + "@/*": ["src/*"], + } } } diff --git a/ui/package-lock.json b/ui/package-lock.json index 8542a424046c37b3bee7f374d96fa7c056c730bf..77cc4afe3b7856ed1b73b40952336cc0b474e510 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -103,7 +103,10 @@ "dependencies": { "@astrojs/preact": "^4.0.3", "astro": "^5.2.2", - "preact": "^10.25.4" + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "preact": "^10.25.4", + "tailwind-merge": "^3.0.2" } }, "node_modules/@adobe/css-tools": { @@ -8891,6 +8894,17 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -17308,6 +17322,15 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwind-merge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", + "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss-in-browser": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/tailwindcss-in-browser/-/tailwindcss-in-browser-0.1.3.tgz", diff --git a/ui/src/features/code-editor/Preview.tsx b/ui/src/features/code-editor/Preview.tsx index 29293052cfafaf24009a42e166fad8eba2391f6c..ca347a373acf919ae310d5856d97573887307bc0 100644 --- a/ui/src/features/code-editor/Preview.tsx +++ b/ui/src/features/code-editor/Preview.tsx @@ -67,6 +67,11 @@ const importMap = { 'react/': 'https://esm.sh/preact/compat/', 'react-dom': 'https://esm.sh/preact/compat', 'react-dom/': 'https://esm.sh/preact/compat/', + // @todo Remove hardcoding and allow components to nominate their own? + clsx: 'https://esm.sh/clsx', + 'class-variance-authority': 'https://esm.sh/class-variance-authority', + 'tailwind-merge': 'https://esm.sh/tailwind-merge', + '@/lib/utils': `${XB_MODULE_UI_PATH}/lib/astro-hydration/dist/utils.js`, }, };