From e1c3fbbaf0eba85d774cb105e6c519427e87d15a Mon Sep 17 00:00:00 2001 From: Nicholas Cunningham Date: Wed, 12 Apr 2023 14:54:24 -0600 Subject: [PATCH] feat(nextjs): Add standalone Nextjs option to react selection when running CNW --- docs/generated/cli/create-nx-workspace.md | 8 +- .../packages/next/generators/application.json | 7 + .../packages/next/generators/init.json | 7 + .../nx/documents/create-nx-workspace.md | 8 +- .../packages/workspace/generators/new.json | 5 + .../packages/workspace/generators/preset.json | 5 + e2e/next/src/next.test.ts | 22 +- .../bin/create-nx-workspace.ts | 93 +++++- .../preset/point-to-tutorial-and-course.ts | 1 + .../src/utils/preset/preset.ts | 1 + .../application/files/app/page.tsx__tmpl__ | 6 +- .../{.babelrc__tmpl__ => __dot__babelrc} | 0 .../files/common/tsconfig.json__tmpl__ | 3 +- .../generators/application/lib/add-cypress.ts | 2 +- .../lib/create-application-files.ts | 52 +++- .../application/lib/normalize-options.ts | 11 +- .../src/generators/application/schema.d.ts | 1 + .../src/generators/application/schema.json | 7 + packages/next/src/generators/init/init.ts | 1 + packages/next/src/generators/init/schema.d.ts | 1 + packages/next/src/generators/init/schema.json | 7 + .../generate-workspace-files.spec.ts.snap | 25 ++ .../src/generators/new/generate-preset.ts | 2 + .../new/generate-workspace-files.spec.ts | 1 + .../new/generate-workspace-files.ts | 7 +- packages/workspace/src/generators/new/new.ts | 1 + .../workspace/src/generators/new/schema.json | 5 + .../workspace/src/generators/preset/preset.ts | 10 + .../src/generators/preset/schema.d.ts | 1 + .../src/generators/preset/schema.json | 5 + .../workspace/src/generators/utils/presets.ts | 1 + tsconfig.base.json | 289 +++++++++++++----- 32 files changed, 495 insertions(+), 100 deletions(-) rename packages/next/src/generators/application/files/common/{.babelrc__tmpl__ => __dot__babelrc} (100%) diff --git a/docs/generated/cli/create-nx-workspace.md b/docs/generated/cli/create-nx-workspace.md index 2e1ee043afefbf..c0927ba7ed8293 100644 --- a/docs/generated/cli/create-nx-workspace.md +++ b/docs/generated/cli/create-nx-workspace.md @@ -25,6 +25,12 @@ Default: `false` Show all prompts +### appDir + +Type: `boolean` + +Add Experimental app/ layout for next.js + ### appName Type: `string` @@ -129,7 +135,7 @@ Package manager to use Type: `string` -Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-standalone"]. To build your own see https://nx.dev/packages/nx-plugin#preset +Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "nextjs-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-standalone"]. To build your own see https://nx.dev/packages/nx-plugin#preset ### routing diff --git a/docs/generated/packages/next/generators/application.json b/docs/generated/packages/next/generators/application.json index fa325636748a00..6089512e1b5c70 100644 --- a/docs/generated/packages/next/generators/application.json +++ b/docs/generated/packages/next/generators/application.json @@ -121,6 +121,13 @@ "default": false, "description": "Enable experimental app directory for the project", "x-prompt": "Do you want to use experimental app/ in this project?" + }, + "rootProject": { + "description": "Create an application at the root of the workspace.", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" } }, "required": [], diff --git a/docs/generated/packages/next/generators/init.json b/docs/generated/packages/next/generators/init.json index 2859ac27a4df1e..725954439807a3 100644 --- a/docs/generated/packages/next/generators/init.json +++ b/docs/generated/packages/next/generators/init.json @@ -36,6 +36,13 @@ "default": false, "description": "Do not add dependencies to `package.json`.", "x-priority": "internal" + }, + "rootProject": { + "description": "Create an application at the root of the workspace.", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" } }, "required": [], diff --git a/docs/generated/packages/nx/documents/create-nx-workspace.md b/docs/generated/packages/nx/documents/create-nx-workspace.md index 2e1ee043afefbf..c0927ba7ed8293 100644 --- a/docs/generated/packages/nx/documents/create-nx-workspace.md +++ b/docs/generated/packages/nx/documents/create-nx-workspace.md @@ -25,6 +25,12 @@ Default: `false` Show all prompts +### appDir + +Type: `boolean` + +Add Experimental app/ layout for next.js + ### appName Type: `string` @@ -129,7 +135,7 @@ Package manager to use Type: `string` -Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-standalone"]. To build your own see https://nx.dev/packages/nx-plugin#preset +Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular-monorepo", "angular-standalone", "react-monorepo", "react-standalone", "nextjs-standalone", "react-native", "expo", "next", "nest", "express", "react", "angular", "node-standalone"]. To build your own see https://nx.dev/packages/nx-plugin#preset ### routing diff --git a/docs/generated/packages/workspace/generators/new.json b/docs/generated/packages/workspace/generators/new.json index ad4605cca4d343..b8ea38cd6b2f1f 100644 --- a/docs/generated/packages/workspace/generators/new.json +++ b/docs/generated/packages/workspace/generators/new.json @@ -68,6 +68,11 @@ "description": "The framework which the application is using", "type": "string", "enum": ["express", "koa", "fastify", "nest", "none"] + }, + "nextAppDir": { + "description": "Enable experimental app directory for the project", + "type": "boolean", + "default": false } }, "additionalProperties": true, diff --git a/docs/generated/packages/workspace/generators/preset.json b/docs/generated/packages/workspace/generators/preset.json index c9165e99375c2c..2945f5cd8ab2f1 100644 --- a/docs/generated/packages/workspace/generators/preset.json +++ b/docs/generated/packages/workspace/generators/preset.json @@ -80,6 +80,11 @@ "description": "Generate a Dockerfile", "type": "boolean", "default": false + }, + "appDir": { + "description": "Enable experimental app directory for the project", + "type": "boolean", + "default": false } }, "presets": [] diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 6b19f46cc8110c..bf340cf28f944a 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -21,7 +21,6 @@ import { } from '@nrwl/e2e/utils'; import * as http from 'http'; import { checkApp } from './utils'; -import { removeSync } from 'fs-extra'; describe('Next.js Applications', () => { let proj: string; @@ -426,6 +425,27 @@ describe('Next.js Applications', () => { checkExport: false, }); }, 300_000); + + it('should create a generate a next.js app with app layout enabled', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nrwl/next:app ${appName} --style=css --appDir --no-interactive` + ); + + checkFilesExist(`apps/${appName}/app/api/hello/route.ts`); + checkFilesExist(`apps/${appName}/app/page.tsx`); + checkFilesExist(`apps/${appName}/app/layout.tsx`); + checkFilesExist(`apps/${appName}/app/global.css`); + checkFilesExist(`apps/${appName}/app/page.module.css`); + + await checkApp(appName, { + checkUnitTest: false, + checkLint: false, + checkE2E: false, + checkExport: false, + }); + }, 300_000); }); function getData(port, path = ''): Promise { diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index f80e5ef82dbc52..210717a8ad6443 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -39,6 +39,7 @@ interface Arguments extends CreateWorkspaceOptions { framework: Framework; standaloneApi: boolean; docker: boolean; + nextAppDir: boolean; routing: boolean; bundler: Bundler; } @@ -104,6 +105,10 @@ export const commandsObject: yargs.Argv = yargs .option('docker', { describe: chalk.dim`Generate a Dockerfile with your node-server`, type: 'boolean', + }) + .option('appDir', { + describe: chalk.dim`Add Experimental app/ layout for next.js`, + type: 'boolean', }), withNxCloud, withCI, @@ -180,6 +185,7 @@ async function normalizeArgsMiddleware( framework, bundler, docker, + nextAppDir, routing, standaloneApi; @@ -209,7 +215,7 @@ async function normalizeArgsMiddleware( if (monorepoStyle === 'package-based') { preset = 'npm'; } else if (monorepoStyle === 'react') { - preset = Preset.ReactStandalone; + preset = await determineReactFramework(argv); } else if (monorepoStyle === 'angular') { preset = Preset.AngularStandalone; } else if (monorepoStyle === 'node-standalone') { @@ -228,7 +234,8 @@ async function normalizeArgsMiddleware( if ( preset === Preset.ReactStandalone || preset === Preset.AngularStandalone || - preset === Preset.NodeStandalone + preset === Preset.NodeStandalone || + preset === Preset.NextJsStandalone ) { appName = argv.appName ?? argv.name ?? (await determineAppName(preset, argv)); @@ -241,6 +248,10 @@ async function normalizeArgsMiddleware( } } + if (preset === Preset.NextJsStandalone) { + nextAppDir = await isNextAppDir(argv); + } + if (preset === Preset.ReactStandalone) { bundler = await determineBundler(argv); } @@ -291,6 +302,7 @@ async function normalizeArgsMiddleware( ci, bundler, docker, + nextAppDir, }); } catch (e) { console.error(e); @@ -363,27 +375,27 @@ async function determineMonorepoStyle(): Promise { { name: 'package-based', message: - 'Package-based monorepo: Nx makes it fast, but lets you run things your way.', + 'Package-based monorepo: Nx makes it fast, but lets you run things your way.', }, { name: 'integrated', message: - 'Integrated monorepo: Nx configures your favorite frameworks and lets you focus on shipping features.', + 'Integrated monorepo: Nx configures your favorite frameworks and lets you focus on shipping features.', }, { name: 'react', message: - 'Standalone React app: Nx configures Vite (or Webpack), ESLint, and Cypress.', + 'Standalone React app: Nx configures a React app with an optional framework (e.g. Next.js).', }, { name: 'angular', message: - 'Standalone Angular app: Nx configures Jest, ESLint and Cypress.', + 'Standalone Angular app: Nx configures Jest, ESLint and Cypress.', }, { name: 'node-standalone', message: - 'Standalone Node app: Nx configures a framework (ex. Express), esbuild, ESlint and Jest.', + 'Standalone Node app: Nx configures a framework (e.g. Express), esbuild, ESlint and Jest.', }, ], }, @@ -582,6 +594,64 @@ async function determineDockerfile( } } +async function determineReactFramework(parsedArgs: yargs.Arguments) { + if (parsedArgs.framework) { + return parsedArgs.framework === 'next.js' + ? Preset.NextJsStandalone + : Preset.ReactStandalone; + } + return enquirer + .prompt<{ framework: 'none' | 'next.js' }>([ + { + name: 'framework', + message: 'What framework would you like to use?', + type: 'autocomplete', + choices: [ + { + name: 'none', + message: 'None', + hint: 'I only want React', + }, + { + name: 'next.js', + message: 'Next.js [https://nextjs.org/]', + }, + ], + initial: 'none' as any, + }, + ]) + .then((choice) => + choice.framework === 'next.js' + ? Preset.NextJsStandalone + : Preset.ReactStandalone + ); +} + +async function isNextAppDir(parsedArgs: yargs.Arguments) { + if (parsedArgs.appDir === undefined) { + return enquirer + .prompt<{ appDir: 'Yes' | 'No' }>([ + { + name: 'appDir', + message: 'Do you want to use experimental app/ in this project?', + type: 'autocomplete', + choices: [ + { + name: 'No', + }, + { + name: 'Yes', + }, + ], + initial: 'No' as any, + }, + ]) + .then((choice) => choice.appDir === 'Yes'); + } else { + return Promise.resolve(parsedArgs.appDir); + } +} + async function determineStyle( preset: Preset, parsedArgs: yargs.Arguments @@ -617,9 +687,12 @@ async function determineStyle( ]; if ( - [Preset.ReactMonorepo, Preset.ReactStandalone, Preset.NextJs].includes( - preset - ) + [ + Preset.ReactMonorepo, + Preset.ReactStandalone, + Preset.NextJs, + Preset.NextJsStandalone, + ].includes(preset) ) { choices.push( { diff --git a/packages/create-nx-workspace/src/utils/preset/point-to-tutorial-and-course.ts b/packages/create-nx-workspace/src/utils/preset/point-to-tutorial-and-course.ts index 3224f10229203e..50c53fbc6a90eb 100644 --- a/packages/create-nx-workspace/src/utils/preset/point-to-tutorial-and-course.ts +++ b/packages/create-nx-workspace/src/utils/preset/point-to-tutorial-and-course.ts @@ -31,6 +31,7 @@ export function pointToTutorialAndCourse(preset: Preset) { break; case Preset.ReactMonorepo: case Preset.NextJs: + case Preset.NextJsStandalone: output.addVerticalSeparator(); output.note({ title, diff --git a/packages/create-nx-workspace/src/utils/preset/preset.ts b/packages/create-nx-workspace/src/utils/preset/preset.ts index 237e9c558594b5..554d3742748361 100644 --- a/packages/create-nx-workspace/src/utils/preset/preset.ts +++ b/packages/create-nx-workspace/src/utils/preset/preset.ts @@ -9,6 +9,7 @@ export enum Preset { AngularStandalone = 'angular-standalone', ReactMonorepo = 'react-monorepo', ReactStandalone = 'react-standalone', + NextJsStandalone = 'nextjs-standalone', ReactNative = 'react-native', Expo = 'expo', NextJs = 'next', diff --git a/packages/next/src/generators/application/files/app/page.tsx__tmpl__ b/packages/next/src/generators/application/files/app/page.tsx__tmpl__ index 4d4e80e0330707..de8950f216d6fa 100644 --- a/packages/next/src/generators/application/files/app/page.tsx__tmpl__ +++ b/packages/next/src/generators/application/files/app/page.tsx__tmpl__ @@ -11,7 +11,7 @@ const StyledPage = styled.div`<%- pageStyleContent %>`; <% }%> -export async function Index() { +export default async function Index() { /* * Replace the elements below with your own. * @@ -23,6 +23,4 @@ export async function Index() { <%- appContent %> > ); -}; - -export default Index; \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/next/src/generators/application/files/common/.babelrc__tmpl__ b/packages/next/src/generators/application/files/common/__dot__babelrc similarity index 100% rename from packages/next/src/generators/application/files/common/.babelrc__tmpl__ rename to packages/next/src/generators/application/files/common/__dot__babelrc diff --git a/packages/next/src/generators/application/files/common/tsconfig.json__tmpl__ b/packages/next/src/generators/application/files/common/tsconfig.json__tmpl__ index fcd3120fc97b7c..35fd206998f618 100644 --- a/packages/next/src/generators/application/files/common/tsconfig.json__tmpl__ +++ b/packages/next/src/generators/application/files/common/tsconfig.json__tmpl__ @@ -11,7 +11,8 @@ "noEmit": true, "resolveJsonModule": true, "isolatedModules": true, - "incremental": true + "incremental": true, + "plugins": [{ "name": "next" }] }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], "exclude": ["node_modules", "jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] diff --git a/packages/next/src/generators/application/lib/add-cypress.ts b/packages/next/src/generators/application/lib/add-cypress.ts index c4ccd30a45f11c..fea217778dea68 100644 --- a/packages/next/src/generators/application/lib/add-cypress.ts +++ b/packages/next/src/generators/application/lib/add-cypress.ts @@ -15,7 +15,7 @@ export async function addCypress(host: Tree, options: NormalizedSchema) { return cypressProjectGenerator(host, { ...options, linter: Linter.EsLint, - name: `${options.name}-e2e`, + name: options.e2eProjectName, directory: options.directory, project: options.projectName, skipFormat: true, diff --git a/packages/next/src/generators/application/lib/create-application-files.ts b/packages/next/src/generators/application/lib/create-application-files.ts index c3610e7cc8bc5a..8829ff25fdf5a1 100644 --- a/packages/next/src/generators/application/lib/create-application-files.ts +++ b/packages/next/src/generators/application/lib/create-application-files.ts @@ -1,5 +1,12 @@ import { join } from 'path'; -import { generateFiles, names, toJS, Tree } from '@nx/devkit'; +import { + generateFiles, + names, + readJson, + toJS, + Tree, + updateJson, +} from '@nx/devkit'; import { getRelativePathToRootTsConfig } from '@nx/js'; import { NormalizedSchema } from './normalize-options'; @@ -12,6 +19,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { const templateVariables = { ...names(options.name), ...options, + dot: '.', tmpl: '', rootTsConfigPath: getRelativePathToRootTsConfig( host, @@ -49,6 +57,38 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { ); } + if (options.rootProject) { + updateJson(host, 'tsconfig.base.json', (json) => { + const appJSON = readJson(host, 'tsconfig.json'); + + let { extends: _, ...updatedJson } = json; + + updatedJson = { + ...updateJson, + compilerOptions: { + ...updatedJson.compilerOptions, + ...appJSON.compilerOptions, + }, + include: [ + ...new Set([ + ...(updatedJson.include || []), + ...(appJSON.include || []), + ]), + ], + exclude: [ + ...new Set([ + ...(updatedJson.exclude || []), + ...(appJSON.exclude || []), + '**e2e/**/*', + ]), + ], + }; + return updatedJson; + }); + host.delete('tsconfig.json'); + host.rename('tsconfig.base.json', 'tsconfig.json'); + } + if (options.unitTestRunner === 'none') { host.delete(`${options.appProjectRoot}/specs/${options.fileName}.spec.tsx`); } @@ -60,9 +100,13 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { } if (options.styledModule) { - host.delete( - `${options.appProjectRoot}/pages/${options.fileName}.module.${options.style}` - ); + if (options.appDir) { + host.delete(`${options.appProjectRoot}/app/page.module.${options.style}`); + } else { + host.delete( + `${options.appProjectRoot}/pages/${options.fileName}.module.${options.style}` + ); + } } if (options.style !== 'styled-components') { diff --git a/packages/next/src/generators/application/lib/normalize-options.ts b/packages/next/src/generators/application/lib/normalize-options.ts index 9962f439d928e5..a3e4fda72c4c31 100644 --- a/packages/next/src/generators/application/lib/normalize-options.ts +++ b/packages/next/src/generators/application/lib/normalize-options.ts @@ -36,10 +36,15 @@ export function normalizeOptions( const appsDir = layoutDirectory ?? getWorkspaceLayout(host).appsDir; const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-'); - const e2eProjectName = `${appProjectName}-e2e`; + const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`; - const appProjectRoot = joinPathFragments(appsDir, appDirectory); - const e2eProjectRoot = joinPathFragments(appsDir, `${appDirectory}-e2e`); + const appProjectRoot = options.rootProject + ? '.' + : joinPathFragments(appsDir, appDirectory); + + const e2eProjectRoot = options.rootProject + ? '.' + : joinPathFragments(appsDir, `${appDirectory}-e2e`); const parsedTags = options.tags ? options.tags.split(',').map((s) => s.trim()) diff --git a/packages/next/src/generators/application/schema.d.ts b/packages/next/src/generators/application/schema.d.ts index 7e468a19080885..fc9ff0fdbb309a 100644 --- a/packages/next/src/generators/application/schema.d.ts +++ b/packages/next/src/generators/application/schema.d.ts @@ -16,4 +16,5 @@ export interface Schema { customServer?: boolean; skipPackageJson?: boolean; appDir?: boolean; + rootProject?: boolean; } diff --git a/packages/next/src/generators/application/schema.json b/packages/next/src/generators/application/schema.json index 889443d88aa620..39bf2d17f88484 100644 --- a/packages/next/src/generators/application/schema.json +++ b/packages/next/src/generators/application/schema.json @@ -124,6 +124,13 @@ "default": false, "description": "Enable experimental app directory for the project", "x-prompt": "Do you want to use experimental app/ in this project?" + }, + "rootProject": { + "description": "Create an application at the root of the workspace.", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" } }, "required": [], diff --git a/packages/next/src/generators/init/init.ts b/packages/next/src/generators/init/init.ts index e0bc4061a29f3c..42712b80dfcb0a 100644 --- a/packages/next/src/generators/init/init.ts +++ b/packages/next/src/generators/init/init.ts @@ -65,6 +65,7 @@ export async function nextInitGenerator(host: Tree, schema: InitSchema) { const reactTask = await reactInitGenerator(host, { ...schema, skipFormat: true, + skipBabelConfig: true, }); tasks.push(reactTask); diff --git a/packages/next/src/generators/init/schema.d.ts b/packages/next/src/generators/init/schema.d.ts index 0ef1c5ded9b54c..43169ac817dad0 100644 --- a/packages/next/src/generators/init/schema.d.ts +++ b/packages/next/src/generators/init/schema.d.ts @@ -4,4 +4,5 @@ export interface InitSchema { skipFormat?: boolean; js?: boolean; skipPackageJson?: boolean; + rootProject?: boolean; } diff --git a/packages/next/src/generators/init/schema.json b/packages/next/src/generators/init/schema.json index 9fa41c1956bd8e..8492383e91d023 100644 --- a/packages/next/src/generators/init/schema.json +++ b/packages/next/src/generators/init/schema.json @@ -33,6 +33,13 @@ "default": false, "description": "Do not add dependencies to `package.json`.", "x-priority": "internal" + }, + "rootProject": { + "description": "Create an application at the root of the workspace.", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" } }, "required": [] diff --git a/packages/workspace/src/generators/new/__snapshots__/generate-workspace-files.spec.ts.snap b/packages/workspace/src/generators/new/__snapshots__/generate-workspace-files.spec.ts.snap index b41c8cdda6ba72..97464c3467d2a2 100644 --- a/packages/workspace/src/generators/new/__snapshots__/generate-workspace-files.spec.ts.snap +++ b/packages/workspace/src/generators/new/__snapshots__/generate-workspace-files.spec.ts.snap @@ -230,6 +230,31 @@ Visit the [Nx Documentation](https://nx.dev) to learn more. " `; +exports[`@nx/workspace:generateWorkspaceFiles README.md should be created for NextJsStandalone preset 1`] = ` +"# Proj + + + +✨ **This workspace has been generated by [Nx, a Smart, fast and extensible build system.](https://nx.dev)** ✨ + +## Development server + +Run \`nx serve app1\` for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files. + +## Understand this workspace + +Run \`nx graph\` to see a diagram of the dependencies of the projects. + +## Remote caching + +Run \`npx nx connect-to-nx-cloud\` to enable [remote caching](https://nx.app) and make CI faster. + +## Further help + +Visit the [Nx Documentation](https://nx.dev) to learn more. +" +`; + exports[`@nx/workspace:generateWorkspaceFiles README.md should be created for NodeStandalone preset 1`] = ` "# Proj diff --git a/packages/workspace/src/generators/new/generate-preset.ts b/packages/workspace/src/generators/new/generate-preset.ts index b453cc291fdcc4..83e6670c15ed3b 100644 --- a/packages/workspace/src/generators/new/generate-preset.ts +++ b/packages/workspace/src/generators/new/generate-preset.ts @@ -77,6 +77,7 @@ export function generatePreset(host: Tree, opts: NormalizedSchema) { opts.bundler ? `--bundler=${opts.bundler}` : null, opts.framework ? `--framework=${opts.framework}` : null, opts.docker ? `--docker=${opts.docker}` : null, + opts.nextAppDir ? `--nextAppDir=${opts.nextAppDir}` : null, opts.packageManager ? `--packageManager=${opts.packageManager}` : null, opts.standaloneApi !== undefined ? `--standaloneApi=${opts.standaloneApi}` @@ -116,6 +117,7 @@ function getPresetDependencies({ }; case Preset.NextJs: + case Preset.NextJsStandalone: return { dependencies: { '@nx/next': nxVersion }, dev: {} }; case Preset.ReactMonorepo: diff --git a/packages/workspace/src/generators/new/generate-workspace-files.spec.ts b/packages/workspace/src/generators/new/generate-workspace-files.spec.ts index 273d3df1acc6d7..1f5a7ec18db217 100644 --- a/packages/workspace/src/generators/new/generate-workspace-files.spec.ts +++ b/packages/workspace/src/generators/new/generate-workspace-files.spec.ts @@ -41,6 +41,7 @@ describe('@nx/workspace:generateWorkspaceFiles', () => { Preset.WebComponents, Preset.Express, Preset.NodeStandalone, + Preset.NextJsStandalone, ].includes(Preset[preset]) ) { appName = 'app1'; diff --git a/packages/workspace/src/generators/new/generate-workspace-files.ts b/packages/workspace/src/generators/new/generate-workspace-files.ts index f6fe12724c42c4..aafed1eef99a02 100644 --- a/packages/workspace/src/generators/new/generate-workspace-files.ts +++ b/packages/workspace/src/generators/new/generate-workspace-files.ts @@ -67,6 +67,7 @@ function createAppsAndLibsFolders(tree: Tree, options: NormalizedSchema) { options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || options.preset === Preset.NodeStandalone || + options.preset === Preset.NextJsStandalone || options.isCustomPreset ) { // don't generate any folders @@ -126,7 +127,8 @@ function createFiles(tree: Tree, options: NormalizedSchema) { const filesDirName = options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || - options.preset === Preset.NodeStandalone + options.preset === Preset.NodeStandalone || + options.preset === Preset.NextJsStandalone ? './files-root-app' : options.preset === Preset.NPM || options.preset === Preset.Core ? './files-package-based-repo' @@ -177,7 +179,8 @@ function addNpmScripts(tree: Tree, options: NormalizedSchema) { if ( options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || - options.preset === Preset.NodeStandalone + options.preset === Preset.NodeStandalone || + options.preset === Preset.NextJsStandalone ) { updateJson(tree, join(options.directory, 'package.json'), (json) => { Object.assign(json.scripts, { diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index 2f8218413e86ad..bb9e7b13978264 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -24,6 +24,7 @@ interface Schema { defaultBase: string; framework?: string; docker?: boolean; + nextAppDir?: boolean; linter?: Linter; bundler?: 'vite' | 'webpack'; standaloneApi?: boolean; diff --git a/packages/workspace/src/generators/new/schema.json b/packages/workspace/src/generators/new/schema.json index b4e7c4c55acec1..f2c6f4655e6411 100644 --- a/packages/workspace/src/generators/new/schema.json +++ b/packages/workspace/src/generators/new/schema.json @@ -71,6 +71,11 @@ "description": "The framework which the application is using", "type": "string", "enum": ["express", "koa", "fastify", "nest", "none"] + }, + "nextAppDir": { + "description": "Enable experimental app directory for the project", + "type": "boolean", + "default": false } }, "additionalProperties": true diff --git a/packages/workspace/src/generators/preset/preset.ts b/packages/workspace/src/generators/preset/preset.ts index d36375155f5454..f3dc2e3522595f 100644 --- a/packages/workspace/src/generators/preset/preset.ts +++ b/packages/workspace/src/generators/preset/preset.ts @@ -79,6 +79,16 @@ async function createPreset(tree: Tree, options: Schema) { style: options.style, linter: options.linter, }); + } else if (options.preset === Preset.NextJsStandalone) { + const { applicationGenerator: nextApplicationGenerator } = require('@nx' + + '/next'); + return nextApplicationGenerator(tree, { + name: options.name, + style: options.style, + linter: options.linter, + appDir: options.nextAppDir, + rootProject: true, + }); } else if (options.preset === Preset.WebComponents) { const { applicationGenerator: webApplicationGenerator } = require('@nx' + '/web'); diff --git a/packages/workspace/src/generators/preset/schema.d.ts b/packages/workspace/src/generators/preset/schema.d.ts index aa6f25925edd20..358162a4d35770 100644 --- a/packages/workspace/src/generators/preset/schema.d.ts +++ b/packages/workspace/src/generators/preset/schema.d.ts @@ -12,6 +12,7 @@ export interface Schema { packageManager?: PackageManager; bundler?: 'vite' | 'webpack'; docker?: boolean; + nextAppDir?: boolean; routing?: boolean; standaloneApi?: boolean; } diff --git a/packages/workspace/src/generators/preset/schema.json b/packages/workspace/src/generators/preset/schema.json index 3731fb59819639..010ced123cfda9 100644 --- a/packages/workspace/src/generators/preset/schema.json +++ b/packages/workspace/src/generators/preset/schema.json @@ -83,6 +83,11 @@ "description": "Generate a Dockerfile", "type": "boolean", "default": false + }, + "appDir": { + "description": "Enable experimental app directory for the project", + "type": "boolean", + "default": false } } } diff --git a/packages/workspace/src/generators/utils/presets.ts b/packages/workspace/src/generators/utils/presets.ts index ac4032bab7331f..2874fa5b506a56 100644 --- a/packages/workspace/src/generators/utils/presets.ts +++ b/packages/workspace/src/generators/utils/presets.ts @@ -9,6 +9,7 @@ export enum Preset { AngularStandalone = 'angular-standalone', ReactMonorepo = 'react-monorepo', ReactStandalone = 'react-standalone', + NextJsStandalone = 'nextjs-standalone', ReactNative = 'react-native', Expo = 'expo', NextJs = 'next', diff --git a/tsconfig.base.json b/tsconfig.base.json index b14d71e378e19c..25500ee2476109 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -9,18 +9,31 @@ "experimentalDecorators": true, "emitDecoratorMetadata": true, "skipLibCheck": true, - "types": ["node", "jest"], - "lib": ["es2019"], + "types": [ + "node", + "jest" + ], + "lib": [ + "es2019" + ], "declaration": true, "resolveJsonModule": true, "baseUrl": ".", "rootDir": ".", "allowJs": true, "paths": { - "@nrwl/e2e/utils": ["e2e/utils"], - "@nrwl/graph/ui-components": ["graph/ui-components/src/index.ts"], - "@nrwl/graph/ui-graph": ["graph/ui-graph/src/index.ts"], - "@nrwl/graph/ui-tooltips": ["graph/ui-tooltips/src/index.ts"], + "@nrwl/e2e/utils": [ + "e2e/utils" + ], + "@nrwl/graph/ui-components": [ + "graph/ui-components/src/index.ts" + ], + "@nrwl/graph/ui-graph": [ + "graph/ui-graph/src/index.ts" + ], + "@nrwl/graph/ui-tooltips": [ + "graph/ui-tooltips/src/index.ts" + ], "@nrwl/nx-dev-feature-package-schema-viewer": [ "nx-dev/feature-package-schema-viewer/src/index.ts" ], @@ -30,7 +43,9 @@ "@nrwl/nx-dev/data-access-documents/node-only": [ "nx-dev/data-access-documents/src/node.index.ts" ], - "@nrwl/nx-dev/data-access-menu": ["nx-dev/data-access-menu/src/index.ts"], + "@nrwl/nx-dev/data-access-menu": [ + "nx-dev/data-access-menu/src/index.ts" + ], "@nrwl/nx-dev/data-access-packages": [ "nx-dev/data-access-packages/src/index.ts" ], @@ -46,74 +61,204 @@ "@nrwl/nx-dev/feature-flavor-selection": [ "nx-dev/feature-flavor-selection/src/index.ts" ], - "@nrwl/nx-dev/feature-search": ["nx-dev/feature-search/src/index.ts"], - "@nrwl/nx-dev/feature-storage": ["nx-dev/feature-storage/src/index.ts"], + "@nrwl/nx-dev/feature-search": [ + "nx-dev/feature-search/src/index.ts" + ], + "@nrwl/nx-dev/feature-storage": [ + "nx-dev/feature-storage/src/index.ts" + ], "@nrwl/nx-dev/feature-versions-and-flavors": [ "nx-dev/feature-versions-and-flavors/src/index.ts" ], - "@nrwl/nx-dev/models-document": ["nx-dev/models-document/src/index.ts"], - "@nrwl/nx-dev/models-menu": ["nx-dev/models-menu/src/index.ts"], - "@nrwl/nx-dev/models-package": ["nx-dev/models-package/src/index.ts"], - "@nrwl/nx-dev/ui-commands": ["nx-dev/ui-commands/src/index.ts"], - "@nrwl/nx-dev/ui-common": ["nx-dev/ui-common/src/index.ts"], - "@nrwl/nx-dev/ui-community": ["nx-dev/ui-community/src/index.ts"], - "@nrwl/nx-dev/ui-conference": ["nx-dev/ui-conference/src/index.ts"], - "@nrwl/nx-dev/ui-home": ["nx-dev/ui-home/src/index.ts"], - "@nrwl/nx-dev/ui-markdoc": ["nx-dev/ui-markdoc/src/index.ts"], - "@nrwl/nx-dev/ui-member-card": ["nx-dev/ui-member-card/src/index.ts"], - "@nrwl/nx-dev/ui-references": ["nx-dev/ui-references/src/index.ts"], - "@nrwl/nx-dev/ui-sponsor-card": ["nx-dev/ui-sponsor-card/src/index.ts"], - "@nrwl/nx-dev/ui-theme": ["nx-dev/ui-theme/src/index.ts"], - "@nrwl/tao": ["packages/tao"], - "@nrwl/tao/*": ["packages/tao/*"], - "@nrwl/typedoc-theme": ["/typedoc-theme/src/index.ts"], - "@nx/angular": ["packages/angular"], - "@nx/angular/*": ["packages/angular/*"], - "@nx/cypress": ["packages/cypress"], - "@nx/cypress/*": ["packages/cypress/*"], - "@nx/detox": ["packages/detox"], - "@nx/devkit": ["packages/devkit"], - "@nx/devkit/*": ["packages/devkit/*"], - "@nx/esbuild": ["packages/esbuild"], - "@nx/eslint-plugin-nx": ["packages/eslint-plugin-nx/src"], - "@nx/expo": ["packages/expo"], - "@nx/expo/*": ["packages/expo/*"], - "@nx/express": ["packages/express"], - "@nx/jest": ["packages/jest"], - "@nx/jest/*": ["packages/jest/*"], - "@nx/js": ["packages/js/src"], - "@nx/js/*": ["packages/js/*"], - "@nx/linter": ["packages/linter"], - "@nx/linter/*": ["packages/linter/*"], - "@nx/nest": ["packages/nest"], - "@nx/next": ["packages/next"], - "@nx/next/*": ["packages/next/*"], - "@nx/node": ["packages/node"], - "@nx/node/*": ["packages/node/*"], - "@nx/nx-plugin": ["packages/nx-plugin"], - "@nx/nx-plugin/*": ["packages/nx-plugin/*"], - "@nx/react": ["packages/react"], - "@nx/react-native": ["packages/react-native"], - "@nx/react-native/*": ["packages/react-native/*"], - "@nx/react/*": ["packages/react/*"], - "@nx/rollup": ["packages/rollup"], - "@nx/rollup/*": ["packages/rollup/*"], - "@nx/storybook": ["packages/storybook"], - "@nx/storybook/*": ["packages/storybook/*"], - "@nx/vite": ["packages/vite"], - "@nx/vite/*": ["packages/vite/*"], - "@nx/web": ["packages/web"], - "@nx/web/*": ["packages/web/*"], - "@nx/webpack": ["packages/webpack"], - "@nx/webpack/*": ["packages/webpack/*"], - "@nx/workspace": ["packages/workspace"], - "@nx/workspace/*": ["packages/workspace/*"], - "@nx/workspace/testing": ["packages/workspace/testing"], - "create-nx-workspace": ["packages/create-nx-workspace/index.ts"], - "create-nx-workspace/*": ["packages/create-nx-workspace/*"], - "nx": ["packages/nx"], - "nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"], - "nx/*": ["packages/nx/*"] + "@nrwl/nx-dev/models-document": [ + "nx-dev/models-document/src/index.ts" + ], + "@nrwl/nx-dev/models-menu": [ + "nx-dev/models-menu/src/index.ts" + ], + "@nrwl/nx-dev/models-package": [ + "nx-dev/models-package/src/index.ts" + ], + "@nrwl/nx-dev/ui-commands": [ + "nx-dev/ui-commands/src/index.ts" + ], + "@nrwl/nx-dev/ui-common": [ + "nx-dev/ui-common/src/index.ts" + ], + "@nrwl/nx-dev/ui-community": [ + "nx-dev/ui-community/src/index.ts" + ], + "@nrwl/nx-dev/ui-conference": [ + "nx-dev/ui-conference/src/index.ts" + ], + "@nrwl/nx-dev/ui-home": [ + "nx-dev/ui-home/src/index.ts" + ], + "@nrwl/nx-dev/ui-markdoc": [ + "nx-dev/ui-markdoc/src/index.ts" + ], + "@nrwl/nx-dev/ui-member-card": [ + "nx-dev/ui-member-card/src/index.ts" + ], + "@nrwl/nx-dev/ui-references": [ + "nx-dev/ui-references/src/index.ts" + ], + "@nrwl/nx-dev/ui-sponsor-card": [ + "nx-dev/ui-sponsor-card/src/index.ts" + ], + "@nrwl/nx-dev/ui-theme": [ + "nx-dev/ui-theme/src/index.ts" + ], + "@nrwl/tao": [ + "packages/tao" + ], + "@nrwl/tao/*": [ + "packages/tao/*" + ], + "@nrwl/typedoc-theme": [ + "/typedoc-theme/src/index.ts" + ], + "@nx/angular": [ + "packages/angular" + ], + "@nx/angular/*": [ + "packages/angular/*" + ], + "@nx/cypress": [ + "packages/cypress" + ], + "@nx/cypress/*": [ + "packages/cypress/*" + ], + "@nx/detox": [ + "packages/detox" + ], + "@nx/devkit": [ + "packages/devkit" + ], + "@nx/devkit/*": [ + "packages/devkit/*" + ], + "@nx/esbuild": [ + "packages/esbuild" + ], + "@nx/eslint-plugin-nx": [ + "packages/eslint-plugin-nx/src" + ], + "@nx/expo": [ + "packages/expo" + ], + "@nx/expo/*": [ + "packages/expo/*" + ], + "@nx/express": [ + "packages/express" + ], + "@nx/jest": [ + "packages/jest" + ], + "@nx/jest/*": [ + "packages/jest/*" + ], + "@nx/js": [ + "packages/js/src" + ], + "@nx/js/*": [ + "packages/js/*" + ], + "@nx/linter": [ + "packages/linter" + ], + "@nx/linter/*": [ + "packages/linter/*" + ], + "@nx/nest": [ + "packages/nest" + ], + "@nx/next": [ + "packages/next" + ], + "@nx/next/*": [ + "packages/next/*" + ], + "@nx/node": [ + "packages/node" + ], + "@nx/node/*": [ + "packages/node/*" + ], + "@nx/nx-plugin": [ + "packages/nx-plugin" + ], + "@nx/nx-plugin/*": [ + "packages/nx-plugin/*" + ], + "@nx/react": [ + "packages/react" + ], + "@nx/react-native": [ + "packages/react-native" + ], + "@nx/react-native/*": [ + "packages/react-native/*" + ], + "@nx/react/*": [ + "packages/react/*" + ], + "@nx/rollup": [ + "packages/rollup" + ], + "@nx/rollup/*": [ + "packages/rollup/*" + ], + "@nx/storybook": [ + "packages/storybook" + ], + "@nx/storybook/*": [ + "packages/storybook/*" + ], + "@nx/vite": [ + "packages/vite" + ], + "@nx/vite/*": [ + "packages/vite/*" + ], + "@nx/web": [ + "packages/web" + ], + "@nx/web/*": [ + "packages/web/*" + ], + "@nx/webpack": [ + "packages/webpack" + ], + "@nx/webpack/*": [ + "packages/webpack/*" + ], + "@nx/workspace": [ + "packages/workspace" + ], + "@nx/workspace/*": [ + "packages/workspace/*" + ], + "@nx/workspace/testing": [ + "packages/workspace/testing" + ], + "create-nx-workspace": [ + "packages/create-nx-workspace/index.ts" + ], + "create-nx-workspace/*": [ + "packages/create-nx-workspace/*" + ], + "nx": [ + "packages/nx" + ], + "nx-dev/ui-primitives": [ + "nx-dev/ui-primitives/src/index.ts" + ], + "nx/*": [ + "packages/nx/*" + ] } } }