From 6d53832f9034b3a780acb5843fcbb7916e507916 Mon Sep 17 00:00:00 2001 From: Nicholas Cunningham <ndcunningham@gmail.com> Date: Fri, 2 Jun 2023 11:44:16 -0600 Subject: [PATCH] fix(nextjs): Update Next.js page generator to support app/ router closes: #17352 --- .../next/src/generators/page/page.spec.ts | 102 +++++++++++++----- packages/next/src/generators/page/page.ts | 23 ++-- 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/packages/next/src/generators/page/page.spec.ts b/packages/next/src/generators/page/page.spec.ts index 9aa9d36013f4e..bfff240c2a3e2 100644 --- a/packages/next/src/generators/page/page.spec.ts +++ b/packages/next/src/generators/page/page.spec.ts @@ -6,47 +6,97 @@ import { Tree } from '@nx/devkit'; describe('component', () => { let tree: Tree; let projectName: string; + let appRouterProjectName; beforeEach(async () => { projectName = 'my-app'; + appRouterProjectName = 'my-app-router'; tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); await applicationGenerator(tree, { name: projectName, style: 'css', + appDir: false, }); - }); - it('should generate component in pages directory', async () => { - await pageGenerator(tree, { - name: 'hello', - project: projectName, + await applicationGenerator(tree, { + name: appRouterProjectName, style: 'css', }); + }); + + describe('page router', () => { + it('should generate component in pages directory', async () => { + await pageGenerator(tree, { + name: 'hello', + project: projectName, + style: 'css', + }); + + expect(tree.exists('apps/my-app/pages/hello/index.tsx')).toBeTruthy(); + expect( + tree.exists('apps/my-app/pages/hello/index.module.css') + ).toBeTruthy(); + }); + + it('should support dynamic routes and directories', async () => { + await pageGenerator(tree, { + name: '[dynamic]', + directory: 'posts', + project: projectName, + style: 'css', + }); - expect(tree.exists('apps/my-app/pages/hello/index.tsx')).toBeTruthy(); - expect( - tree.exists('apps/my-app/pages/hello/index.module.css') - ).toBeTruthy(); + expect( + tree.exists('apps/my-app/pages/posts/[dynamic]/index.tsx') + ).toBeTruthy(); + expect( + tree.exists('apps/my-app/pages/posts/[dynamic]/index.module.css') + ).toBeTruthy(); + + const content = tree + .read('apps/my-app/pages/posts/[dynamic]/index.tsx') + .toString(); + expect(content).toMatch(/DynamicProps/); + }); }); - it('should support dynamic routes and directories', async () => { - await pageGenerator(tree, { - name: '[dynamic]', - directory: 'posts', - project: projectName, - style: 'css', + describe('app router', () => { + it('should generate component in app directory', async () => { + await pageGenerator(tree, { + name: 'about', + project: appRouterProjectName, + style: 'css', + }); + + expect( + tree.exists(`apps/${appRouterProjectName}/app/about/page.tsx`) + ).toBeTruthy(); + expect( + tree.exists(`apps/${appRouterProjectName}/app/about/page.module.css`) + ).toBeTruthy(); }); - expect( - tree.exists('apps/my-app/pages/posts/[dynamic]/index.tsx') - ).toBeTruthy(); - expect( - tree.exists('apps/my-app/pages/posts/[dynamic]/index.module.css') - ).toBeTruthy(); - - const content = tree - .read('apps/my-app/pages/posts/[dynamic]/index.tsx') - .toString(); - expect(content).toMatch(/DynamicProps/); + it('should support dynamic routes and directories', async () => { + await pageGenerator(tree, { + name: '[dynamic]', + project: appRouterProjectName, + directory: 'posts', + style: 'css', + }); + + expect( + tree.exists(`apps/${appRouterProjectName}/app/posts/[dynamic]/page.tsx`) + ).toBeTruthy(); + expect( + tree.exists( + `apps/${appRouterProjectName}/app/posts/[dynamic]/page.module.css` + ) + ).toBeTruthy(); + + const content = tree + .read(`apps/${appRouterProjectName}/app/posts/[dynamic]/page.tsx`) + .toString(); + expect(content).toMatch(/DynamicProps/); + }); }); }); diff --git a/packages/next/src/generators/page/page.ts b/packages/next/src/generators/page/page.ts index 9732a0cfa6a80..04aa8e6cca996 100644 --- a/packages/next/src/generators/page/page.ts +++ b/packages/next/src/generators/page/page.ts @@ -16,25 +16,23 @@ import { Schema } from './schema'; * extra dependencies for css, sass, less, styl style options, and make sure * it is under `pages` folder. */ -export async function pageGenerator(host: Tree, options: Schema) { - const project = readProjectConfiguration(host, options.project); - const directory = options.directory ? `pages/${options.directory}` : 'pages'; +export async function pageGenerator(host: Tree, schema: Schema) { + const options = normalizeOptions(host, schema); const componentTask = await reactComponentGenerator(host, { ...options, - directory, + project: schema.project, pascalCaseFiles: false, export: false, classComponent: false, routing: false, skipTests: !options.withTests, flat: !!options.flat, - fileName: !options.flat ? 'index' : undefined, skipFormat: true, }); const styledTask = addStyleDependencies(host, { style: options.style, - swc: !host.exists(joinPathFragments(project.root, '.babelrc')), + swc: !host.exists(joinPathFragments(options.project.root, '.babelrc')), }); if (!options.skipFormat) { @@ -44,5 +42,18 @@ export async function pageGenerator(host: Tree, options: Schema) { return runTasksInSerial(componentTask, styledTask); } +function normalizeOptions(host: Tree, options: Schema) { + const project = readProjectConfiguration(host, options.project); + + // app/ is a reserved folder in nextjs so it is safe to check it's existence + const isAppRouter = host.exists(`${project.root}/app`); + const routerDirectory = isAppRouter ? 'app' : 'pages'; + const directory = options.directory + ? `${routerDirectory}/${options.directory}` + : `${routerDirectory}`; + const fileName = isAppRouter ? 'page' : !options.flat ? 'index' : undefined; + return { ...options, project, directory, fileName }; +} + export default pageGenerator; export const pageSchematic = convertNxGenerator(pageGenerator);