Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nextjs): Update Next.js page generator to support app/ router #17394

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);