React micro-frontend that runs alongside the Angular host via Webpack Module Federation.
| Phase | Scope | Status |
|---|---|---|
| 1 | Webpack 5 + Module Federation setup | Done |
| 1.5 | Published paragraph (pilot) | Done |
| 2 | Notebook and interpreter modules | Planned |
The published paragraph was picked as pilot because it's read-only and has almost no coupling to other modules.
Angular host (port 4200) React remote (port 3001)
┌─────────────────────────┐ ┌─────────────────────────┐
│ paragraph.component.ts │ │ webpack.config.js │
│ loads remoteEntry.js ──┼──────>│ ModuleFederationPlugin │
│ calls mount(el, props) │ │ name: 'reactApp' │
└─────────────────────────┘ │ exposes: │
│ ./PublishedParagraph │
└─────────────────────────┘
remoteEntry.js from the React dev server or production assets.window.reactApp as a Module Federation container.container.get('./PublishedParagraph') to get the module.mount(element, props), which calls createRoot() and renders into the DOM element.Append ?react=true to any published paragraph URL to activate React mode.
Run npm install then npm run dev to start the dev server on http://localhost:3001.
The Angular host must be running on port 4200. From zeppelin-web-angular/, npm start runs both servers together.
From projects/zeppelin-react/, run npm run build. Output goes to dist/. In production, Angular loads remoteEntry.js from /assets/react/ (see environment.prod.ts).
From projects/zeppelin-react/, run npm run lint to check, npm run lint:fix to auto-fix. See .eslintrc.json for rules.
src/ ├── components/ │ ├── common/ # Empty, Loading │ ├── renderers/ # HTMLRenderer, ImageRenderer, TextRenderer │ └── visualizations/ # TableVisualization, VisualizationControls ├── pages/ │ └── PublishedParagraph.tsx # entry component + mount() ├── templates/ │ └── SingleResultRenderer.tsx # routes result types to renderers ├── utils/ # tableUtils, textUtils, exportFile └── main.ts # re-exports for Module Federation
src/pages/ExampleFeature.tsx).mount(element, props) function that creates a React root and renders the component.webpack.config.js under exposes:exposes: { './PublishedParagraph': './src/pages/PublishedParagraph', './ExampleFeature': './src/pages/ExampleFeature' }
main.ts:export { ExampleFeature, mount as mountExampleFeature } from './pages/ExampleFeature';
paragraph.component.ts):const factory = await container.get('./ExampleFeature'); const { mount } = factory(); mount(hostElement, props);