Oi, Leandro! Como vai? Vamos resolver isso.
Com base no que você explicou o problema aqui é que react e react-dom não estão mapeados no import map, então o navegador interrompe com bare specifier.
Siga esses passos para resolver:
1) index.ejs — mantenha seu arquivo e adicione os mapeamentos e o @empresa/projeto no bloco local:
<!-- dentro do bloco local do import map -->
<script type="injector-importmap">
{
"imports": {
"@home-hub/root-config": "//localhost:9000/home-hub-root-config.js",
"@single-spa/welcome": "https://cdn.jsdelivr.net/npm/single-spa-welcome/dist/single-spa-welcome.min.js",
"react": "https://cdn.jsdelivr.net/npm/react@18.2.0/+esm",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@18.2.0/+esm",
"react-dom/client": "https://cdn.jsdelivr.net/npm/react-dom@18.2.0/client/+esm",
"@empresa/projeto": "//localhost:9001/empresa-projeto.js"
}
}
</script>
Observação: sua CSP já permite https:
e localhost:*
, então estes CDNs funcionam.
2) root-config — registre o microfrontend @empresa/projeto:
// root-config/src/root-config.ts
import { registerApplication, start } from "single-spa";
registerApplication({
name: "@empresa/projeto",
app: () => import("@empresa/projeto"),
activeWhen: (loc) => loc.pathname.startsWith("/")
});
start();
3) @empresa/projeto — exponha os lifecycles (React 18):
// projeto/src/root.component.tsx
import React from "react";
export default function Root() {
return <div style={{ padding: 8 }}>Hello World - MF React</div>;
}
// projeto/src/empresa-projeto.tsx
import React from "react";
import * as ReactDOMClient from "react-dom/client";
import singleSpaReact from "single-spa-react";
import Root from "./root.component";
const lifecycles = singleSpaReact({
React,
ReactDOMClient,
rootComponent: Root,
errorBoundary() {
return React.createElement("div", null, "Erro no microfrontend");
},
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;
4) webpack — ajuste org/project, mantenha externals e ESM:
// projeto/webpack.config.js
const { merge } = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa-react-ts");
module.exports = (webpackConfigEnv, argv) => {
const defaultConfig = singleSpaDefaults({
orgName: "empresa",
projectName: "projeto",
webpackConfigEnv,
argv,
outputSystemJS: false, // ESM nativo com import-map
});
return merge(defaultConfig, {
externals: ["react", "react-dom", "react-dom/client"], // resolvidos via import map
devServer: {
port: 9001,
headers: { "Access-Control-Allow-Origin": "*" },
historyApiFallback: true,
hot: true,
},
output: {
filename: "empresa-projeto.js",
publicPath: "http://localhost:9001/",
},
});
};
5) package.json — confira o name e scripts:
// projeto/package.json (trechos)
{
"name": "@empresa/projeto",
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production"
},
"dependencies": {
"single-spa-react": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Faça a reinstalação de dependências tanto no root como no projeto e agora deve funcionar normalmente.
Checklist rápido do que conferir:
[ ] index.ejs mapeia **react**, **react-dom**, **react-dom/client**
[ ] index.ejs mapeia **@empresa/projeto** para 9001/empresa-projeto.js
[ ] webpack usa **externals** para react* e **publicPath/filename** corretos
[ ] root-config faz **import("@empresa/projeto")**
[ ] portas e CORS conferem (9000/9001, Access-Control-Allow-Origin: *)
Espero ter ajudado. Conte com o apoio do Fórum na sua jornada. Fico à disposição.
Abraços e bons estudos!