feat(nextjs): Standalone projects now default to src (#21010)
This commit is contained in:
parent
49cff89908
commit
c43b22dc88
@ -119,6 +119,12 @@ Type: `boolean`
|
|||||||
|
|
||||||
Enable the App Router for Next.js
|
Enable the App Router for Next.js
|
||||||
|
|
||||||
|
### nextSrcDir
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Generate a 'src/' directory for Next.js
|
||||||
|
|
||||||
### nxCloud
|
### nxCloud
|
||||||
|
|
||||||
Type: `boolean`
|
Type: `boolean`
|
||||||
|
|||||||
@ -124,6 +124,12 @@
|
|||||||
"description": "Enable the App Router for this project.",
|
"description": "Enable the App Router for this project.",
|
||||||
"x-prompt": "Would you like to use the App Router (recommended)?"
|
"x-prompt": "Would you like to use the App Router (recommended)?"
|
||||||
},
|
},
|
||||||
|
"src": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Generate a `src` directory for the project.",
|
||||||
|
"x-prompt": "Would you like to use `src/` directory?"
|
||||||
|
},
|
||||||
"rootProject": {
|
"rootProject": {
|
||||||
"description": "Create an application at the root of the workspace.",
|
"description": "Create an application at the root of the workspace.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -119,6 +119,12 @@ Type: `boolean`
|
|||||||
|
|
||||||
Enable the App Router for Next.js
|
Enable the App Router for Next.js
|
||||||
|
|
||||||
|
### nextSrcDir
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Generate a 'src/' directory for Next.js
|
||||||
|
|
||||||
### nxCloud
|
### nxCloud
|
||||||
|
|
||||||
Type: `boolean`
|
Type: `boolean`
|
||||||
|
|||||||
@ -65,6 +65,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"nextSrcDir": {
|
||||||
|
"description": "Generate a `src` directory for this project.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
"e2eTestRunner": {
|
"e2eTestRunner": {
|
||||||
"description": "The tool to use for running e2e tests.",
|
"description": "The tool to use for running e2e tests.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -82,6 +82,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"nextSrcDir": {
|
||||||
|
"description": "Generate a `src` directory for this project.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
"e2eTestRunner": {
|
"e2eTestRunner": {
|
||||||
"description": "The tool to use for running e2e tests.",
|
"description": "The tool to use for running e2e tests.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -45,7 +45,7 @@ describe('Next.js Apps Libs', () => {
|
|||||||
const buildableLib = uniq('buildablelib');
|
const buildableLib = uniq('buildablelib');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false --src=false`
|
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
|
||||||
);
|
);
|
||||||
runCLI(`generate @nx/next:lib ${nextLib} --no-interactive`);
|
runCLI(`generate @nx/next:lib ${nextLib} --no-interactive`);
|
||||||
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
||||||
@ -100,11 +100,11 @@ describe('Next.js Apps Libs', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
const mainPath = `packages/${appName}/pages/index.tsx`;
|
const mainPath = `packages/${appName}/src/pages/index.tsx`;
|
||||||
const content = readFile(mainPath);
|
const content = readFile(mainPath);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`packages/${appName}/pages/api/hello.ts`,
|
`packages/${appName}/src/pages/api/hello.ts`,
|
||||||
`
|
`
|
||||||
import { jsLibAsync } from '@${proj}/${jsLib}';
|
import { jsLibAsync } from '@${proj}/${jsLib}';
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ describe('Next.js Applications', () => {
|
|||||||
|
|
||||||
// check files are generated without the layout directory ("apps/") and
|
// check files are generated without the layout directory ("apps/") and
|
||||||
// using the project name as the directory when no directory is provided
|
// using the project name as the directory when no directory is provided
|
||||||
checkFilesExist(`${appName}/app/page.tsx`);
|
checkFilesExist(`${appName}/src/app/page.tsx`);
|
||||||
// check build works
|
// check build works
|
||||||
expect(runCLI(`build ${appName}`)).toContain(
|
expect(runCLI(`build ${appName}`)).toContain(
|
||||||
`Successfully ran target build for project ${appName}`
|
`Successfully ran target build for project ${appName}`
|
||||||
@ -179,7 +179,7 @@ describe('Next.js Applications', () => {
|
|||||||
`generate @nx/next:app ${appName} --no-interactive --js --appDir=false`
|
`generate @nx/next:app ${appName} --no-interactive --js --appDir=false`
|
||||||
);
|
);
|
||||||
|
|
||||||
checkFilesExist(`apps/${appName}/pages/index.js`);
|
checkFilesExist(`apps/${appName}/src/pages/index.js`);
|
||||||
|
|
||||||
await checkApp(appName, {
|
await checkApp(appName, {
|
||||||
checkUnitTest: true,
|
checkUnitTest: true,
|
||||||
@ -195,7 +195,7 @@ describe('Next.js Applications', () => {
|
|||||||
`generate @nx/next:lib ${libName} --no-interactive --style=none --js`
|
`generate @nx/next:lib ${libName} --no-interactive --style=none --js`
|
||||||
);
|
);
|
||||||
|
|
||||||
const mainPath = `apps/${appName}/pages/index.js`;
|
const mainPath = `apps/${appName}/src/pages/index.js`;
|
||||||
updateFile(
|
updateFile(
|
||||||
mainPath,
|
mainPath,
|
||||||
`import '@${proj}/${libName}';\n` + readFile(mainPath)
|
`import '@${proj}/${libName}';\n` + readFile(mainPath)
|
||||||
|
|||||||
@ -108,7 +108,9 @@ function addBabelSupport(path: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createAppWithCt(appName: string) {
|
function createAppWithCt(appName: string) {
|
||||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --appDir=false`);
|
runCLI(
|
||||||
|
`generate @nx/next:app ${appName} --no-interactive --appDir=false --src=false`
|
||||||
|
);
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/next:component button --project=${appName} --directory=components --flat --no-interactive`
|
`generate @nx/next:component button --project=${appName} --directory=components --flat --no-interactive`
|
||||||
);
|
);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ describe('Next.js Styles', () => {
|
|||||||
const lessApp = uniq('app');
|
const lessApp = uniq('app');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/next:app ${lessApp} --no-interactive --style=less --appDir=false`
|
`generate @nx/next:app ${lessApp} --no-interactive --style=less --appDir=false --src=false`
|
||||||
);
|
);
|
||||||
|
|
||||||
await checkApp(lessApp, {
|
await checkApp(lessApp, {
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
getPackageManagerCommand,
|
getPackageManagerCommand,
|
||||||
getSelectedPackageManager,
|
getSelectedPackageManager,
|
||||||
runCommand,
|
runCommand,
|
||||||
|
runE2ETests,
|
||||||
} from '@nx/e2e/utils';
|
} from '@nx/e2e/utils';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
@ -41,7 +42,9 @@ describe('@nx/workspace:convert-to-monorepo', () => {
|
|||||||
expect(() => runCLI(`test ${reactApp}`)).not.toThrow();
|
expect(() => runCLI(`test ${reactApp}`)).not.toThrow();
|
||||||
expect(() => runCLI(`lint ${reactApp}`)).not.toThrow();
|
expect(() => runCLI(`lint ${reactApp}`)).not.toThrow();
|
||||||
expect(() => runCLI(`lint e2e`)).not.toThrow();
|
expect(() => runCLI(`lint e2e`)).not.toThrow();
|
||||||
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
if (runE2ETests()) {
|
||||||
|
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be convert a standalone vite and playwright react project to a monorepo', async () => {
|
it('should be convert a standalone vite and playwright react project to a monorepo', async () => {
|
||||||
@ -61,7 +64,9 @@ describe('@nx/workspace:convert-to-monorepo', () => {
|
|||||||
expect(() => runCLI(`test ${reactApp}`)).not.toThrow();
|
expect(() => runCLI(`test ${reactApp}`)).not.toThrow();
|
||||||
expect(() => runCLI(`lint ${reactApp}`)).not.toThrow();
|
expect(() => runCLI(`lint ${reactApp}`)).not.toThrow();
|
||||||
expect(() => runCLI(`lint e2e`)).not.toThrow();
|
expect(() => runCLI(`lint e2e`)).not.toThrow();
|
||||||
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
if (runE2ETests()) {
|
||||||
|
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -229,6 +229,7 @@ export function runCreateWorkspace(
|
|||||||
standaloneApi,
|
standaloneApi,
|
||||||
docker,
|
docker,
|
||||||
nextAppDir,
|
nextAppDir,
|
||||||
|
nextSrcDir,
|
||||||
e2eTestRunner,
|
e2eTestRunner,
|
||||||
ssr,
|
ssr,
|
||||||
framework,
|
framework,
|
||||||
@ -247,6 +248,7 @@ export function runCreateWorkspace(
|
|||||||
routing?: boolean;
|
routing?: boolean;
|
||||||
docker?: boolean;
|
docker?: boolean;
|
||||||
nextAppDir?: boolean;
|
nextAppDir?: boolean;
|
||||||
|
nextSrcDir?: boolean;
|
||||||
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
||||||
ssr?: boolean;
|
ssr?: boolean;
|
||||||
framework?: string;
|
framework?: string;
|
||||||
@ -275,6 +277,10 @@ export function runCreateWorkspace(
|
|||||||
command += ` --nextAppDir=${nextAppDir}`;
|
command += ` --nextAppDir=${nextAppDir}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nextSrcDir !== undefined) {
|
||||||
|
command += ` --nextSrcDir=${nextSrcDir}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (docker !== undefined) {
|
if (docker !== undefined) {
|
||||||
command += ` --docker=${docker}`;
|
command += ` --docker=${docker}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -223,11 +223,12 @@ describe('create-nx-workspace', () => {
|
|||||||
style: 'css',
|
style: 'css',
|
||||||
appName,
|
appName,
|
||||||
nextAppDir: false,
|
nextAppDir: false,
|
||||||
|
nextSrcDir: true,
|
||||||
packageManager,
|
packageManager,
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilesExist(`apps/${appName}/pages/index.tsx`);
|
checkFilesExist(`apps/${appName}/src/pages/index.tsx`);
|
||||||
|
|
||||||
expectNoAngularDevkit();
|
expectNoAngularDevkit();
|
||||||
expectCodeIsFormatted();
|
expectCodeIsFormatted();
|
||||||
@ -240,12 +241,13 @@ describe('create-nx-workspace', () => {
|
|||||||
preset: 'nextjs-standalone',
|
preset: 'nextjs-standalone',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
nextAppDir: true,
|
nextAppDir: true,
|
||||||
|
nextSrcDir: true,
|
||||||
appName,
|
appName,
|
||||||
packageManager,
|
packageManager,
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilesExist('app/page.tsx');
|
checkFilesExist('src/app/page.tsx');
|
||||||
|
|
||||||
expectNoAngularDevkit();
|
expectNoAngularDevkit();
|
||||||
expectCodeIsFormatted();
|
expectCodeIsFormatted();
|
||||||
@ -258,12 +260,13 @@ describe('create-nx-workspace', () => {
|
|||||||
preset: 'nextjs-standalone',
|
preset: 'nextjs-standalone',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
nextAppDir: false,
|
nextAppDir: false,
|
||||||
|
nextSrcDir: true,
|
||||||
appName,
|
appName,
|
||||||
packageManager,
|
packageManager,
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilesExist('pages/index.tsx');
|
checkFilesExist('src/pages/index.tsx');
|
||||||
|
|
||||||
expectNoAngularDevkit();
|
expectNoAngularDevkit();
|
||||||
expectCodeIsFormatted();
|
expectCodeIsFormatted();
|
||||||
|
|||||||
@ -49,6 +49,7 @@ interface ReactArguments extends BaseArguments {
|
|||||||
style: string;
|
style: string;
|
||||||
bundler: 'webpack' | 'vite' | 'rspack';
|
bundler: 'webpack' | 'vite' | 'rspack';
|
||||||
nextAppDir: boolean;
|
nextAppDir: boolean;
|
||||||
|
nextSrcDir: boolean;
|
||||||
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +165,10 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
|||||||
describe: chalk.dim`Enable the App Router for Next.js`,
|
describe: chalk.dim`Enable the App Router for Next.js`,
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
})
|
})
|
||||||
|
.option('nextSrcDir', {
|
||||||
|
describe: chalk.dim`Generate a 'src/' directory for Next.js`,
|
||||||
|
type: 'boolean',
|
||||||
|
})
|
||||||
.option('e2eTestRunner', {
|
.option('e2eTestRunner', {
|
||||||
describe: chalk.dim`Test runner to use for end to end (E2E) tests.`,
|
describe: chalk.dim`Test runner to use for end to end (E2E) tests.`,
|
||||||
choices: ['cypress', 'playwright', 'none'],
|
choices: ['cypress', 'playwright', 'none'],
|
||||||
@ -512,6 +517,7 @@ async function determineReactOptions(
|
|||||||
let bundler: undefined | 'webpack' | 'vite' | 'rspack' = undefined;
|
let bundler: undefined | 'webpack' | 'vite' | 'rspack' = undefined;
|
||||||
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
|
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
|
||||||
let nextAppDir = false;
|
let nextAppDir = false;
|
||||||
|
let nextSrcDir = false;
|
||||||
|
|
||||||
if (parsedArgs.preset && parsedArgs.preset !== Preset.React) {
|
if (parsedArgs.preset && parsedArgs.preset !== Preset.React) {
|
||||||
preset = parsedArgs.preset;
|
preset = parsedArgs.preset;
|
||||||
@ -563,6 +569,7 @@ async function determineReactOptions(
|
|||||||
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
||||||
} else if (preset === Preset.NextJs || preset === Preset.NextJsStandalone) {
|
} else if (preset === Preset.NextJs || preset === Preset.NextJsStandalone) {
|
||||||
nextAppDir = await determineNextAppDir(parsedArgs);
|
nextAppDir = await determineNextAppDir(parsedArgs);
|
||||||
|
nextSrcDir = await determineNextSrcDir(parsedArgs);
|
||||||
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +621,15 @@ async function determineReactOptions(
|
|||||||
style = reply.style;
|
style = reply.style;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { preset, style, appName, bundler, nextAppDir, e2eTestRunner };
|
return {
|
||||||
|
preset,
|
||||||
|
style,
|
||||||
|
appName,
|
||||||
|
bundler,
|
||||||
|
nextAppDir,
|
||||||
|
nextSrcDir,
|
||||||
|
e2eTestRunner,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function determineVueOptions(
|
async function determineVueOptions(
|
||||||
@ -1066,6 +1081,29 @@ async function determineNextAppDir(
|
|||||||
return reply.nextAppDir === 'Yes';
|
return reply.nextAppDir === 'Yes';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function determineNextSrcDir(
|
||||||
|
parsedArgs: yargs.Arguments<ReactArguments>
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (parsedArgs.nextSrcDir !== undefined) return parsedArgs.nextSrcDir;
|
||||||
|
const reply = await enquirer.prompt<{ nextSrcDir: 'Yes' | 'No' }>([
|
||||||
|
{
|
||||||
|
name: 'nextSrcDir',
|
||||||
|
message: 'Would you like to use the src/ directory?',
|
||||||
|
type: 'autocomplete',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Yes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'No',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initial: 'Yes' as any,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return reply.nextSrcDir === 'Yes';
|
||||||
|
}
|
||||||
|
|
||||||
async function determineVueFramework(
|
async function determineVueFramework(
|
||||||
parsedArgs: yargs.Arguments<VueArguments>
|
parsedArgs: yargs.Arguments<VueArguments>
|
||||||
): Promise<'none' | 'nuxt'> {
|
): Promise<'none' | 'nuxt'> {
|
||||||
|
|||||||
@ -82,12 +82,12 @@ describe('app', () => {
|
|||||||
`../dist/${name}/.next/types/**/*.ts`,
|
`../dist/${name}/.next/types/**/*.ts`,
|
||||||
'next-env.d.ts',
|
'next-env.d.ts',
|
||||||
]);
|
]);
|
||||||
expect(tree.exists(`${name}/pages/styles.css`)).toBeFalsy();
|
expect(tree.exists(`${name}/src/pages/styles.css`)).toBeFalsy();
|
||||||
expect(tree.exists(`${name}/app/global.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/page.tsx`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/page.tsx`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/layout.tsx`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/layout.tsx`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/api/hello/route.ts`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/api/hello/route.ts`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/page.module.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/page.module.css`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/public/favicon.ico`)).toBeTruthy();
|
expect(tree.exists(`${name}/public/favicon.ico`)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ describe('app', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
const content = tree.read('app/page.tsx').toString();
|
const content = tree.read('src/app/page.tsx').toString();
|
||||||
|
|
||||||
expect(content).not.toContain('import styles from');
|
expect(content).not.toContain('import styles from');
|
||||||
expect(content).not.toContain('const StyledPage');
|
expect(content).not.toContain('const StyledPage');
|
||||||
@ -138,6 +138,7 @@ describe('app', () => {
|
|||||||
name,
|
name,
|
||||||
style: 'css',
|
style: 'css',
|
||||||
appDir: false,
|
appDir: false,
|
||||||
|
src: false,
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
expect(tree.exists(`${name}/tsconfig.json`)).toBeTruthy();
|
expect(tree.exists(`${name}/tsconfig.json`)).toBeTruthy();
|
||||||
@ -166,6 +167,7 @@ describe('app', () => {
|
|||||||
name,
|
name,
|
||||||
style: 'none',
|
style: 'none',
|
||||||
appDir: false,
|
appDir: false,
|
||||||
|
src: false,
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -186,12 +188,12 @@ describe('app', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists(`${name}/app/page.module.scss`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/page.module.scss`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/global.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||||
|
|
||||||
const indexContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
expect(indexContent).toContain(`import styles from './page.module.scss'`);
|
expect(indexContent).toContain(`import styles from './page.module.scss'`);
|
||||||
expect(tree.read(`${name}/app/layout.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"import './global.css';
|
"import './global.css';
|
||||||
|
|
||||||
@ -225,12 +227,12 @@ describe('app', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists(`${name}/app/page.module.less`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/page.module.less`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/app/global.less`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.less`)).toBeTruthy();
|
||||||
|
|
||||||
const indexContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
expect(indexContent).toContain(`import styles from './page.module.less'`);
|
expect(indexContent).toContain(`import styles from './page.module.less'`);
|
||||||
expect(tree.read(`${name}/app/layout.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"import './global.less';
|
"import './global.less';
|
||||||
|
|
||||||
@ -265,14 +267,14 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists(`${name}/app/page.module.styled-components`)
|
tree.exists(`${name}/src/app/page.module.styled-components`)
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
expect(tree.exists(`${name}/app/global.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||||
|
|
||||||
const indexContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||||
expect(indexContent).toContain(`import styled from 'styled-components'`);
|
expect(indexContent).toContain(`import styled from 'styled-components'`);
|
||||||
expect(tree.read(`${name}/app/layout.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"import './global.css';
|
"import './global.css';
|
||||||
import { StyledComponentsRegistry } from './registry';
|
import { StyledComponentsRegistry } from './registry';
|
||||||
@ -297,7 +299,7 @@ describe('app', () => {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
expect(tree.read(`${name}/app/registry.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/registry.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"'use client';
|
"'use client';
|
||||||
|
|
||||||
@ -348,14 +350,14 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists(`${name}/app/page.module.styled-components`)
|
tree.exists(`${name}/src/app/page.module.styled-components`)
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
expect(tree.exists(`${name}/app/global.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||||
|
|
||||||
const indexContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||||
expect(indexContent).toContain(`import styled from '@emotion/styled'`);
|
expect(indexContent).toContain(`import styled from '@emotion/styled'`);
|
||||||
expect(tree.read(`${name}/app/layout.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"import './global.css';
|
"import './global.css';
|
||||||
|
|
||||||
@ -406,17 +408,17 @@ describe('app', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
const indexContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
|
|
||||||
expect(indexContent).toMatchSnapshot();
|
expect(indexContent).toMatchSnapshot();
|
||||||
expect(tree.exists(`${name}/app/page.module.styled-jsx`)).toBeFalsy();
|
expect(tree.exists(`${name}/src/app/page.module.styled-jsx`)).toBeFalsy();
|
||||||
expect(tree.exists(`${name}/app/global.css`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||||
|
|
||||||
expect(indexContent).not.toContain(`import styles from './page.module`);
|
expect(indexContent).not.toContain(`import styles from './page.module`);
|
||||||
expect(indexContent).not.toContain(
|
expect(indexContent).not.toContain(
|
||||||
`import styled from 'styled-components'`
|
`import styled from 'styled-components'`
|
||||||
);
|
);
|
||||||
expect(tree.read(`${name}/app/layout.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"import './global.css';
|
"import './global.css';
|
||||||
import { StyledJsxRegistry } from './registry';
|
import { StyledJsxRegistry } from './registry';
|
||||||
@ -436,7 +438,7 @@ describe('app', () => {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
expect(tree.read(`${name}/app/registry.tsx`, 'utf-8'))
|
expect(tree.read(`${name}/src/app/registry.tsx`, 'utf-8'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"'use client';
|
"'use client';
|
||||||
|
|
||||||
@ -588,7 +590,7 @@ describe('app', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
const appContent = tree.read(`${name}/app/page.tsx`, 'utf-8');
|
const appContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||||
|
|
||||||
expect(appContent).not.toMatch(/extends Component/);
|
expect(appContent).not.toMatch(/extends Component/);
|
||||||
});
|
});
|
||||||
@ -709,7 +711,7 @@ describe('app', () => {
|
|||||||
js: true,
|
js: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists(`${name}/app/page.js`)).toBeTruthy();
|
expect(tree.exists(`${name}/src/app/page.js`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/specs/index.spec.js`)).toBeTruthy();
|
expect(tree.exists(`${name}/specs/index.spec.js`)).toBeTruthy();
|
||||||
expect(tree.exists(`${name}/index.d.js`)).toBeFalsy();
|
expect(tree.exists(`${name}/index.d.js`)).toBeFalsy();
|
||||||
expect(tree.exists(`${name}/index.d.ts`)).toBeFalsy();
|
expect(tree.exists(`${name}/index.d.ts`)).toBeFalsy();
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
<% if (src) { %>
|
||||||
|
import Index from '../src/pages/index';
|
||||||
|
<% } else { %>
|
||||||
import Index from '../pages/index';
|
import Index from '../pages/index';
|
||||||
|
<% } %>
|
||||||
describe('Index', () => {
|
describe('Index', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', () => {
|
||||||
const { baseElement } = render(<Index />);
|
const { baseElement } = render(<Index />);
|
||||||
|
|||||||
@ -48,6 +48,10 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
stylesExt: options.style === 'less' ? options.style : 'css',
|
stylesExt: options.style === 'less' ? options.style : 'css',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generatedAppFilePath = options.src
|
||||||
|
? join(options.appProjectRoot, 'src')
|
||||||
|
: options.appProjectRoot;
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/common'),
|
join(__dirname, '../files/common'),
|
||||||
@ -59,7 +63,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/app'),
|
join(__dirname, '../files/app'),
|
||||||
join(options.appProjectRoot, 'app'),
|
join(generatedAppFilePath, 'app'),
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -76,21 +80,21 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/app-styled-components'),
|
join(__dirname, '../files/app-styled-components'),
|
||||||
join(options.appProjectRoot, 'app'),
|
join(generatedAppFilePath, 'app'),
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
} else if (options.style === 'styled-jsx') {
|
} else if (options.style === 'styled-jsx') {
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/app-styled-jsx'),
|
join(__dirname, '../files/app-styled-jsx'),
|
||||||
join(options.appProjectRoot, 'app'),
|
join(generatedAppFilePath, 'app'),
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/app-default-layout'),
|
join(__dirname, '../files/app-default-layout'),
|
||||||
join(options.appProjectRoot, 'app'),
|
join(generatedAppFilePath, 'app'),
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -98,7 +102,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, '../files/pages'),
|
join(__dirname, '../files/pages'),
|
||||||
join(options.appProjectRoot, 'pages'),
|
join(generatedAppFilePath, 'pages'),
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -151,16 +155,16 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
|
|
||||||
if (options.styledModule) {
|
if (options.styledModule) {
|
||||||
if (options.appDir) {
|
if (options.appDir) {
|
||||||
host.delete(`${options.appProjectRoot}/app/page.module.${options.style}`);
|
host.delete(`${generatedAppFilePath}/app/page.module.${options.style}`);
|
||||||
} else {
|
} else {
|
||||||
host.delete(
|
host.delete(
|
||||||
`${options.appProjectRoot}/pages/${options.fileName}.module.${options.style}`
|
`${generatedAppFilePath}/pages/${options.fileName}.module.${options.style}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.style !== 'styled-components') {
|
if (options.style !== 'styled-components') {
|
||||||
host.delete(`${options.appProjectRoot}/pages/_document.tsx`);
|
host.delete(`${generatedAppFilePath}/pages/_document.tsx`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.js) {
|
if (options.js) {
|
||||||
|
|||||||
@ -53,6 +53,7 @@ export async function normalizeOptions(
|
|||||||
const fileName = 'index';
|
const fileName = 'index';
|
||||||
|
|
||||||
const appDir = options.appDir ?? true;
|
const appDir = options.appDir ?? true;
|
||||||
|
const src = options.src ?? true;
|
||||||
|
|
||||||
const styledModule = /^(css|scss|less)$/.test(options.style)
|
const styledModule = /^(css|scss|less)$/.test(options.style)
|
||||||
? null
|
? null
|
||||||
@ -63,6 +64,7 @@ export async function normalizeOptions(
|
|||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
appDir,
|
appDir,
|
||||||
|
src,
|
||||||
appProjectRoot,
|
appProjectRoot,
|
||||||
e2eProjectName,
|
e2eProjectName,
|
||||||
e2eProjectRoot,
|
e2eProjectRoot,
|
||||||
|
|||||||
@ -18,5 +18,6 @@ export interface Schema {
|
|||||||
customServer?: boolean;
|
customServer?: boolean;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
appDir?: boolean;
|
appDir?: boolean;
|
||||||
|
src?: boolean;
|
||||||
rootProject?: boolean;
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,6 +127,12 @@
|
|||||||
"description": "Enable the App Router for this project.",
|
"description": "Enable the App Router for this project.",
|
||||||
"x-prompt": "Would you like to use the App Router (recommended)?"
|
"x-prompt": "Would you like to use the App Router (recommended)?"
|
||||||
},
|
},
|
||||||
|
"src": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Generate a `src` directory for the project.",
|
||||||
|
"x-prompt": "Would you like to use `src/` directory?"
|
||||||
|
},
|
||||||
"rootProject": {
|
"rootProject": {
|
||||||
"description": "Create an application at the root of the workspace.",
|
"description": "Create an application at the root of the workspace.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -111,6 +111,7 @@ describe('monorepo generator', () => {
|
|||||||
unitTestRunner: 'jest',
|
unitTestRunner: 'jest',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
appDir: true,
|
appDir: true,
|
||||||
|
src: true,
|
||||||
linter: 'eslint',
|
linter: 'eslint',
|
||||||
rootProject: true,
|
rootProject: true,
|
||||||
});
|
});
|
||||||
@ -121,7 +122,7 @@ describe('monorepo generator', () => {
|
|||||||
expect(readProjectConfiguration(tree, 'demo')).toMatchObject({
|
expect(readProjectConfiguration(tree, 'demo')).toMatchObject({
|
||||||
sourceRoot: 'apps/demo',
|
sourceRoot: 'apps/demo',
|
||||||
});
|
});
|
||||||
expect(tree.read('apps/demo/app/page.tsx', 'utf-8')).toContain('demo');
|
expect(tree.read('apps/demo/src/app/page.tsx', 'utf-8')).toContain('demo');
|
||||||
expect(readProjectConfiguration(tree, 'util')).toMatchObject({
|
expect(readProjectConfiguration(tree, 'util')).toMatchObject({
|
||||||
sourceRoot: 'libs/util/src',
|
sourceRoot: 'libs/util/src',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -70,6 +70,7 @@ export function generatePreset(host: Tree, opts: NormalizedSchema) {
|
|||||||
opts.docker ? `--docker=${opts.docker}` : null,
|
opts.docker ? `--docker=${opts.docker}` : null,
|
||||||
opts.js ? `--js` : null,
|
opts.js ? `--js` : null,
|
||||||
opts.nextAppDir ? '--nextAppDir=true' : '--nextAppDir=false',
|
opts.nextAppDir ? '--nextAppDir=true' : '--nextAppDir=false',
|
||||||
|
opts.nextSrcDir ? '--nextSrcDir=true' : '--nextSrcDir=false',
|
||||||
opts.packageManager ? `--packageManager=${opts.packageManager}` : null,
|
opts.packageManager ? `--packageManager=${opts.packageManager}` : null,
|
||||||
opts.standaloneApi !== undefined
|
opts.standaloneApi !== undefined
|
||||||
? `--standaloneApi=${opts.standaloneApi}`
|
? `--standaloneApi=${opts.standaloneApi}`
|
||||||
|
|||||||
@ -26,6 +26,7 @@ interface Schema {
|
|||||||
docker?: boolean;
|
docker?: boolean;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
nextAppDir?: boolean;
|
nextAppDir?: boolean;
|
||||||
|
nextSrcDir?: boolean;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
bundler?: 'vite' | 'webpack';
|
bundler?: 'vite' | 'webpack';
|
||||||
standaloneApi?: boolean;
|
standaloneApi?: boolean;
|
||||||
|
|||||||
@ -68,6 +68,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"nextSrcDir": {
|
||||||
|
"description": "Generate a `src` directory for this project.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
"e2eTestRunner": {
|
"e2eTestRunner": {
|
||||||
"description": "The tool to use for running e2e tests.",
|
"description": "The tool to use for running e2e tests.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -70,7 +70,7 @@ describe('preset', () => {
|
|||||||
style: 'css',
|
style: 'css',
|
||||||
linter: 'eslint',
|
linter: 'eslint',
|
||||||
});
|
});
|
||||||
expect(tree.exists('/apps/proj/app/page.tsx')).toBe(true);
|
expect(tree.exists('/apps/proj/src/app/page.tsx')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should create files (preset = ${Preset.Express})`, async () => {
|
it(`should create files (preset = ${Preset.Express})`, async () => {
|
||||||
|
|||||||
@ -143,6 +143,7 @@ async function createPreset(tree: Tree, options: Schema) {
|
|||||||
style: options.style,
|
style: options.style,
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
appDir: options.nextAppDir,
|
appDir: options.nextAppDir,
|
||||||
|
src: options.nextSrcDir,
|
||||||
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
|
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
|
||||||
});
|
});
|
||||||
} else if (options.preset === Preset.NextJsStandalone) {
|
} else if (options.preset === Preset.NextJsStandalone) {
|
||||||
@ -155,6 +156,7 @@ async function createPreset(tree: Tree, options: Schema) {
|
|||||||
style: options.style,
|
style: options.style,
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
appDir: options.nextAppDir,
|
appDir: options.nextAppDir,
|
||||||
|
src: options.nextSrcDir,
|
||||||
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
|
e2eTestRunner: options.e2eTestRunner ?? 'cypress',
|
||||||
rootProject: true,
|
rootProject: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export interface Schema {
|
|||||||
bundler?: 'vite' | 'webpack' | 'rspack' | 'esbuild';
|
bundler?: 'vite' | 'webpack' | 'rspack' | 'esbuild';
|
||||||
docker?: boolean;
|
docker?: boolean;
|
||||||
nextAppDir?: boolean;
|
nextAppDir?: boolean;
|
||||||
|
nextSrcDir?: boolean;
|
||||||
routing?: boolean;
|
routing?: boolean;
|
||||||
standaloneApi?: boolean;
|
standaloneApi?: boolean;
|
||||||
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
||||||
|
|||||||
@ -85,6 +85,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"nextSrcDir": {
|
||||||
|
"description": "Generate a `src` directory for this project.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
"e2eTestRunner": {
|
"e2eTestRunner": {
|
||||||
"description": "The tool to use for running e2e tests.",
|
"description": "The tool to use for running e2e tests.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user