Skip to content

Commit

Permalink
fix(nextjs): Update Next.js page generator to support app/ router
Browse files Browse the repository at this point in the history
closes: #17352
  • Loading branch information
ndcunningham committed Jun 2, 2023
1 parent 8f771e0 commit 6d53832
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 32 deletions.
102 changes: 76 additions & 26 deletions packages/next/src/generators/page/page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/);
});
});
});
23 changes: 17 additions & 6 deletions packages/next/src/generators/page/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);

0 comments on commit 6d53832

Please sign in to comment.