From 0fb3284d1fc0829c9934547fb939067acbe95d8b Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 20 Sep 2022 12:40:27 +0200 Subject: [PATCH] Use resolved url in flight entry loader (#40697) If there's any resolve alias in webpack config, an aliased resource url will error with `require.resolve` usage in flight manifest plugin, we need to resolve the absolute resource url first then pass down to flight manifest ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` --- .../next-flight-client-entry-loader.ts | 9 ++++- .../webpack/plugins/flight-manifest-plugin.ts | 2 +- test/e2e/app-dir/app-alias.test.ts | 37 +++++++++++++++++++ .../e2e/app-dir/app-alias/app/button/page.tsx | 5 +++ test/e2e/app-dir/app-alias/next.config.js | 8 ++++ test/e2e/app-dir/app-alias/tsconfig.json | 24 ++++++++++++ test/e2e/app-dir/app-alias/ui/button.tsx | 3 ++ 7 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 test/e2e/app-dir/app-alias.test.ts create mode 100644 test/e2e/app-dir/app-alias/app/button/page.tsx create mode 100644 test/e2e/app-dir/app-alias/next.config.js create mode 100644 test/e2e/app-dir/app-alias/tsconfig.json create mode 100644 test/e2e/app-dir/app-alias/ui/button.tsx diff --git a/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts b/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts index 8a24db9e083bb..5ed891fa7a005 100644 --- a/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts +++ b/packages/next/build/webpack/loaders/next-flight-client-entry-loader.ts @@ -41,9 +41,16 @@ export default async function transformSource(this: any): Promise { ` const buildInfo = getModuleBuildInfo(this._module) + const resolve = this.getResolve() + + // Resolve to absolute resource url for flight manifest to collect and use to determine client components + const resolvedRequests = await Promise.all( + requests.map(async (r) => await resolve(this.rootContext, r)) + ) + buildInfo.rsc = { type: RSC_MODULE_TYPES.client, - requests, + requests: resolvedRequests, } return code diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index f4996c20d81cd..9005435a9f0b6 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -112,7 +112,7 @@ export class FlightManifestPlugin { if (mod.resource === '' && mod.buildInfo.rsc) { const { requests = [] } = mod.buildInfo.rsc requests.forEach((r: string) => { - clientRequestsSet.add(require.resolve(r)) + clientRequestsSet.add(r) }) } } diff --git a/test/e2e/app-dir/app-alias.test.ts b/test/e2e/app-dir/app-alias.test.ts new file mode 100644 index 0000000000000..e9634fb6ea0f3 --- /dev/null +++ b/test/e2e/app-dir/app-alias.test.ts @@ -0,0 +1,37 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' +import path from 'path' + +describe('app-dir alias handling', () => { + if ((global as any).isNextDeploy) { + it('should skip next deploy for now', () => {}) + return + } + + if (process.env.NEXT_TEST_REACT_VERSION === '^17') { + it('should skip for react v17', () => {}) + return + } + + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: new FileRef(path.join(__dirname, 'app-alias')), + dependencies: { + react: 'experimental', + 'react-dom': 'experimental', + typescript: 'latest', + '@types/react': 'latest', + '@types/node': 'latest', + }, + }) + }) + afterAll(() => next.destroy()) + + it('should handle typescript paths alias correctly', async () => { + const html = await renderViaHTTP(next.url, '/button') + expect(html).toContain('') + }) +}) diff --git a/test/e2e/app-dir/app-alias/app/button/page.tsx b/test/e2e/app-dir/app-alias/app/button/page.tsx new file mode 100644 index 0000000000000..3625572213c5a --- /dev/null +++ b/test/e2e/app-dir/app-alias/app/button/page.tsx @@ -0,0 +1,5 @@ +import Button from '@/ui/button' + +export default function page() { + return +} diff --git a/test/e2e/app-dir/app-alias/next.config.js b/test/e2e/app-dir/app-alias/next.config.js new file mode 100644 index 0000000000000..b76b309cf1e35 --- /dev/null +++ b/test/e2e/app-dir/app-alias/next.config.js @@ -0,0 +1,8 @@ +module.exports = { + experimental: { + appDir: true, + serverComponents: true, + legacyBrowsers: false, + browsersListForSwc: true, + }, +} diff --git a/test/e2e/app-dir/app-alias/tsconfig.json b/test/e2e/app-dir/app-alias/tsconfig.json new file mode 100644 index 0000000000000..da141694cc0c8 --- /dev/null +++ b/test/e2e/app-dir/app-alias/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES6", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "baseUrl": ".", + "paths": { + "@/ui/*": ["ui/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/test/e2e/app-dir/app-alias/ui/button.tsx b/test/e2e/app-dir/app-alias/ui/button.tsx new file mode 100644 index 0000000000000..39aa4721d6aff --- /dev/null +++ b/test/e2e/app-dir/app-alias/ui/button.tsx @@ -0,0 +1,3 @@ +export default function Button(props: any) { + return