Compare commits
7 commits
canary
...
lift-unive
Author | SHA1 | Date | |
---|---|---|---|
|
51f0f6e5c3 | ||
|
1b33835b14 | ||
|
03eba8f996 | ||
|
1fa178b627 | ||
|
127fccb133 | ||
|
849568cc14 | ||
|
001664e3bc |
38 changed files with 1910 additions and 190 deletions
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"dev": "RSPACK_CONFIG_VALIDATE=loose-silent next",
|
||||
"build": "cross-env RSPACK_CONFIG_VALIDATE=loose-silent NEXT_TELEMETRY_DISABLED=1 node --trace-deprecation --enable-source-maps ../../packages/next/dist/bin/next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"dev": "RSPACK_CONFIG_VALIDATE=loose-silent next",
|
||||
"build": "cross-env RSPACK_CONFIG_VALIDATE=loose-silent NEXT_TELEMETRY_DISABLED=1 node --trace-deprecation --enable-source-maps ../../packages/next/dist/bin/next build",
|
||||
"start": "serve out"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
77
examples/federation-single-app/components/SharedNav.tsx
Normal file
77
examples/federation-single-app/components/SharedNav.tsx
Normal file
|
@ -0,0 +1,77 @@
|
|||
import React from "react";
|
||||
import { Menu, Layout } from "antd";
|
||||
import { useRouter } from "next/router";
|
||||
import "./menu";
|
||||
|
||||
const SharedNav = () => {
|
||||
const { asPath, push } = useRouter();
|
||||
let activeMenu;
|
||||
|
||||
if (asPath === "/" || asPath.startsWith("/home")) {
|
||||
activeMenu = "/";
|
||||
} else if (asPath.startsWith("/shop")) {
|
||||
activeMenu = "/shop";
|
||||
} else if (asPath.startsWith("/checkout")) {
|
||||
activeMenu = "/checkout";
|
||||
}
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
className: "home-menu-link",
|
||||
label: (
|
||||
<>
|
||||
Home <sup>3000</sup>
|
||||
</>
|
||||
),
|
||||
key: "/",
|
||||
onMouseEnter: () => {},
|
||||
},
|
||||
{
|
||||
className: "shop-menu-link",
|
||||
label: (
|
||||
<>
|
||||
Shop <sup>3001</sup>
|
||||
</>
|
||||
),
|
||||
key: "/shop",
|
||||
},
|
||||
{
|
||||
className: "checkout-menu-link",
|
||||
label: (
|
||||
<>
|
||||
Checkout <sup>3002</sup>
|
||||
</>
|
||||
),
|
||||
key: "/checkout",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Layout.Header>
|
||||
<div className="header-logo">nextjs-mf</div>
|
||||
<Menu
|
||||
theme="dark"
|
||||
mode="horizontal"
|
||||
selectedKeys={activeMenu ? [activeMenu] : undefined}
|
||||
onClick={({ key }) => {
|
||||
push(key);
|
||||
}}
|
||||
items={menuItems}
|
||||
/>
|
||||
<style jsx>
|
||||
{`
|
||||
.header-logo {
|
||||
float: left;
|
||||
width: 200px;
|
||||
height: 31px;
|
||||
margin-right: 24px;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</Layout.Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default SharedNav;
|
37
examples/federation-single-app/components/menu.tsx
Normal file
37
examples/federation-single-app/components/menu.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import type { ItemType } from "antd/es/menu/interface";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { Menu } from "antd";
|
||||
|
||||
const menuItems: ItemType[] = [
|
||||
{ label: "Main home", key: "/" },
|
||||
{ label: "Test hook from remote", key: "/home/test-remote-hook" },
|
||||
{ label: "Test broken remotes", key: "/home/test-broken-remotes" },
|
||||
{ label: "Exposed pages", key: "/home/exposed-pages" },
|
||||
{
|
||||
label: "Exposed components",
|
||||
type: "group",
|
||||
children: [{ label: "home/SharedNav", key: "/home/test-shared-nav" }],
|
||||
},
|
||||
];
|
||||
|
||||
export default function AppMenu() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{ padding: "10px", fontWeight: 600, backgroundColor: "#fff" }}
|
||||
>
|
||||
Home App Menu
|
||||
</div>
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={[router.asPath]}
|
||||
style={{ height: "100%" }}
|
||||
onClick={({ key }) => router.push(key)}
|
||||
items={menuItems}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.tjhin {
|
||||
display: flex;
|
||||
}
|
5
examples/federation-single-app/next-env.d.ts
vendored
Normal file
5
examples/federation-single-app/next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
13
examples/federation-single-app/next.config.js
Normal file
13
examples/federation-single-app/next.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const nextConfig = {
|
||||
webpack(config, options) {
|
||||
config.plugins.push({
|
||||
name: "xxx",
|
||||
apply(compiler) {
|
||||
compiler.options.devtool = false;
|
||||
},
|
||||
});
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
29
examples/federation-single-app/package.json
Normal file
29
examples/federation-single-app/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "@module-federation/3000-home",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"next": "latest",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"antd": "5.19.1",
|
||||
"@ant-design/cssinjs": "^1.21.0",
|
||||
"buffer": "5.7.1",
|
||||
"encoding": "0.1.13",
|
||||
"eslint-scope": "7.2.2",
|
||||
"events": "3.3.0",
|
||||
"js-cookie": "3.0.5",
|
||||
"lodash": "4.17.21",
|
||||
"node-fetch": "2.7.0",
|
||||
"schema-utils": "3.3.0",
|
||||
"terser-webpack-plugin": "5.3.10",
|
||||
"typescript": "5.3.3",
|
||||
"upath": "2.0.1",
|
||||
"url": "0.11.3",
|
||||
"util": "0.12.5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "RSPACK_CONFIG_VALIDATE=loose-silent next",
|
||||
"build": "cross-env RSPACK_CONFIG_VALIDATE=loose-silent NEXT_TELEMETRY_DISABLED=1 node --trace-deprecation --enable-source-maps ../../packages/next/dist/bin/next build"
|
||||
}
|
||||
}
|
70
examples/federation-single-app/pages/_app.tsx
Normal file
70
examples/federation-single-app/pages/_app.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import * as React from "react";
|
||||
import { useState } from "react";
|
||||
import App from "next/app";
|
||||
import { Layout, version, ConfigProvider } from "antd";
|
||||
import { StyleProvider } from "@ant-design/cssinjs";
|
||||
import Router, { useRouter } from "next/router";
|
||||
|
||||
const SharedNav = React.lazy(() => import("../components/SharedNav"));
|
||||
import HostAppMenu from "../components/menu";
|
||||
function MyApp(props) {
|
||||
const { Component, pageProps } = props;
|
||||
const { asPath } = useRouter();
|
||||
const [MenuComponent, setMenuComponent] = useState(() => HostAppMenu);
|
||||
const handleRouteChange = async (url) => {
|
||||
if (url.startsWith("/shop")) {
|
||||
} else if (url.startsWith("/checkout")) {
|
||||
} else {
|
||||
setMenuComponent(() => HostAppMenu);
|
||||
}
|
||||
};
|
||||
// handle first route hit.
|
||||
React.useEffect(() => {
|
||||
handleRouteChange(asPath);
|
||||
}, [asPath]);
|
||||
|
||||
//handle route change
|
||||
React.useEffect(() => {
|
||||
// Step 3: Subscribe on events
|
||||
Router.events.on("routeChangeStart", handleRouteChange);
|
||||
return () => {
|
||||
Router.events.off("routeChangeStart", handleRouteChange);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<StyleProvider layer>
|
||||
<ConfigProvider theme={{ hashed: false }}>
|
||||
<Layout style={{ minHeight: "100vh" }} prefixCls={"dd"}>
|
||||
<React.Suspense>
|
||||
<SharedNav />
|
||||
</React.Suspense>
|
||||
<Layout>
|
||||
<Layout.Sider width={200}>
|
||||
<MenuComponent />
|
||||
</Layout.Sider>
|
||||
<Layout>
|
||||
<Layout.Content style={{ background: "#fff", padding: 20 }}>
|
||||
<Component {...pageProps} />
|
||||
</Layout.Content>
|
||||
<Layout.Footer
|
||||
style={{
|
||||
background: "#fff",
|
||||
color: "#999",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
antd@{version}
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
|
||||
MyApp.getInitialProps = async (ctx) => {
|
||||
return App.getInitialProps(ctx);
|
||||
};
|
||||
|
||||
export default MyApp;
|
26
examples/federation-single-app/pages/_document.js
Normal file
26
examples/federation-single-app/pages/_document.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import Document, { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
|
||||
return {
|
||||
...initialProps,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument;
|
3
examples/federation-single-app/pages/api/test.js
Normal file
3
examples/federation-single-app/pages/api/test.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function handler(req, res) {
|
||||
res.status(200).json({ name: "John Doe" });
|
||||
}
|
19
examples/federation-single-app/pages/home/exposed-pages.tsx
Normal file
19
examples/federation-single-app/pages/home/exposed-pages.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* eslint-disable */
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function ExposedPages() {
|
||||
const [pageMap] = useState("");
|
||||
const [pageMapV2] = useState("");
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>This app exposes the following pages:</h1>
|
||||
|
||||
<h2>./pages-map</h2>
|
||||
<pre>{JSON.stringify(pageMap, undefined, 2)}</pre>
|
||||
|
||||
<h2>./pages-map-v2</h2>
|
||||
<pre>{JSON.stringify(pageMapV2, undefined, 2)}</pre>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* eslint-disable */
|
||||
import Link from "next/link";
|
||||
|
||||
export default function TestBrokenRemotes() {
|
||||
return (
|
||||
<div>
|
||||
<h2>This page is a test for broken remoteEntries.js</h2>
|
||||
|
||||
<p>
|
||||
Check unresolved host –{" "}
|
||||
<Link href="/unresolved-host">/unresolved-host</Link> (on
|
||||
http://localhost:<b>3333</b>/_next/static/chunks/remoteEntry.js)
|
||||
</p>
|
||||
<p>
|
||||
Check wrong response for remoteEntry –{" "}
|
||||
<Link href="/wrong-entry">/wrong-entry</Link> (on
|
||||
http://localhost:3000/_next/static/chunks/remoteEntry<b>Wrong</b>
|
||||
.js)
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import { NextPage } from "next";
|
||||
|
||||
const TestRemoteHook: NextPage = () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
Page with custom remote hook. You must see text in red box below:
|
||||
</div>
|
||||
<div style={{ border: "1px solid red", padding: 5 }}>
|
||||
blank text for now
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TestRemoteHook;
|
|
@ -0,0 +1,9 @@
|
|||
import SharedNav from "../../components/SharedNav";
|
||||
|
||||
export default function TestSharedNav() {
|
||||
return (
|
||||
<div>
|
||||
<SharedNav />
|
||||
</div>
|
||||
);
|
||||
}
|
139
examples/federation-single-app/pages/index.tsx
Normal file
139
examples/federation-single-app/pages/index.tsx
Normal file
|
@ -0,0 +1,139 @@
|
|||
/* eslint-disable */
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Home</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<h1 style={{ fontSize: "2em" }}>
|
||||
This is SPA combined from 3 different nextjs applications.
|
||||
</h1>
|
||||
<p className="description">
|
||||
They utilize omnidirectional routing and pages or components are able to
|
||||
be federated between applications.
|
||||
</p>
|
||||
<p>You may open any application by clicking on the links below:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="#reloadPage"
|
||||
onClick={() => (window.location.href = "http://localhost:3000")}
|
||||
>
|
||||
localhost:3000
|
||||
</a>
|
||||
{" – "}
|
||||
<b>home</b>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#reloadPage"
|
||||
onClick={() => (window.location.href = "http://localhost:3001")}
|
||||
>
|
||||
localhost:3001
|
||||
</a>
|
||||
{" – "}
|
||||
<b>shop</b>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#reloadPage"
|
||||
onClick={() => (window.location.href = "http://localhost:3002")}
|
||||
>
|
||||
localhost:3002
|
||||
</a>
|
||||
{" – "}
|
||||
<b>checkout</b>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 style={{ marginTop: "30px" }}>Federation test cases</h2>
|
||||
<table border={1} cellPadding={5}>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Test case</td>
|
||||
<td>Expected</td>
|
||||
<td>Actual</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>✅</td>
|
||||
<td>
|
||||
Loading remote component (CheckoutTitle) from localhost:3002
|
||||
<br />
|
||||
<blockquote>
|
||||
lazy(()=>import('checkout/CheckoutTitle'))
|
||||
</blockquote>
|
||||
</td>
|
||||
<td>
|
||||
<h3>This title came from checkout with hooks data!!!</h3>
|
||||
</td>
|
||||
<td>old</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>✅</td>
|
||||
<td>
|
||||
Load federated component from checkout with old antd version
|
||||
</td>
|
||||
<td>[Button from antd@5.18.3]</td>
|
||||
<td>test</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>✅</td>
|
||||
<td>
|
||||
Loading remote component with PNG image from localhost:3001
|
||||
<br />
|
||||
<blockquote>(check publicPath fix in image-loader)</blockquote>
|
||||
</td>
|
||||
<td>
|
||||
<img className="home-webpack-png" src="./webpack.png" />
|
||||
</td>
|
||||
<td>other thing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>✅</td>
|
||||
<td>
|
||||
Loading remote component with SVG from localhost:3001
|
||||
<br />
|
||||
<blockquote>(check publicPath fix in url-loader)</blockquote>
|
||||
</td>
|
||||
<td>
|
||||
<img src="./webpack.svg" />
|
||||
</td>
|
||||
<td>anothaaaaaa</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 style={{ marginTop: "30px" }}>Other problems to fix:</h2>
|
||||
<ul>
|
||||
<li>
|
||||
🐞 Incorrectly exposed modules in next.config.js (e.g. typo in path)
|
||||
do not throw an error in console
|
||||
</li>
|
||||
<li>
|
||||
📝 Try to introduce a remote entry loading according to prefix path.
|
||||
It will be nice runtime improvement if you have eg 20 apps and load
|
||||
just one remoteEntry instead of all of them.
|
||||
</li>
|
||||
<li>
|
||||
📝 It will be nice to regenerate remoteEntry if new page was added in
|
||||
remote app.
|
||||
</li>
|
||||
<li>
|
||||
📝 Remote components do not regenerate chunks if they were changed.
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Home.getInitialProps = () => {
|
||||
console.log("home calls get initial props");
|
||||
return {};
|
||||
};
|
||||
export default Home;
|
0
examples/federation-single-app/public/.gitkeep
Normal file
0
examples/federation-single-app/public/.gitkeep
Normal file
BIN
examples/federation-single-app/public/favicon.ico
Normal file
BIN
examples/federation-single-app/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
examples/federation-single-app/public/webpack.png
Normal file
BIN
examples/federation-single-app/public/webpack.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
5
examples/federation-single-app/public/webpack.svg
Normal file
5
examples/federation-single-app/public/webpack.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg viewBox="0 0 3046.7 875.7" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m387 0 387 218.9v437.9l-387 218.9-387-218.9v-437.9z" fill="#fff"/>
|
||||
<path d="m704.9 641.7-305.1 172.6v-134.4l190.1-104.6zm20.9-18.9v-360.9l-111.6 64.5v232zm-657.9 18.9 305.1 172.6v-134.4l-190.2-104.6zm-20.9-18.9v-360.9l111.6 64.5v232zm13.1-384.3 312.9-177v129.9l-200.5 110.3-1.6.9zm652.6 0-312.9-177v129.9l200.5 110.2 1.6.9z" fill="#8ed6fb"/>
|
||||
<path d="m373 649.3-187.6-103.2v-204.3l187.6 108.3zm26.8 0 187.6-103.1v-204.4l-187.6 108.3zm-201.7-331.1 188.3-103.5 188.3 103.5-188.3 108.7z" fill="#1c78c0"/>
|
||||
</svg>
|
After Width: | Height: | Size: 592 B |
5
examples/federation-single-app/remotes.d.ts
vendored
Normal file
5
examples/federation-single-app/remotes.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare module "shop/useCustomRemoteHook";
|
||||
declare module "shop/WebpackSvg";
|
||||
declare module "shop/WebpackPng";
|
||||
declare module "checkout/CheckoutTitle";
|
||||
declare module "checkout/ButtonOldAnt";
|
20
examples/federation-single-app/tsconfig.json
Normal file
20
examples/federation-single-app/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "environment.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import css from "./hello-world.module.css";
|
||||
|
||||
console.log('css:',css);
|
||||
export default function HelloWorld() {
|
||||
return (
|
||||
<div className={css.hello}>
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
"caniuse-lite": "^1.0.30001579",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"postcss": "8.4.31",
|
||||
"source-map-support": "0.5.21",
|
||||
"styled-jsx": "5.1.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -163,6 +164,7 @@
|
|||
"@next/swc": "15.0.0-canary.56",
|
||||
"@opentelemetry/api": "1.6.0",
|
||||
"@playwright/test": "1.41.2",
|
||||
"@rspack/core": "alpha",
|
||||
"@swc/core": "1.6.6",
|
||||
"@swc/types": "0.1.7",
|
||||
"@taskr/clear": "1.1.0",
|
||||
|
|
|
@ -922,8 +922,9 @@ export default async function getBaseWebpackConfig(
|
|||
context,
|
||||
request,
|
||||
dependencyType,
|
||||
contextInfo.issuerLayer as WebpackLayerName,
|
||||
contextInfo?.issuerLayer as WebpackLayerName,
|
||||
(options) => {
|
||||
return true as any;
|
||||
const resolveFunction = getResolve(options)
|
||||
return (resolveContext: string, requestToResolve: string) =>
|
||||
new Promise((resolve, reject) => {
|
||||
|
@ -1037,7 +1038,6 @@ export default async function getBaseWebpackConfig(
|
|||
}): boolean {
|
||||
return (
|
||||
!module.type?.startsWith('css') &&
|
||||
module.size() > 160000 &&
|
||||
/node_modules[/\\]/.test(module.nameForCondition() || '')
|
||||
)
|
||||
},
|
||||
|
@ -1104,35 +1104,35 @@ export default async function getBaseWebpackConfig(
|
|||
const {
|
||||
TerserPlugin,
|
||||
} = require('./webpack/plugins/terser-webpack-plugin/src/index.js')
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
...terserOptions,
|
||||
compress: {
|
||||
...terserOptions.compress,
|
||||
},
|
||||
mangle: {
|
||||
...terserOptions.mangle,
|
||||
},
|
||||
},
|
||||
}).apply(compiler)
|
||||
// new TerserPlugin({
|
||||
// terserOptions: {
|
||||
// ...terserOptions,
|
||||
// compress: {
|
||||
// ...terserOptions.compress,
|
||||
// },
|
||||
// mangle: {
|
||||
// ...terserOptions.mangle,
|
||||
// },
|
||||
// },
|
||||
// }).apply(compiler)
|
||||
},
|
||||
// Minify CSS
|
||||
(compiler: webpack.Compiler) => {
|
||||
const {
|
||||
CssMinimizerPlugin,
|
||||
} = require('./webpack/plugins/css-minimizer-plugin')
|
||||
new CssMinimizerPlugin({
|
||||
postcssOptions: {
|
||||
map: {
|
||||
// `inline: false` generates the source map in a separate file.
|
||||
// Otherwise, the CSS file is needlessly large.
|
||||
inline: false,
|
||||
// `annotation: false` skips appending the `sourceMappingURL`
|
||||
// to the end of the CSS file. Webpack already handles this.
|
||||
annotation: false,
|
||||
},
|
||||
},
|
||||
}).apply(compiler)
|
||||
// new CssMinimizerPlugin({
|
||||
// postcssOptions: {
|
||||
// map: {
|
||||
// // `inline: false` generates the source map in a separate file.
|
||||
// // Otherwise, the CSS file is needlessly large.
|
||||
// inline: false,
|
||||
// // `annotation: false` skips appending the `sourceMappingURL`
|
||||
// // to the end of the CSS file. Webpack already handles this.
|
||||
// annotation: false,
|
||||
// },
|
||||
// },
|
||||
// }).apply(compiler)
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1740,7 +1740,7 @@ export default async function getBaseWebpackConfig(
|
|||
resource.request,
|
||||
'.shared-runtime'
|
||||
)
|
||||
const layer = resource.contextInfo.issuerLayer
|
||||
const layer = 'pages' as string
|
||||
let runtime
|
||||
|
||||
switch (layer) {
|
||||
|
@ -1757,7 +1757,7 @@ export default async function getBaseWebpackConfig(
|
|||
}
|
||||
),
|
||||
dev && new MemoryWithGcCachePlugin({ maxGenerations: 5 }),
|
||||
dev && isClient && new ReactRefreshWebpackPlugin(webpack),
|
||||
// dev && isClient && new ReactRefreshWebpackPlugin(webpack),
|
||||
// Makes sure `Buffer` and `process` are polyfilled in client and flight bundles (same behavior as webpack 4)
|
||||
(isClient || isEdgeServer) &&
|
||||
new webpack.ProvidePlugin({
|
||||
|
@ -1788,7 +1788,7 @@ export default async function getBaseWebpackConfig(
|
|||
runtimeAsset: `server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`,
|
||||
dev,
|
||||
}),
|
||||
(isClient || isEdgeServer) && new DropClientPage(),
|
||||
// (isClient || isEdgeServer) && new DropClientPage(),
|
||||
isNodeServer &&
|
||||
!dev &&
|
||||
new (require('./webpack/plugins/next-trace-entrypoints-plugin')
|
||||
|
@ -1823,9 +1823,9 @@ export default async function getBaseWebpackConfig(
|
|||
const { NextJsRequireCacheHotReloader } =
|
||||
require('./webpack/plugins/nextjs-require-cache-hot-reloader') as typeof import('./webpack/plugins/nextjs-require-cache-hot-reloader')
|
||||
const devPlugins: any[] = [
|
||||
new NextJsRequireCacheHotReloader({
|
||||
serverComponents: hasAppDir,
|
||||
}),
|
||||
// new NextJsRequireCacheHotReloader({
|
||||
// serverComponents: hasAppDir,
|
||||
// }),
|
||||
]
|
||||
|
||||
if (isClient || isEdgeServer) {
|
||||
|
@ -1876,11 +1876,11 @@ export default async function getBaseWebpackConfig(
|
|||
require('./webpack/plugins/font-stylesheet-gathering-plugin') as {
|
||||
FontStylesheetGatheringPlugin: typeof import('./webpack/plugins/font-stylesheet-gathering-plugin').FontStylesheetGatheringPlugin
|
||||
}
|
||||
return new FontStylesheetGatheringPlugin({
|
||||
adjustFontFallbacks: config.experimental.adjustFontFallbacks,
|
||||
adjustFontFallbacksWithSizeAdjust:
|
||||
config.experimental.adjustFontFallbacksWithSizeAdjust,
|
||||
})
|
||||
// return new FontStylesheetGatheringPlugin({
|
||||
// adjustFontFallbacks: config.experimental.adjustFontFallbacks,
|
||||
// adjustFontFallbacksWithSizeAdjust:
|
||||
// config.experimental.adjustFontFallbacksWithSizeAdjust,
|
||||
// })
|
||||
})(),
|
||||
new WellKnownErrorsPlugin(),
|
||||
isClient &&
|
||||
|
@ -1929,9 +1929,9 @@ export default async function getBaseWebpackConfig(
|
|||
new NextFontManifestPlugin({
|
||||
appDir,
|
||||
}),
|
||||
!dev &&
|
||||
isClient &&
|
||||
new CssChunkingPlugin(config.experimental.cssChunking === 'strict'),
|
||||
// !dev &&
|
||||
// isClient &&
|
||||
// new CssChunkingPlugin(config.experimental.cssChunking === 'strict'),
|
||||
!dev &&
|
||||
isClient &&
|
||||
new (require('./webpack/plugins/telemetry-plugin').TelemetryPlugin)(
|
||||
|
@ -2266,22 +2266,22 @@ export default async function getBaseWebpackConfig(
|
|||
const webpack5Config = webpackConfig as webpack.Configuration
|
||||
|
||||
// disable lazy compilation of entries as next.js has it's own method here
|
||||
if (webpack5Config.experiments?.lazyCompilation === true) {
|
||||
webpack5Config.experiments.lazyCompilation = {
|
||||
entries: false,
|
||||
}
|
||||
} else if (
|
||||
typeof webpack5Config.experiments?.lazyCompilation === 'object' &&
|
||||
webpack5Config.experiments.lazyCompilation.entries !== false
|
||||
) {
|
||||
webpack5Config.experiments.lazyCompilation.entries = false
|
||||
}
|
||||
// if (webpack5Config.experiments?.lazyCompilation === true) {
|
||||
// webpack5Config.experiments.lazyCompilation = {
|
||||
// entries: false,
|
||||
// }
|
||||
// } else if (
|
||||
// typeof webpack5Config.experiments?.lazyCompilation === 'object' &&
|
||||
// webpack5Config.experiments.lazyCompilation.entries !== false
|
||||
// ) {
|
||||
// webpack5Config.experiments.lazyCompilation.entries = false
|
||||
// }
|
||||
|
||||
if (typeof (webpackConfig as any).then === 'function') {
|
||||
console.warn(
|
||||
'> Promise returned in next config. https://nextjs.org/docs/messages/promise-in-next-config'
|
||||
)
|
||||
}
|
||||
// if (typeof (webpackConfig as any).then === 'function') {
|
||||
// console.warn(
|
||||
// '> Promise returned in next config. https://nextjs.org/docs/messages/promise-in-next-config'
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
const rules = webpackConfig.module?.rules || []
|
||||
|
|
|
@ -591,7 +591,7 @@ export const css = curry(async function css(
|
|||
if (ctx.isClient && (ctx.isProduction || ctx.hasAppDir)) {
|
||||
// Extract CSS as CSS file(s) in the client-side production bundle.
|
||||
const MiniCssExtractPlugin =
|
||||
require('../../../plugins/mini-css-extract-plugin').default
|
||||
require('@rspack/core').CssExtractRspackPlugin;
|
||||
fns.push(
|
||||
plugin(
|
||||
// @ts-ignore webpack 5 compat
|
||||
|
|
|
@ -33,7 +33,7 @@ export function isCSSMod(mod: {
|
|||
mod.loaders?.some(
|
||||
({ loader }) =>
|
||||
loader.includes('next-style-loader/index.js') ||
|
||||
loader.includes('mini-css-extract-plugin/loader.js') ||
|
||||
loader.includes('rspack.CssExtractRspackPlugin.loader') ||
|
||||
loader.includes('@vanilla-extract/webpack-plugin/loader/')
|
||||
)
|
||||
)
|
||||
|
|
|
@ -568,7 +568,7 @@ function getExtractMetadata(params: {
|
|||
metadataByEntry.clear()
|
||||
const telemetry: Telemetry | undefined = traceGlobals.get('telemetry')
|
||||
|
||||
for (const [entryName, entry] of compilation.entries) {
|
||||
for (const [entryName, entry] of compilation.entries ?? []) {
|
||||
if (entry.options.runtime !== EDGE_RUNTIME_WEBPACK) {
|
||||
// Only process edge runtime entries
|
||||
continue
|
||||
|
@ -782,9 +782,9 @@ export default class MiddlewarePlugin {
|
|||
compiler,
|
||||
compilation,
|
||||
})
|
||||
hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
|
||||
hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
|
||||
hooks.parser.for('javascript/esm').tap(NAME, codeAnalyzer)
|
||||
// hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
|
||||
// hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
|
||||
// hooks.parser.for('javascript/esm').tap(NAME, codeAnalyzer)
|
||||
|
||||
/**
|
||||
* Extract all metadata for the entry points in a Map object.
|
||||
|
|
|
@ -677,6 +677,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
|
|||
}
|
||||
|
||||
apply(compiler: webpack.Compiler) {
|
||||
return;
|
||||
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
||||
const readlink = async (path: string): Promise<string | null> => {
|
||||
try {
|
||||
|
|
|
@ -63,7 +63,7 @@ export class ProfilingPlugin {
|
|||
} = {}
|
||||
) {
|
||||
let span: Span | undefined
|
||||
startHook.tap(
|
||||
startHook?.tap(
|
||||
{ name: pluginName, stage: -Infinity },
|
||||
(...params: any[]) => {
|
||||
const name = typeof spanName === 'function' ? spanName() : spanName
|
||||
|
@ -75,7 +75,7 @@ export class ProfilingPlugin {
|
|||
if (onStart) onStart(span, ...params)
|
||||
}
|
||||
)
|
||||
stopHook.tap({ name: pluginName, stage: Infinity }, (...params: any[]) => {
|
||||
stopHook?.tap({ name: pluginName, stage: Infinity }, (...params: any[]) => {
|
||||
// `stopHook` may be triggered when `startHook` has not in cases
|
||||
// where `stopHook` is used as the terminating event for more
|
||||
// than one pair of hooks.
|
||||
|
@ -149,54 +149,54 @@ export class ProfilingPlugin {
|
|||
compiler.hooks.compilation.tap(
|
||||
{ name: pluginName, stage: -Infinity },
|
||||
(compilation: any) => {
|
||||
compilation.hooks.buildModule.tap(pluginName, (module: any) => {
|
||||
const moduleType = (() => {
|
||||
const r = module.userRequest
|
||||
if (!r || r.endsWith('!')) {
|
||||
return ''
|
||||
} else {
|
||||
const resource = r.split('!').pop()
|
||||
const match = /^[^?]+\.([^?]+)$/.exec(resource)
|
||||
return match ? match[1] : ''
|
||||
}
|
||||
})()
|
||||
// compilation.hooks.buildModule.tap(pluginName, (module: any) => {
|
||||
// const moduleType = (() => {
|
||||
// const r = module.userRequest
|
||||
// if (!r || r.endsWith('!')) {
|
||||
// return ''
|
||||
// } else {
|
||||
// const resource = r.split('!').pop()
|
||||
// const match = /^[^?]+\.([^?]+)$/.exec(resource)
|
||||
// return match ? match[1] : ''
|
||||
// }
|
||||
// })()
|
||||
|
||||
const issuerModule = compilation?.moduleGraph?.getIssuer(module)
|
||||
// const issuerModule = compilation?.moduleGraph?.getIssuer(module)
|
||||
|
||||
let span: Span
|
||||
// let span: Span
|
||||
|
||||
const moduleSpans = moduleSpansByCompilation.get(compilation)
|
||||
const spanName = `build-module${moduleType ? `-${moduleType}` : ''}`
|
||||
const issuerSpan: Span | undefined =
|
||||
issuerModule && moduleSpans?.get(issuerModule)
|
||||
if (issuerSpan) {
|
||||
span = issuerSpan.traceChild(spanName)
|
||||
} else {
|
||||
let parentSpan: Span | undefined
|
||||
for (const incomingConnection of compilation.moduleGraph.getIncomingConnections(
|
||||
module
|
||||
)) {
|
||||
const entrySpan = spans.get(incomingConnection.dependency)
|
||||
if (entrySpan) {
|
||||
parentSpan = entrySpan
|
||||
break
|
||||
}
|
||||
}
|
||||
// const moduleSpans = moduleSpansByCompilation.get(compilation)
|
||||
// const spanName = `build-module${moduleType ? `-${moduleType}` : ''}`
|
||||
// const issuerSpan: Span | undefined =
|
||||
// issuerModule && moduleSpans?.get(issuerModule)
|
||||
// if (issuerSpan) {
|
||||
// span = issuerSpan.traceChild(spanName)
|
||||
// } else {
|
||||
// let parentSpan: Span | undefined
|
||||
// for (const incomingConnection of compilation.moduleGraph?.getIncomingConnections(
|
||||
// module
|
||||
// )) {
|
||||
// const entrySpan = spans.get(incomingConnection.dependency)
|
||||
// if (entrySpan) {
|
||||
// parentSpan = entrySpan
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!parentSpan) {
|
||||
const compilationSpan = spans.get(compilation)
|
||||
if (!compilationSpan) {
|
||||
return
|
||||
}
|
||||
// if (!parentSpan) {
|
||||
// const compilationSpan = spans.get(compilation)
|
||||
// if (!compilationSpan) {
|
||||
// return
|
||||
// }
|
||||
|
||||
parentSpan = compilationSpan
|
||||
}
|
||||
span = parentSpan.traceChild(spanName)
|
||||
}
|
||||
span.setAttribute('name', module.userRequest)
|
||||
span.setAttribute('layer', module.layer)
|
||||
moduleSpans!.set(module, span)
|
||||
})
|
||||
// parentSpan = compilationSpan
|
||||
// }
|
||||
// span = parentSpan.traceChild(spanName)
|
||||
// }
|
||||
// span.setAttribute('name', module.userRequest)
|
||||
// span.setAttribute('layer', module.layer)
|
||||
// moduleSpans!.set(module, span)
|
||||
// })
|
||||
|
||||
const moduleHooks = NormalModule.getCompilationHooks(compilation)
|
||||
moduleHooks.readResource.for(undefined).intercept({
|
||||
|
@ -214,17 +214,41 @@ export class ProfilingPlugin {
|
|||
moduleHooks.loader.tap(
|
||||
pluginName,
|
||||
(loaderContext: any, module: any) => {
|
||||
const moduleSpan = moduleSpansByCompilation
|
||||
.get(compilation)
|
||||
?.get(module)
|
||||
// const moduleSpan = moduleSpansByCompilation
|
||||
// .get(compilation)
|
||||
// ?.get(module)
|
||||
const moduleSpan = {
|
||||
traceChild() {
|
||||
return {
|
||||
traceFn(fn: any) {
|
||||
return fn();
|
||||
},
|
||||
traceAsyncFn(fn:any){
|
||||
return fn();
|
||||
},
|
||||
setAttribute() {},
|
||||
traceChild(){
|
||||
return {
|
||||
traceFn(fn: any) {
|
||||
return fn();
|
||||
},
|
||||
traceAsyncFn(fn:any){
|
||||
return fn();
|
||||
},
|
||||
setAttribute() {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
loaderContext.currentTraceSpan = moduleSpan
|
||||
}
|
||||
)
|
||||
|
||||
compilation.hooks.succeedModule.tap(pluginName, (module: any) => {
|
||||
compilation.hooks.succeedModule?.tap(pluginName, (module: any) => {
|
||||
moduleSpansByCompilation?.get(compilation)?.get(module)?.stop()
|
||||
})
|
||||
compilation.hooks.failedModule.tap(pluginName, (module: any) => {
|
||||
compilation.hooks.failedModule?.tap(pluginName, (module: any) => {
|
||||
moduleSpansByCompilation?.get(compilation)?.get(module)?.stop()
|
||||
})
|
||||
|
||||
|
@ -243,7 +267,7 @@ export class ProfilingPlugin {
|
|||
}
|
||||
)
|
||||
|
||||
compilation.hooks.addEntry.tap(pluginName, (entry: any) => {
|
||||
compilation.hooks.addEntry?.tap(pluginName, (entry: any) => {
|
||||
const parentSpan =
|
||||
makeSpanByCompilation.get(compilation) || spans.get(compilation)
|
||||
if (!parentSpan) {
|
||||
|
@ -254,11 +278,11 @@ export class ProfilingPlugin {
|
|||
spans.set(entry, addEntrySpan)
|
||||
})
|
||||
|
||||
compilation.hooks.succeedEntry.tap(pluginName, (entry: any) => {
|
||||
compilation.hooks.succeedEntry?.tap(pluginName, (entry: any) => {
|
||||
spans.get(entry)?.stop()
|
||||
spans.delete(entry)
|
||||
})
|
||||
compilation.hooks.failedEntry.tap(pluginName, (entry: any) => {
|
||||
compilation.hooks.failedEntry?.tap(pluginName, (entry: any) => {
|
||||
spans.get(entry)?.stop()
|
||||
spans.delete(entry)
|
||||
})
|
||||
|
@ -355,31 +379,31 @@ export class ProfilingPlugin {
|
|||
)
|
||||
|
||||
const logs = new Map()
|
||||
const originalTime = compilation.logger.time
|
||||
const originalTimeEnd = compilation.logger.timeEnd
|
||||
const originalTime = compilation.logger?.time
|
||||
const originalTimeEnd = compilation.logger?.timeEnd
|
||||
|
||||
compilation.logger.time = (label: string) => {
|
||||
if (!inTraceLabelsSeal(label)) {
|
||||
return originalTime.call(compilation.logger, label)
|
||||
}
|
||||
const span = sealSpanByCompilation.get(compilation)
|
||||
if (span) {
|
||||
logs.set(label, span.traceChild(label.replace(/ /g, '-')))
|
||||
}
|
||||
return originalTime.call(compilation.logger, label)
|
||||
}
|
||||
compilation.logger.timeEnd = (label: string) => {
|
||||
if (!inTraceLabelsSeal(label)) {
|
||||
return originalTimeEnd.call(compilation.logger, label)
|
||||
}
|
||||
// compilation.logger.time = (label: string) => {
|
||||
// if (!inTraceLabelsSeal(label)) {
|
||||
// return originalTime.call(compilation.logger, label)
|
||||
// }
|
||||
// const span = sealSpanByCompilation.get(compilation)
|
||||
// if (span) {
|
||||
// logs.set(label, span.traceChild(label.replace(/ /g, '-')))
|
||||
// }
|
||||
// return originalTime.call(compilation.logger, label)
|
||||
// }
|
||||
// compilation.logger.timeEnd = (label: string) => {
|
||||
// if (!inTraceLabelsSeal(label)) {
|
||||
// return originalTimeEnd.call(compilation.logger, label)
|
||||
// }
|
||||
|
||||
const span = logs.get(label)
|
||||
if (span) {
|
||||
span.stop()
|
||||
logs.delete(label)
|
||||
}
|
||||
return originalTimeEnd.call(compilation.logger, label)
|
||||
}
|
||||
// const span = logs.get(label)
|
||||
// if (span) {
|
||||
// span.stop()
|
||||
// logs.delete(label)
|
||||
// }
|
||||
// return originalTimeEnd.call(compilation.logger, label)
|
||||
// }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -134,9 +134,9 @@ function buildManifest(
|
|||
}
|
||||
}
|
||||
}
|
||||
for (const module of compilation.modules) {
|
||||
module.blocks.forEach(handleBlock)
|
||||
}
|
||||
// for (const module of compilation.modules) {
|
||||
// module.blocks.forEach(handleBlock)
|
||||
// }
|
||||
|
||||
manifest = Object.keys(manifest)
|
||||
.sort()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env node
|
||||
require('source-map-support').install();
|
||||
|
||||
import '../server/lib/cpu-profile'
|
||||
import { existsSync } from 'fs'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env node
|
||||
require('source-map-support').install();
|
||||
|
||||
import '../server/lib/cpu-profile'
|
||||
import type { StartServerOptions } from '../server/lib/start-server'
|
||||
|
|
|
@ -21,6 +21,7 @@ exports.init = function () {
|
|||
webpack: require('webpack'),
|
||||
})
|
||||
} else {
|
||||
Object.assign(exports, require('./bundle5')())
|
||||
console.log('load rspack');
|
||||
Object.assign(exports, require('@rspack/core'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -791,7 +791,7 @@ export default class NextNodeServer extends BaseServer<
|
|||
}
|
||||
|
||||
protected getFontManifest(): FontManifest {
|
||||
return requireFontManifest(this.distDir)
|
||||
return requireFontManifest(this.distDir) as any;
|
||||
}
|
||||
|
||||
protected getNextFontManifest(): NextFontManifest | undefined {
|
||||
|
|
|
@ -133,6 +133,7 @@ export async function requirePage(
|
|||
|
||||
export function requireFontManifest(distDir: string) {
|
||||
const serverBuildPath = path.join(distDir, SERVER_DIRECTORY)
|
||||
return {} as FontManifest;
|
||||
const fontManifest = loadManifest(
|
||||
path.join(serverBuildPath, AUTOMATIC_FONT_OPTIMIZATION_MANIFEST)
|
||||
) as FontManifest
|
||||
|
|
1277
pnpm-lock.yaml
1277
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -2,3 +2,5 @@ packages:
|
|||
- 'packages/*'
|
||||
- 'bench/*'
|
||||
- 'packages/next-swc/crates/next-core/js'
|
||||
- 'examples/next-css'
|
||||
- 'examples/federation-single-app'
|
||||
|
|
Loading…
Reference in a new issue