feat(nextjs): update Next.js to 13.4.1 and default to App Router for new apps (#16948)
This commit is contained in:
parent
0aa3e45e2e
commit
95421c6945
@ -113,7 +113,7 @@ Workspace name (e.g. org name)
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Add Experimental app/ layout for next.js
|
||||
Enable the App Router for Next.js
|
||||
|
||||
### nxCloud
|
||||
|
||||
|
||||
@ -118,9 +118,9 @@
|
||||
},
|
||||
"appDir": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable experimental app directory for the project",
|
||||
"x-prompt": "Do you want to use experimental app/ in this project?"
|
||||
"default": true,
|
||||
"description": "Enable the App Router for this project.",
|
||||
"x-prompt": "Would you like to use the App Router (recommended)?"
|
||||
},
|
||||
"rootProject": {
|
||||
"description": "Create an application at the root of the workspace.",
|
||||
|
||||
@ -113,7 +113,7 @@ Workspace name (e.g. org name)
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Add Experimental app/ layout for next.js
|
||||
Enable the App Router for Next.js
|
||||
|
||||
### nxCloud
|
||||
|
||||
|
||||
@ -70,9 +70,9 @@
|
||||
"enum": ["express", "koa", "fastify", "nest", "none"]
|
||||
},
|
||||
"nextAppDir": {
|
||||
"description": "Enable experimental app directory for the project",
|
||||
"description": "Enable the App Router for this project.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
"default": true
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
|
||||
@ -82,9 +82,9 @@
|
||||
"default": false
|
||||
},
|
||||
"nextAppDir": {
|
||||
"description": "Enable experimental app/ for the project",
|
||||
"description": "Enable the App Router for this project.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
"default": true
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
|
||||
56
e2e/next/src/next-appdir.test.ts
Normal file
56
e2e/next/src/next-appdir.test.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import {
|
||||
cleanupProject,
|
||||
isNotWindows,
|
||||
killPorts,
|
||||
newProject,
|
||||
runCLI,
|
||||
runCommandUntil,
|
||||
tmpProjPath,
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import { getData } from 'ajv/dist/compile/validate';
|
||||
import { detectPackageManager } from 'nx/src/utils/package-manager';
|
||||
import { checkApp } from './utils';
|
||||
import { p } from 'vitest/dist/types-b7007192';
|
||||
|
||||
describe('Next.js App Router', () => {
|
||||
let proj: string;
|
||||
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
it('should be able to generate and build app with default App Router', async () => {
|
||||
const appName = uniq('app');
|
||||
const jsLib = uniq('tslib');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName}`);
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
||||
|
||||
updateFile(
|
||||
`apps/${appName}/app/page.tsx`,
|
||||
`
|
||||
import React from 'react';
|
||||
import { ${jsLib} } from '@${proj}/${jsLib}';
|
||||
|
||||
export default async function Page() {
|
||||
return (
|
||||
<p>{${jsLib}()}</p>
|
||||
);
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
await checkApp(appName, {
|
||||
checkUnitTest: false,
|
||||
checkLint: true,
|
||||
checkE2E: false,
|
||||
checkExport: false,
|
||||
});
|
||||
}, 300_000);
|
||||
});
|
||||
@ -65,7 +65,7 @@ describe('NextJs Component Testing', () => {
|
||||
});
|
||||
|
||||
function createAppWithCt(appName: string) {
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive`);
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --appDir=false`);
|
||||
runCLI(
|
||||
`generate @nx/next:component button --project=${appName} --directory=components --flat --no-interactive`
|
||||
);
|
||||
|
||||
@ -21,7 +21,9 @@ describe('Next.js apps', () => {
|
||||
it('should support different --style options', async () => {
|
||||
const lessApp = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${lessApp} --no-interactive --style=less`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${lessApp} --no-interactive --style=less --appDir=false`
|
||||
);
|
||||
|
||||
await checkApp(lessApp, {
|
||||
checkUnitTest: false,
|
||||
@ -32,7 +34,9 @@ describe('Next.js apps', () => {
|
||||
|
||||
const stylusApp = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${stylusApp} --no-interactive --style=styl`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${stylusApp} --no-interactive --style=styl --appDir=false`
|
||||
);
|
||||
|
||||
await checkApp(stylusApp, {
|
||||
checkUnitTest: false,
|
||||
@ -44,7 +48,7 @@ describe('Next.js apps', () => {
|
||||
const scApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${scApp} --no-interactive --style=styled-components`
|
||||
`generate @nx/next:app ${scApp} --no-interactive --style=styled-components --appDir=false`
|
||||
);
|
||||
|
||||
await checkApp(scApp, {
|
||||
@ -57,7 +61,7 @@ describe('Next.js apps', () => {
|
||||
const emotionApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${emotionApp} --no-interactive --style=@emotion/styled`
|
||||
`generate @nx/next:app ${emotionApp} --no-interactive --style=@emotion/styled --appDir=false`
|
||||
);
|
||||
|
||||
await checkApp(emotionApp, {
|
||||
|
||||
@ -52,7 +52,9 @@ describe('Next.js Applications', () => {
|
||||
const jsLib = uniq('tslib');
|
||||
const buildableLib = uniq('buildablelib');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
|
||||
);
|
||||
runCLI(`generate @nx/next:lib ${nextLib} --no-interactive`);
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
||||
runCLI(
|
||||
@ -231,7 +233,7 @@ describe('Next.js Applications', () => {
|
||||
|
||||
const port = 4200;
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName}`);
|
||||
runCLI(`generate @nx/next:app ${appName} --appDir=false`);
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
||||
|
||||
const proxyConf = {
|
||||
@ -297,7 +299,9 @@ describe('Next.js Applications', () => {
|
||||
it('should support custom next.config.js and output it in dist', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`apps/${appName}/next.config.js`,
|
||||
@ -354,7 +358,9 @@ describe('Next.js Applications', () => {
|
||||
it('should support --js flag', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --js`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --js --appDir=false`
|
||||
);
|
||||
|
||||
checkFilesExist(`apps/${appName}/pages/index.js`);
|
||||
|
||||
|
||||
@ -18,14 +18,6 @@ export async function checkApp(
|
||||
}
|
||||
) {
|
||||
const appsDir = opts.appsDir ?? 'apps';
|
||||
const buildResult = runCLI(`build ${appName}`);
|
||||
expect(buildResult).toContain(`Compiled successfully`);
|
||||
checkFilesExist(`dist/${appsDir}/${appName}/.next/build-manifest.json`);
|
||||
|
||||
const packageJson = readJson(`dist/${appsDir}/${appName}/package.json`);
|
||||
expect(packageJson.dependencies.react).toBeDefined();
|
||||
expect(packageJson.dependencies['react-dom']).toBeDefined();
|
||||
expect(packageJson.dependencies.next).toBeDefined();
|
||||
|
||||
if (opts.checkLint) {
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
@ -39,8 +31,19 @@ export async function checkApp(
|
||||
);
|
||||
}
|
||||
|
||||
const buildResult = runCLI(`build ${appName}`);
|
||||
expect(buildResult).toContain(`Successfully ran target build`);
|
||||
checkFilesExist(`dist/${appsDir}/${appName}/.next/build-manifest.json`);
|
||||
|
||||
const packageJson = readJson(`dist/${appsDir}/${appName}/package.json`);
|
||||
expect(packageJson.dependencies.react).toBeDefined();
|
||||
expect(packageJson.dependencies['react-dom']).toBeDefined();
|
||||
expect(packageJson.dependencies.next).toBeDefined();
|
||||
|
||||
if (opts.checkE2E && runCypressTests()) {
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`);
|
||||
const e2eResults = runCLI(
|
||||
`e2e ${appName}-e2e --no-watch --configuration=production`
|
||||
);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
expect(await killPorts()).toBeTruthy();
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('nextAppDir', {
|
||||
describe: chalk.dim`Add Experimental app/ layout for next.js`,
|
||||
describe: chalk.dim`Enable the App Router for Next.js`,
|
||||
type: 'boolean',
|
||||
}),
|
||||
withNxCloud,
|
||||
@ -638,8 +638,8 @@ async function isNextAppDir(parsedArgs: yargs.Arguments<Arguments>) {
|
||||
.prompt<{ appDir: 'Yes' | 'No' }>([
|
||||
{
|
||||
name: 'appDir',
|
||||
message: 'Do you want to use experimental app/ in this project?',
|
||||
type: 'autocomplete',
|
||||
message: 'Would you like to use the App Router (recommended)?',
|
||||
type: 'autocomplete' as const,
|
||||
choices: [
|
||||
{
|
||||
name: 'No',
|
||||
@ -648,7 +648,7 @@ async function isNextAppDir(parsedArgs: yargs.Arguments<Arguments>) {
|
||||
name: 'Yes',
|
||||
},
|
||||
],
|
||||
initial: 'No' as any,
|
||||
initial: 'Yes' as any,
|
||||
},
|
||||
])
|
||||
.then((choice) => choice.appDir === 'Yes');
|
||||
|
||||
@ -316,6 +316,22 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.2.0": {
|
||||
"version": "16.2.0-beta.0",
|
||||
"requires": {
|
||||
"next": ">=13.0.0"
|
||||
},
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "13.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "13.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,14 +6,6 @@ import { WithNxOptions } from './with-nx';
|
||||
const addLessToRegExp = (rx) =>
|
||||
new RegExp(rx.source.replace('|sass', '|sass|less'), rx.flags);
|
||||
|
||||
function patchNextCSSWithLess(
|
||||
nextCSSModule: any = require('next/dist/build/webpack/config/blocks/css')
|
||||
) {
|
||||
nextCSSModule.regexLikeCss = addLessToRegExp(nextCSSModule.regexLikeCss);
|
||||
}
|
||||
|
||||
patchNextCSSWithLess();
|
||||
|
||||
export function withLess(
|
||||
configOrFn: NextConfigFn | WithNxOptions
|
||||
): NextConfigFn {
|
||||
@ -108,4 +100,3 @@ export function withLess(
|
||||
|
||||
module.exports = withLess;
|
||||
module.exports.withLess = withLess;
|
||||
module.exports.patchNext = patchNextCSSWithLess;
|
||||
|
||||
@ -6,14 +6,6 @@ import { WithNxOptions } from './with-nx';
|
||||
const addStylusToRegExp = (rx) =>
|
||||
new RegExp(rx.source.replace('|sass', '|sass|styl'), rx.flags);
|
||||
|
||||
function patchNextCSSWithStylus(
|
||||
nextCSSModule = require('next/dist/build/webpack/config/blocks/css') as any
|
||||
) {
|
||||
nextCSSModule.regexLikeCss = addStylusToRegExp(nextCSSModule.regexLikeCss);
|
||||
}
|
||||
|
||||
patchNextCSSWithStylus();
|
||||
|
||||
export function withStylus(
|
||||
configOrFn: WithNxOptions | NextConfigFn
|
||||
): NextConfigFn {
|
||||
@ -107,4 +99,3 @@ export function withStylus(
|
||||
|
||||
module.exports = withStylus;
|
||||
module.exports.withStylus = withStylus;
|
||||
module.exports.patchNext = patchNextCSSWithStylus;
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`app --style styled-jsx should use <style jsx> in index page 1`] = `
|
||||
"export function Index() {
|
||||
"'use client';
|
||||
|
||||
export default async function Index() {
|
||||
/*
|
||||
* Replace the elements below with your own.
|
||||
*
|
||||
@ -420,7 +422,5 @@ exports[`app --style styled-jsx should use <style jsx> in index page 1`] = `
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
||||
"
|
||||
`;
|
||||
|
||||
@ -50,12 +50,23 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate files', async () => {
|
||||
it('should generate files for app router layout', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'myApp',
|
||||
style: 'css',
|
||||
});
|
||||
expect(tree.exists('apps/my-app/tsconfig.json')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.module.css')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should generate files for pages layout', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'myApp',
|
||||
style: 'css',
|
||||
appDir: false,
|
||||
});
|
||||
expect(tree.exists('apps/my-app/tsconfig.json')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/pages/index.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/specs/index.spec.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/pages/index.module.css')).toBeTruthy();
|
||||
@ -91,13 +102,11 @@ describe('app', () => {
|
||||
style: 'scss',
|
||||
});
|
||||
|
||||
expect(tree.exists('apps/my-app/pages/index.module.scss')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.module.scss')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/global.css')).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(
|
||||
`import styles from './index.module.scss'`
|
||||
);
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(`import styles from './page.module.scss'`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -108,13 +117,11 @@ describe('app', () => {
|
||||
style: 'less',
|
||||
});
|
||||
|
||||
expect(tree.exists('apps/my-app/pages/index.module.less')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.less')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.module.less')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/global.less')).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(
|
||||
`import styles from './index.module.less'`
|
||||
);
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(`import styles from './page.module.less'`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -125,13 +132,11 @@ describe('app', () => {
|
||||
style: 'styl',
|
||||
});
|
||||
|
||||
expect(tree.exists('apps/my-app/pages/index.module.styl')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.styl')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.module.styl')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/global.styl')).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(
|
||||
`import styles from './index.module.styl'`
|
||||
);
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
expect(indexContent).toContain(`import styles from './page.module.styl'`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -143,12 +148,12 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
tree.exists('apps/my-app/pages/index.module.styled-components')
|
||||
tree.exists('apps/my-app/app/page.module.styled-components')
|
||||
).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/global.css')).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
expect(indexContent).not.toContain(`import styles from './index.module`);
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||
expect(indexContent).toContain(`import styled from 'styled-components'`);
|
||||
});
|
||||
});
|
||||
@ -161,12 +166,12 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
tree.exists('apps/my-app/pages/index.module.styled-components')
|
||||
tree.exists('apps/my-app/app/page.module.styled-components')
|
||||
).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/global.css')).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
expect(indexContent).not.toContain(`import styles from './index.module`);
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||
expect(indexContent).toContain(`import styled from '@emotion/styled'`);
|
||||
});
|
||||
|
||||
@ -191,15 +196,13 @@ describe('app', () => {
|
||||
style: 'styled-jsx',
|
||||
});
|
||||
|
||||
const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
const indexContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
|
||||
expect(indexContent).toMatchSnapshot();
|
||||
expect(
|
||||
tree.exists('apps/my-app/pages/index.module.styled-jsx')
|
||||
).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/pages/styles.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.module.styled-jsx')).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/app/global.css')).toBeTruthy();
|
||||
|
||||
expect(indexContent).not.toContain(`import styles from './index.module`);
|
||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||
expect(indexContent).not.toContain(
|
||||
`import styled from 'styled-components'`
|
||||
);
|
||||
@ -311,7 +314,7 @@ describe('app', () => {
|
||||
style: 'css',
|
||||
});
|
||||
|
||||
const appContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8');
|
||||
const appContent = tree.read('apps/my-app/app/page.tsx', 'utf-8');
|
||||
|
||||
expect(appContent).not.toMatch(/extends Component/);
|
||||
});
|
||||
@ -419,7 +422,7 @@ describe('app', () => {
|
||||
js: true,
|
||||
});
|
||||
|
||||
expect(tree.exists('apps/my-app/pages/index.js')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/app/page.js')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/specs/index.spec.js')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/index.d.js')).toBeFalsy();
|
||||
expect(tree.exists('apps/my-app/index.d.ts')).toBeFalsy();
|
||||
@ -450,8 +453,17 @@ describe('app', () => {
|
||||
appDir: true,
|
||||
});
|
||||
|
||||
expect(tree.exists('apps/testApp/pages/styles.css')).toBeFalsy();
|
||||
|
||||
const tsConfig = readJson(tree, 'apps/test-app/tsconfig.json');
|
||||
expect(tsConfig.include).toEqual([
|
||||
'**/*.ts',
|
||||
'**/*.tsx',
|
||||
'**/*.js',
|
||||
'**/*.jsx',
|
||||
'../../apps/test-app/.next/types/**/*.ts',
|
||||
'../../dist/apps/test-app/.next/types/**/*.ts',
|
||||
'next-env.d.ts',
|
||||
]);
|
||||
expect(tree.exists('apps/test-app/pages/styles.css')).toBeFalsy();
|
||||
expect(tree.exists('apps/test-app/app/global.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/test-app/app/page.tsx')).toBeTruthy();
|
||||
expect(tree.exists('apps/test-app/app/layout.tsx')).toBeTruthy();
|
||||
@ -459,5 +471,25 @@ describe('app', () => {
|
||||
expect(tree.exists('apps/test-app/app/page.module.css')).toBeTruthy();
|
||||
expect(tree.exists('apps/test-app/public/favicon.ico')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add layout types correctly for standalone apps', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'testApp',
|
||||
style: 'css',
|
||||
appDir: true,
|
||||
rootProject: true,
|
||||
});
|
||||
|
||||
const tsConfig = readJson(tree, 'tsconfig.json');
|
||||
expect(tsConfig.include).toEqual([
|
||||
'**/*.ts',
|
||||
'**/*.tsx',
|
||||
'**/*.js',
|
||||
'**/*.jsx',
|
||||
'.next/types/**/*.ts',
|
||||
'dist/test-app/.next/types/**/*.ts',
|
||||
'next-env.d.ts',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import Head from 'next/head';
|
||||
import './global.<%= stylesExt %>';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Nx Next App',
|
||||
title: 'Welcome to <%= name %>',
|
||||
description: 'Generated by create-nx-workspace',
|
||||
}
|
||||
|
||||
@ -13,9 +12,6 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<Head>
|
||||
<title>Welcome to <%= name %>!</title>
|
||||
</Head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@ -17,11 +17,6 @@ const nextConfig = {
|
||||
// See: https://github.com/gregberge/svgr
|
||||
svgr: false,
|
||||
},
|
||||
<% if(appDir) { %>
|
||||
experimental: {
|
||||
appDir: true
|
||||
},
|
||||
<% } %>
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
@ -43,11 +38,6 @@ const nextConfig = {
|
||||
// See: https://github.com/gregberge/svgr
|
||||
svgr: false,
|
||||
},
|
||||
<% if(appDir) { %>
|
||||
experimental: {
|
||||
appDir: true
|
||||
},
|
||||
<% } %>
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
@ -72,11 +62,6 @@ const nextConfig = {
|
||||
// See: https://github.com/gregberge/svgr
|
||||
svgr: false,
|
||||
},
|
||||
<% if(appDir) { %>
|
||||
experimental: {
|
||||
appDir: true
|
||||
},
|
||||
<% } %>
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
@ -96,11 +81,6 @@ const nextConfig = {
|
||||
// See: https://github.com/gregberge/svgr
|
||||
svgr: false,
|
||||
},
|
||||
<% if(appDir) { %>
|
||||
experimental: {
|
||||
appDir: true
|
||||
},
|
||||
<% } %>
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
|
||||
@ -14,6 +14,16 @@
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }]
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
<% if (appDir) { %>
|
||||
"<%= layoutTypeSrcPath %>",
|
||||
"<%= layoutTypeDistPath %>",
|
||||
<% } %>
|
||||
"next-env.d.ts"
|
||||
],
|
||||
"exclude": ["node_modules", "jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
joinPathFragments,
|
||||
ProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
@ -9,18 +8,13 @@ import {
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const targets: Record<string, any> = {};
|
||||
|
||||
const outputPath = joinPathFragments(
|
||||
'dist',
|
||||
options.appProjectRoot,
|
||||
...(options.rootProject ? [options.name] : [])
|
||||
);
|
||||
targets.build = {
|
||||
executor: '@nx/next:build',
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: {
|
||||
root: options.appProjectRoot,
|
||||
outputPath: outputPath,
|
||||
outputPath: options.outputPath,
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
generateFiles,
|
||||
joinPathFragments,
|
||||
names,
|
||||
offsetFromRoot as _offsetFromRoot,
|
||||
readJson,
|
||||
toJS,
|
||||
Tree,
|
||||
@ -17,11 +18,25 @@ import {
|
||||
} from './create-application-files.helpers';
|
||||
|
||||
export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
const offsetFromRoot = _offsetFromRoot(options.appProjectRoot);
|
||||
const layoutTypeSrcPath = joinPathFragments(
|
||||
offsetFromRoot,
|
||||
options.appProjectRoot,
|
||||
'.next/types/**/*.ts'
|
||||
);
|
||||
const layoutTypeDistPath = joinPathFragments(
|
||||
offsetFromRoot,
|
||||
options.outputPath,
|
||||
'.next/types/**/*.ts'
|
||||
);
|
||||
const templateVariables = {
|
||||
...names(options.name),
|
||||
...options,
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
offsetFromRoot,
|
||||
layoutTypeSrcPath,
|
||||
layoutTypeDistPath,
|
||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
||||
host,
|
||||
options.appProjectRoot
|
||||
@ -29,6 +44,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
appContent: createAppJsx(options.name),
|
||||
styleContent: createStyleRules(),
|
||||
pageStyleContent: `.page {}`,
|
||||
|
||||
stylesExt:
|
||||
options.style === 'less' || options.style === 'styl'
|
||||
? options.style
|
||||
|
||||
@ -13,6 +13,7 @@ import { Schema } from '../schema';
|
||||
export interface NormalizedSchema extends Schema {
|
||||
projectName: string;
|
||||
appProjectRoot: string;
|
||||
outputPath: string;
|
||||
e2eProjectName: string;
|
||||
e2eProjectRoot: string;
|
||||
parsedTags: string[];
|
||||
@ -28,6 +29,7 @@ export function normalizeOptions(
|
||||
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
|
||||
options.directory
|
||||
);
|
||||
const name = names(options.name).fileName;
|
||||
|
||||
const appDirectory = projectDirectory
|
||||
? `${names(projectDirectory).fileName}/${names(options.name).fileName}`
|
||||
@ -46,13 +48,19 @@ export function normalizeOptions(
|
||||
? '.'
|
||||
: joinPathFragments(appsDir, `${appDirectory}-e2e`);
|
||||
|
||||
const outputPath = joinPathFragments(
|
||||
'dist',
|
||||
appProjectRoot,
|
||||
...(options.rootProject ? [name] : [])
|
||||
);
|
||||
|
||||
const parsedTags = options.tags
|
||||
? options.tags.split(',').map((s) => s.trim())
|
||||
: [];
|
||||
|
||||
const fileName = 'index';
|
||||
|
||||
const appDir = options.appDir ?? false;
|
||||
const appDir = options.appDir ?? true;
|
||||
|
||||
const styledModule = /^(css|scss|less|styl)$/.test(options.style)
|
||||
? null
|
||||
@ -63,17 +71,18 @@ export function normalizeOptions(
|
||||
return {
|
||||
...options,
|
||||
appDir,
|
||||
name: names(options.name).fileName,
|
||||
projectName: appProjectName,
|
||||
linter: options.linter || Linter.EsLint,
|
||||
unitTestRunner: options.unitTestRunner || 'jest',
|
||||
e2eTestRunner: options.e2eTestRunner || 'cypress',
|
||||
style: options.style || 'css',
|
||||
appProjectRoot,
|
||||
e2eProjectRoot,
|
||||
e2eProjectName,
|
||||
parsedTags,
|
||||
e2eProjectRoot,
|
||||
e2eTestRunner: options.e2eTestRunner || 'cypress',
|
||||
fileName,
|
||||
linter: options.linter || Linter.EsLint,
|
||||
name,
|
||||
outputPath,
|
||||
parsedTags,
|
||||
projectName: appProjectName,
|
||||
style: options.style || 'css',
|
||||
styledModule,
|
||||
unitTestRunner: options.unitTestRunner || 'jest',
|
||||
};
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { NormalizedSchema } from './normalize-options';
|
||||
export function showPossibleWarnings(tree: Tree, options: NormalizedSchema) {
|
||||
if (options.style === '@emotion/styled' && options.appDir) {
|
||||
logger.warn(
|
||||
`Emotion may not work with the experimental appDir layout. See: https://beta.nextjs.org/docs/styling/css-in-js`
|
||||
`Emotion may not work with the App Router. See: https://beta.nextjs.org/docs/styling/css-in-js`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,9 +121,9 @@
|
||||
},
|
||||
"appDir": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable experimental app directory for the project",
|
||||
"x-prompt": "Do you want to use experimental app/ in this project?"
|
||||
"default": true,
|
||||
"description": "Enable the App Router for this project.",
|
||||
"x-prompt": "Would you like to use the App Router (recommended)?"
|
||||
},
|
||||
"rootProject": {
|
||||
"description": "Create an application at the root of the workspace.",
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const nextVersion = '13.3.0';
|
||||
export const eslintConfigNextVersion = '13.3.0';
|
||||
export const sassVersion = '1.61.0';
|
||||
export const nextVersion = '13.4.1';
|
||||
export const eslintConfigNextVersion = '13.4.1';
|
||||
export const sassVersion = '1.62.1';
|
||||
export const lessLoader = '11.1.0';
|
||||
export const stylusLoader = '7.1.0';
|
||||
export const emotionServerVersion = '11.10.0';
|
||||
export const emotionServerVersion = '11.11.0';
|
||||
export const babelPluginStyledComponentsVersion = '1.10.7';
|
||||
export const tsLibVersion = '^2.3.0';
|
||||
|
||||
@ -73,9 +73,9 @@
|
||||
"enum": ["express", "koa", "fastify", "nest", "none"]
|
||||
},
|
||||
"nextAppDir": {
|
||||
"description": "Enable experimental app directory for the project",
|
||||
"description": "Enable the App Router for this project.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
"default": true
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
|
||||
@ -48,7 +48,7 @@ describe('preset', () => {
|
||||
style: 'css',
|
||||
linter: 'eslint',
|
||||
});
|
||||
expect(tree.exists('/apps/proj/pages/index.tsx')).toBe(true);
|
||||
expect(tree.exists('/apps/proj/app/page.tsx')).toBe(true);
|
||||
});
|
||||
|
||||
it(`should create files (preset = ${Preset.Express})`, async () => {
|
||||
|
||||
@ -85,9 +85,9 @@
|
||||
"default": false
|
||||
},
|
||||
"nextAppDir": {
|
||||
"description": "Enable experimental app/ for the project",
|
||||
"description": "Enable the App Router for this project.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
"default": true
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user