diff --git a/docs/generated/packages/react/generators/library.json b/docs/generated/packages/react/generators/library.json index 35b01e811a9cab..84df21ad32adfa 100644 --- a/docs/generated/packages/react/generators/library.json +++ b/docs/generated/packages/react/generators/library.json @@ -185,6 +185,14 @@ "default": false, "x-priority": "internal" }, + "additionalImportPaths": { + "description": "List of additional import paths for the library.", + "type": "array", + "items": { "type": "string" }, + "default": [], + "hidden": true, + "x-priority": "internal" + }, "minimal": { "description": "Create a React library with a minimal setup, no separate test files.", "type": "boolean", diff --git a/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts b/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts index 850e2d7c6023a4..c6f5f7a5a0580e 100644 --- a/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts +++ b/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts @@ -136,6 +136,7 @@ describe('create-nx-workspace --preset=npm', () => { const tsconfig = readJson(`tsconfig.base.json`); expect(tsconfig.compilerOptions.paths).toEqual({ [libName]: [`packages/${libName}/src/index.ts`], + [`${libName}/server`]: [`packages/${libName}/src/server.ts`], }); }); diff --git a/packages/js/src/utils/typescript/ts-config.ts b/packages/js/src/utils/typescript/ts-config.ts index e52f146a3a210f..e82590c52aa60f 100644 --- a/packages/js/src/utils/typescript/ts-config.ts +++ b/packages/js/src/utils/typescript/ts-config.ts @@ -59,6 +59,9 @@ export function updateRootTsConfig( importPath?: string; projectRoot: string; js?: boolean; + // For Next.js RSC users need to use deep-imports as a workaround for mixed RSC + 'use client' libraries. + // See: https://github.com/nrwl/nx/issues/15830 + additionalImportPaths?: string[]; } ) { if (!options.importPath) { @@ -85,6 +88,17 @@ export function updateRootTsConfig( ), ]; + if (options.additionalImportPaths) { + for (const additionalImportPath of options.additionalImportPaths) { + c.paths[`${options.importPath}/${additionalImportPath}`] = [ + joinPathFragments( + options.projectRoot, + `./src/${additionalImportPath}.${options.js ? 'js' : 'ts'}` + ), + ]; + } + } + return json; }); } diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts index b0ecd6c36b6fd4..04166ca6a88f2f 100644 --- a/packages/next/plugins/with-nx.ts +++ b/packages/next/plugins/with-nx.ts @@ -224,6 +224,13 @@ export function getNextConfig( const userWebpack = nextConfig.webpack || ((x) => x); const { nx, ...validNextConfig } = nextConfig; return { + // Need to skip type checks for now when app layout is enabled until Next.js releases a fix. + // The PR is merged but unreleased. + // PR: https://github.com/vercel/next.js/pull/47534 + typescript: { + ignoreBuildErrors: !!validNextConfig?.experimental?.appDir, + ...(validNextConfig.typescript ?? {}), + }, eslint: { ignoreDuringBuilds: true, ...(validNextConfig.eslint ?? {}), diff --git a/packages/next/src/generators/library/library.spec.ts b/packages/next/src/generators/library/library.spec.ts index 7905b85fa53ead..604ebac9a22704 100644 --- a/packages/next/src/generators/library/library.spec.ts +++ b/packages/next/src/generators/library/library.spec.ts @@ -4,9 +4,11 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; import libraryGenerator from './library'; import { Schema } from './schema'; + // need to mock cypress otherwise it'll use the nx installed version from package.json // which is v9 while we are testing for the new v10 version jest.mock('@nrwl/cypress/src/utils/cypress-version'); + describe('next library', () => { let mockedInstalledCypressVersion: jest.Mock< ReturnType @@ -133,4 +135,31 @@ describe('next library', () => { .jsxImportSource ).toEqual('@emotion/react'); }); + + it('should generate a server-only entry point', async () => { + const appTree = createTreeWithEmptyWorkspace(); + + await libraryGenerator(appTree, { + name: 'myLib', + linter: Linter.EsLint, + skipFormat: false, + skipTsConfig: false, + unitTestRunner: 'jest', + style: 'css', + component: true, + }); + + expect(appTree.read('my-lib/src/index.ts', 'utf-8')).toContain( + 'React client components' + ); + expect(appTree.read('my-lib/src/server.ts', 'utf-8')).toContain( + 'React server components' + ); + expect( + readJson(appTree, 'tsconfig.base.json').compilerOptions.paths + ).toMatchObject({ + '@proj/my-lib': ['my-lib/src/index.ts'], + '@proj/my-lib/server': ['my-lib/src/server.ts'], + }); + }); }); diff --git a/packages/next/src/generators/library/library.ts b/packages/next/src/generators/library/library.ts index 427c9bac4f5cac..065705d5c4212a 100644 --- a/packages/next/src/generators/library/library.ts +++ b/packages/next/src/generators/library/library.ts @@ -33,9 +33,28 @@ export async function libraryGenerator(host: Tree, options: Schema) { ...options, compiler: 'swc', skipFormat: true, + // Always include deep import paths so users can mix server and client react components in the same library. + // See: https://github.com/nrwl/nx/issues/15830 + additionalImportPaths: ['server'], }); tasks.push(libTask); + const indexPath = joinPathFragments( + projectRoot, + 'src', + `index.${options.js ? 'js' : 'ts'}` + ); + const indexContent = host.read(indexPath, 'utf-8'); + host.write( + indexPath, + `// Use this file to export React client components (e.g. those with 'use client' directive) or other non-server utilities\n${indexContent}` + ); + + host.write( + joinPathFragments(projectRoot, 'src', `server.${options.js ? 'js' : 'ts'}`), + `// Use this file to export React server components\n` + ); + updateJson(host, joinPathFragments(projectRoot, '.babelrc'), (json) => { if (options.style === '@emotion/styled') { json.presets = [ diff --git a/packages/next/src/utils/versions.ts b/packages/next/src/utils/versions.ts index 53d0ae41f0bb64..178e529d94fad7 100644 --- a/packages/next/src/utils/versions.ts +++ b/packages/next/src/utils/versions.ts @@ -1,8 +1,8 @@ export const nxVersion = require('../../package.json').version; -export const nextVersion = '13.1.1'; -export const eslintConfigNextVersion = '13.1.1'; -export const sassVersion = '1.55.0'; +export const nextVersion = '13.2.4'; +export const eslintConfigNextVersion = '13.2.4'; +export const sassVersion = '1.60.0'; export const lessLoader = '11.1.0'; export const stylusLoader = '7.1.0'; export const emotionServerVersion = '11.10.0'; diff --git a/packages/react/src/generators/library/lib/create-files.ts b/packages/react/src/generators/library/lib/create-files.ts index be4ce71e1b8c9a..a4cbc128efc612 100644 --- a/packages/react/src/generators/library/lib/create-files.ts +++ b/packages/react/src/generators/library/lib/create-files.ts @@ -47,6 +47,19 @@ export function createFiles(host: Tree, options: NormalizedSchema) { host.delete(`${options.projectRoot}/package.json`); } + if (options.additionalImportPaths) { + for (const additionalImportPath of options.additionalImportPaths) { + host.write( + joinPathFragments( + options.projectRoot, + 'src', + `${additionalImportPath}.ts` + ), + `\n` + ); + } + } + if (options.js) { toJS(host); } diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index e35a3946166310..fd6f5052d308a0 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -771,4 +771,24 @@ describe('lib', () => { }); } ); + + it('should support additional import paths for RSC or testing', async () => { + await libraryGenerator(tree, { + ...defaultSchema, + bundler: 'vite', + unitTestRunner: 'vitest', + name: 'myLib', + additionalImportPaths: ['server'], + }); + + expect(tree.exists(`libs/my-lib/src/server.ts`)).toBeTruthy(); + expect(readJson(tree, 'tsconfig.base.json')).toMatchObject({ + compilerOptions: { + paths: { + '@proj/my-lib': ['libs/my-lib/src/index.ts'], + '@proj/my-lib/server': ['libs/my-lib/src/server.ts'], + }, + }, + }); + }); }); diff --git a/packages/react/src/generators/library/schema.d.ts b/packages/react/src/generators/library/schema.d.ts index 1633b34d17bda9..fd0b884fd496f4 100644 --- a/packages/react/src/generators/library/schema.d.ts +++ b/packages/react/src/generators/library/schema.d.ts @@ -11,6 +11,7 @@ export interface Schema { globalCss?: boolean; importPath?: string; inSourceTests?: boolean; + additionalImportPaths?: string[]; js?: boolean; linter: Linter; name: string; diff --git a/packages/react/src/generators/library/schema.json b/packages/react/src/generators/library/schema.json index fa7e3a82c2e45b..e0592d9de64fe6 100644 --- a/packages/react/src/generators/library/schema.json +++ b/packages/react/src/generators/library/schema.json @@ -188,6 +188,16 @@ "default": false, "x-priority": "internal" }, + "additionalImportPaths": { + "description": "List of additional import paths for the library.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "hidden": true, + "x-priority": "internal" + }, "minimal": { "description": "Create a React library with a minimal setup, no separate test files.", "type": "boolean", diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index 70f25b7e5cc646..38f4c165a344fc 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -53,7 +53,7 @@ export async function configurationGenerator( */ let storybook7 = - storybookMajorVersion() === 7 ?? rawSchema.storybook7Configuration; + storybookMajorVersion() === 7 || rawSchema.storybook7Configuration; if (storybookMajorVersion() === 6 && rawSchema.storybook7Configuration) { logger.error(