Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Utilities in `tests/utils.py`: `get_json()`, `get_html()`, `get_sections_types()

## WebComponents

React 18 + Webpack 5 app in `src/imio/smartweb/core/webcomponents/`. Single entry point (`src/index.jsx`) builds **5 custom HTML elements**:
React 18 + Vite app in `src/imio/smartweb/core/webcomponents/`. Single entry point (`src/index.jsx`) builds **5 custom HTML elements**:

| Element | Component | Purpose |
|---------|-----------|---------|
Expand All @@ -157,6 +157,9 @@ React 18 + Webpack 5 app in `src/imio/smartweb/core/webcomponents/`. Single entr

Each component follows the same structure: main component, Card, Content, List sub-components, and Filter.

Output: `build/js/smartweb-webcomponents-compiled.js` + code-split chunks.
Output: `build/js/smartweb-webcomponents-compiled.js` (ES module) + code-split chunks, loaded via
`type="module"` by the `imio.smartweb.webcomponents.js` viewlet (`viewlets/webcomponents.py`), not through
the resource registry (which can't render a `type` attribute). Set `VITE_DEV_URL=http://localhost:2000` to
load the bundle from `npm run watch` (Vite dev server) instead of the built production bundle.

Key deps: React Router DOM, React Bootstrap, Leaflet/React-Leaflet (maps), Axios, React-Select, React-DatePicker, date-fns.
4 changes: 4 additions & 0 deletions src/imio/smartweb/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
DIRECTORY_URL = os.environ.get("DIRECTORY_URL", "https://annuaire.enwallonie.be")
EVENTS_URL = os.environ.get("EVENTS_URL", "https://agenda.enwallonie.be")
NEWS_URL = os.environ.get("NEWS_URL", "https://actualites.enwallonie.be")
# Set to the Vite dev server URL (e.g. http://localhost:2000) to load the
# webcomponents bundle from `npm run watch` instead of the built production
# bundle. Leave empty in production.
VITE_DEV_URL = os.environ.get("VITE_DEV_URL", "")
2 changes: 1 addition & 1 deletion src/imio/smartweb/core/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<metadata>
<version>1080</version>
<version>1081</version>
<dependencies>
<dependency>profile-plone.app.dexterity:default</dependency>
<dependency>profile-plone.app.discussion:default</dependency>
Expand Down
5 changes: 4 additions & 1 deletion src/imio/smartweb/core/profiles/default/registry/bundles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@

<records prefix="plone.bundles/smartweb-webcomponents"
interface="Products.CMFPlone.interfaces.IBundleRegistry">
<!-- jscompilation is intentionally not set here: the JS entry point is
loaded as an ES module (type="module") by the
imio.smartweb.webcomponents.js viewlet instead, since this
registry has no way to render a "type" attribute on <script>. -->
<value key="enabled">True</value>
<value key="jscompilation">++plone++imio.smartweb.webcomponents/js/smartweb-webcomponents-compiled.js</value>
<value key="csscompilation">++plone++imio.smartweb.webcomponents/css/smartweb-webcomponents-compiled.css</value>
<value key="depends">plone</value>
<value key="load_async">False</value>
Expand Down
18 changes: 18 additions & 0 deletions src/imio/smartweb/core/upgrades/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,14 @@
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<genericsetup:registerProfile
name="upgrade_1080_to_1081"
title="Upgrade core from 1080 to 1081"
directory="profiles/1080_to_1081"
description="Clear stale jscompilation for smartweb-webcomponents bundle"
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<genericsetup:upgradeStep
title="Configure first official release"
description="Run needed profiles steps and reindex catalog"
Expand Down Expand Up @@ -1220,4 +1228,14 @@
/>
</genericsetup:upgradeSteps>

<genericsetup:upgradeSteps
source="1080"
destination="1081"
profile="imio.smartweb.core:default">
<genericsetup:upgradeDepends
title="Clear stale jscompilation for smartweb-webcomponents bundle (now loaded as an ES module by a viewlet)"
import_profile="imio.smartweb.core.upgrades:upgrade_1080_to_1081"
/>
</genericsetup:upgradeSteps>

</configure>
16 changes: 16 additions & 0 deletions src/imio/smartweb/core/upgrades/profiles/1080_to_1081/registry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<registry
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="imio.smartweb.core">

<!-- The webcomponents JS bundle is now loaded as an ES module
(type="module") by the imio.smartweb.webcomponents.js viewlet
instead of through the resource registry, which has no way to
render a "type" attribute on <script>. Clear the stale
jscompilation value left over from before this change. -->
<records prefix="plone.bundles/smartweb-webcomponents"
interface="Products.CMFPlone.interfaces.IBundleRegistry">
<value key="jscompilation"></value>
</records>

</registry>
10 changes: 10 additions & 0 deletions src/imio/smartweb/core/viewlets/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
/>
</configure>

<browser:viewlet
name="imio.smartweb.webcomponents.js"
for="*"
manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
template="webcomponents_js_header.pt"
class=".webcomponents.WebComponentsViewlet"
permission="zope2.View"
/>

<browser:viewlet
name="imio.smartweb.minisite_portal_link"
for="*"
Expand Down
18 changes: 18 additions & 0 deletions src/imio/smartweb/core/viewlets/webcomponents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-

from imio.smartweb.core import config
from plone.app.layout.viewlets import common


class WebComponentsViewlet(common.ViewletBase):
"""Loads the smartweb webcomponents bundle.

In production, the built ES module bundle is loaded through the
resource registry's static directory. During development, if
VITE_DEV_URL is set, the source entry point is loaded directly from
the Vite dev server instead, enabling real HMR.
"""

@property
def vite_dev_url(self):
return config.VITE_DEV_URL
23 changes: 23 additions & 0 deletions src/imio/smartweb/core/viewlets/webcomponents_js_header.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<tal:block define="portal_state context/@@plone_portal_state;
portal_url portal_state/portal_url">
<tal:dev condition="view/vite_dev_url">
<!-- @vitejs/plugin-react normally injects this preamble into index.html
itself before loading any component; since Plone serves its own
HTML (no Vite-generated index.html), it must be injected by hand
here, before @vite/client and the entry point. -->
<script type="module"
tal:content="structure string:import RefreshRuntime from '${view/vite_dev_url}/@react-refresh';
RefreshRuntime.injectIntoGlobalHook(window);
window.$$RefreshReg$$ = () => {};
window.$$RefreshSig$$ = () => (type) => type;
window.__vite_plugin_react_preamble_installed__ = true;"></script>
<script type="module"
tal:attributes="src string:${view/vite_dev_url}/@vite/client"></script>
<script type="module"
tal:attributes="src string:${view/vite_dev_url}/src/index.jsx"></script>
</tal:dev>
<tal:prod condition="not:view/vite_dev_url">
<script type="module"
tal:attributes="src string:${portal_url}/++plone++imio.smartweb.webcomponents/js/smartweb-webcomponents-compiled.js"></script>
</tal:prod>
</tal:block>
26 changes: 24 additions & 2 deletions src/imio/smartweb/core/webcomponents/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
```bash
npm run build # Production build → build/js/smartweb-webcomponents-compiled.js
npm run build-dev # Dev build (source maps, no minification)
npm run watch # Dev server on port 2000 (proxies API to localhost:8080)
npm run watch # Vite dev server on port 2000 (proxies everything to localhost:8080 except its own module graph)
npm run lint # ESLint (zero warnings tolerance)
npm run prettier # Check formatting (4-space tabs, 100-char width, trailing comma es5)
```

To actually see `npm run watch` changes reflected in Plone, run `make dev` from the buildout root
(`buildout.smartweb/`) instead of `make start` — it starts the instance with `VITE_DEV_URL=http://localhost:2000`
and this Vite dev server together. Plain `bin/instance`/`make start` never set `VITE_DEV_URL`, so they always
serve the built prod bundle — see "Build output" below.

## Architecture

### Entry point & custom element registration
Expand Down Expand Up @@ -78,7 +83,24 @@ Text filter debounce: only triggers fetch when input length > 2 characters. Firs

### Build output

Webpack 5 produces `build/js/smartweb-webcomponents-compiled.js` plus auto-split chunks. Dev server proxies `localhost:2000 → localhost:8080` (Plone). Performance budget: 2 MB assets / 750 KB entrypoint.
Vite (library mode, ES module output) produces `build/js/smartweb-webcomponents-compiled.js` — a thin entry
that imports lazy-loaded chunks (`build/js/chunks/*.js`, one per `@loadable/component`-wrapped widget) —
plus `build/css/smartweb-webcomponents-compiled.css`. These two fixed filenames are the only ones referenced
outside this package (`configure.zcml`, `profiles/default/registry/bundles.xml` in the parent
`imio.smartweb.core` package); chunk filenames are free to change since the browser resolves them itself via
relative ES module imports.

Because Rollup only supports code-splitting for ES module output, the bundle is loaded as
`<script type="module">`, not via Plone's resource registry (which has no way to render a `type` attribute).
Instead, a viewlet in the parent package (`viewlets/webcomponents.py` + `webcomponents_js_header.pt`,
manager `IHtmlHead`) injects the script tag directly:
- **Production** (`VITE_DEV_URL` unset): `<script type="module" src=".../++plone++imio.smartweb.webcomponents/js/smartweb-webcomponents-compiled.js">`.
- **Development** (`VITE_DEV_URL=http://localhost:2000`): injects `@vite/client` + `src/index.jsx` directly
from the Vite dev server (`npm run watch`), giving real React Fast Refresh. The dev server also proxies
every other request to `localhost:8080` (Plone), so the site can be browsed through either origin.

The CSS bundle still goes through Plone's normal resource registry (`csscompilation`), unaffected by the
module/type constraint.

### CSS conventions

Expand Down
2 changes: 1 addition & 1 deletion src/imio/smartweb/core/webcomponents/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ build: ## Build the production bundle
npm run build

.PHONY: watch
watch: ## Start a Webpack dev server and watch for resources changes
watch: ## Start a Vite dev server and watch for resources changes
npm run watch

.PHONY: lint
Expand Down
10 changes: 0 additions & 10 deletions src/imio/smartweb/core/webcomponents/babel.config.json

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading