Overview

Currently, the js.compiled property of code components and asset libraries imports React/Preact code from bare module specifiers: e.g., react/jsx-runtime, preact/hooks, etc.

When the browser loads the JS file (aka the astro island's component-url) of the code component, it needs to know where to get that Preact code from.

Proposed resolution

The following options are ordered from least scope to most scope. Any option is acceptable for resolving this issue. If we only do option 1 or option 2 for now, we can do whatever remains to get us to option 3 in a less urgent follow-up.

Option 1

Within XbAssetLibraryTrait::getJs(), do string/regex replacements to change js.compiled before it's written to disk. Change the bare module specifiers to the corresponding paths to the files in ui/lib/astro-hydration/dist. See https://git.drupalcode.org/project/experience_builder/-/merge_requests/3... for prior art in the PoC.

Option 2

Do the same as option 1, but instead of changing the contents of the JS that gets written to disk, add those replacements to an import map. Use https://www.drupal.org/project/importmaps or https://www.drupal.org/project/importmap if that's helpful, but we can also for now assume that only our import map is needed and not worry about merging with import maps from other Drupal modules. Browser support for multiple import maps is coming soon, which might obsolete the importmap(s) Drupal modules.

Option 3

Do the same as option 2, but make the import map map the React packages to preact/compat from https://esm.sh. Along with this, change ui/lib/astro-hydration/astro.config.mjs to use React instead of Preact and to externalize React.

Note that the scope of this issue only needs to cover React/Preact itself, not other 3rd party packages. #3500761: Allow code components to import from npm packages is a separate issue for the latter.

User interface changes

CommentFileSizeAuthor
#6 Screenshot from 2025-02-21 15-09-22.png138.91 KBlarowlan
Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

effulgentsia created an issue. See original summary.

effulgentsia’s picture

Issue tags: +sprint
effulgentsia’s picture

Issue summary: View changes
larowlan’s picture

Assigned: Unassigned » larowlan

larowlan’s picture

Assigned: larowlan » Unassigned
Status: Active » Needs review
StatusFileSize
new138.91 KB

In my local testing this is allowing hydration although props aren't being passed through properly.
Ran out of time to work out what is wrong with props:

Here's my component

uuid: 221b3268-089f-48dd-ad84-52a9d9dd868c
langcode: en
status: true
dependencies:
  config:
    - experience_builder.js_component.chips
label: Chips
id: js.chips
source: js
category: '@todo'
settings:
  plugin_id: chips
  prop_field_definitions:
    name:
      field_type: string
      field_storage_settings: {  }
      field_instance_settings: {  }
      field_widget: string_textfield
      default_value:
        value: steve
      expression: ℹ︎string␟value

And my Javascript component:

uuid: 690ce531-b957-42f2-925d-cac7bd042cf0
langcode: en
status: true
dependencies: {  }
machineName: chips
name: Chips
required: {  }
props:
  name:
    title: Name
    type: string
    examples:
      - steve
slots: {  }
js:
  original: "const Title = ({name} = 'Steve') => {\n  return <h1 className=\"biggin\">Heya {name}</h1>\n}\nexport default Title;"
  compiled: "import { jsxs as _jsxs } from \"react/jsx-runtime\";\nconst Title = ({ name } = 'Steve')=>{\n    return /*#__PURE__*/ _jsxs(\"h1\", {\n        className: \"biggin\",\n        children: [\n            \"Heya \",\n            name\n        ]\n    });\n};\nexport default Title;\n"
css:
  original: '.biggin { font-size: 5rem;}'
  compiled: "/*removed a load of tailwind stuff here that I didn't add*/.biggin{font-size:5rem}"

Here's how it is rendered in the island

<astro-island uid="14806625-5dd9-4035-9da4-93cccf9859a8" component-url="/sites/default/files/astro-island/i1mwDMruPt1dJK1tlNyp7MGMN38P9JKooOtZVqAxnQc.js" component-export="default" renderer-url="/modules/experience_builder/ui/lib/astro-hydration/dist/client.js" props="{&quot;name&quot;:&quot;bobby&quot;}" client="only" opts="{&quot;name&quot;:&quot;Chips&quot;,&quot;value&quot;:&quot;preact&quot;}" data-xb-uuid="14806625-5dd9-4035-9da4-93cccf9859a8"><h1 class="biggin">Heya </h1></astro-island>

You can see the name prop is being set. but not being rendered properly

Screenshot showing 3 placed instances

larowlan’s picture

Ignore the default value in the component above {name} = 'Steve' if I fix that to {name = 'Steve' } I get a name to show, but it's always Steve, i.e. the props still don't get passed in.

effulgentsia’s picture

props="{&quot;name&quot;:&quot;bobby&quot;}"

Astro receives props as tuples, so this would need to be props="{&quot;name&quot;:[0,&quot;bobby&quot;]}". #3505993: Code Components as Block Overrides, step 1 contains a more generic fix for non-scalar prop values by using 'raw' instead of 0. In any case, this is pre-existing in 0.x and not this MR's responsibility to fix.

effulgentsia’s picture

Status: Needs review » Fixed

This is a really nice implementation of option #2. Merged to 0.x.

balintbrews’s picture

I made #3508562: Fix code component prop serialization in Astro islands for #7 and #8, I thought it would be useful to land that sooner.

wim leers’s picture

Very nice use of the html_response.attachments_processor infrastructure, in two ways:

  1. service decoration to insert ourselves in the right spot
  2. declaring one new kind of attachment (import_maps) and mapping it to a pre-existing one provided by core (html_head), which then allows it to transparently work everywhere!

(This is the same overall strategy that BigPipe in core uses.)

This merits being documented in #3500945: Documentation for code components.

nagwani’s picture

Issue tags: -sprint

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.