feat(testing): cypress vite (#13474)
This commit is contained in:
parent
92d33f9539
commit
1ef01f8ccc
@ -111,10 +111,23 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Do not add dependencies to `package.json`."
|
"description": "Do not add dependencies to `package.json`."
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "Create a application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The Cypress bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["vite", "webpack", "none"],
|
||||||
|
"x-prompt": "Which Cypress bundler do you want to use?",
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
"examplesFile": "Adding Cypress to an existing application requires two options. The name of the e2e app to create and what project that e2e app is for.\n\n```bash\nnx g cypress-project --name=my-app-e2e --project=my-app\n```\n\nWhen providing `--project` option, the generator will look for the `serve` target in that given project. This allows the [cypress executor](/packages/cypress/executors/cypress) to spin up the project and start the cypress runner.\n\nIf you prefer to not have the project served automatically, you can provide a `--base-url` argument in place of `--project`\n\n```bash\nnx g cypress-project --name=my-app-e2e --base-url=http://localhost:1234\n```\n\n{% callout type=\"note\" title=\"What about API Projects?\" %}\nYou can also run the `cypress-project` generator against API projects like a [Nest API](/packages/nest/generators/application#@nrwl/nest:application).\nIf there is a URL to visit then you can test it with Cypress!\n{% /callout %}\n",
|
"examplesFile": "Adding Cypress to an existing application requires two options. The name of the e2e app to create and what project that e2e app is for.\n\n```bash\nnx g cypress-project --name=my-app-e2e --project=my-app\n```\n\nWhen providing `--project` option, the generator will look for the `serve` target in that given project. This allows the [cypress executor](/packages/cypress/executors/cypress) to spin up the project and start the cypress runner.\n\nIf you prefer to not have the project served automatically, you can provide a `--base-url` argument in place of `--project`\n\n```bash\nnx g cypress-project --name=my-app-e2e --base-url=http://localhost:1234\n```\n\n{% callout type=\"note\" title=\"What about API Projects?\" %}\nYou can also run the `cypress-project` generator against API projects like a [Nest API](/packages/nest/generators/application#@nrwl/nest:application).\nIf there is a URL to visit then you can test it with Cypress!\n{% /callout %}\n\n## Using Cypress with Vite.js\n\nNow, you can generate your Cypress project with Vite.js as the bundler:\n\n```bash\nnx g cypress-project --name=my-app-e2e --project=my-app --bundler=vite\n```\n\nThis generator will pass the `bundler` information (`bundler: 'vite'`) to our `nxE2EPreset`, in your project's `cypress.config.ts` file (eg. `my-app-e2e/cypress.config.ts`).\n\n### Customizing the Vite.js configuration\n\nThe `nxE2EPreset` will then use the `bundler` information to generate the correct settings for your Cypress project to use Vite.js. In the background, the way this works is that it's using a custom Vite preprocessor for your files, that's called on the `file:preprocessor` event. If you want to customize this behaviour, you can do so like this in your project's `cypress.config.ts` file:\n\n```ts\nimport { defineConfig } from 'cypress';\nimport { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';\n\nconst config = nxE2EPreset(__filename, { bundler: 'vite' });\nexport default defineConfig({\n e2e: {\n ...config,\n setupNodeEvents(on, config): {\n config.setupNodeEvents(on);\n // Your settings here\n }\n },\n});\n```\n",
|
||||||
"presets": []
|
"presets": []
|
||||||
},
|
},
|
||||||
"description": "Add a Cypress E2E Project.",
|
"description": "Add a Cypress E2E Project.",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -114,6 +114,7 @@
|
|||||||
},
|
},
|
||||||
"bundler": {
|
"bundler": {
|
||||||
"description": "The bundler to use.",
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
"enum": ["vite", "webpack"],
|
"enum": ["vite", "webpack"],
|
||||||
"x-prompt": "Which bundler do you want to use?",
|
"x-prompt": "Which bundler do you want to use?",
|
||||||
"default": "webpack"
|
"default": "webpack"
|
||||||
@ -196,7 +197,10 @@
|
|||||||
},
|
},
|
||||||
"bundler": {
|
"bundler": {
|
||||||
"description": "The Storybook builder to use.",
|
"description": "The Storybook builder to use.",
|
||||||
"enum": ["vite", "webpack"]
|
"type": "string",
|
||||||
|
"enum": ["vite", "webpack"],
|
||||||
|
"x-prompt": "Which Storybook builder do you want to use?",
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -85,7 +85,7 @@ ${e.stack ? e.stack : e}`
|
|||||||
const offset = offsetFromRoot(normalizedFromWorkspaceRootPath);
|
const offset = offsetFromRoot(normalizedFromWorkspaceRootPath);
|
||||||
const buildContext = createExecutorContext(
|
const buildContext = createExecutorContext(
|
||||||
graph,
|
graph,
|
||||||
graph.nodes[buildTarget.project].data.targets,
|
graph.nodes[buildTarget.project]?.data.targets,
|
||||||
buildTarget.project,
|
buildTarget.project,
|
||||||
buildTarget.target,
|
buildTarget.target,
|
||||||
buildTarget.configuration
|
buildTarget.configuration
|
||||||
@ -117,7 +117,7 @@ ${e.stack ? e.stack : e}`
|
|||||||
|
|
||||||
function getBuildableTarget(ctContext: ExecutorContext) {
|
function getBuildableTarget(ctContext: ExecutorContext) {
|
||||||
const targets =
|
const targets =
|
||||||
ctContext.projectGraph.nodes[ctContext.projectName].data?.targets;
|
ctContext.projectGraph.nodes[ctContext.projectName]?.data?.targets;
|
||||||
const targetConfig = targets?.[ctContext.targetName];
|
const targetConfig = targets?.[ctContext.targetName];
|
||||||
|
|
||||||
if (!targetConfig) {
|
if (!targetConfig) {
|
||||||
@ -232,7 +232,7 @@ function normalizeBuildTargetOptions(
|
|||||||
buildOptions.stylePreprocessorOptions = { includePaths: [] };
|
buildOptions.stylePreprocessorOptions = { includePaths: [] };
|
||||||
}
|
}
|
||||||
const { root, sourceRoot } =
|
const { root, sourceRoot } =
|
||||||
buildContext.projectGraph.nodes[buildContext.projectName].data;
|
buildContext.projectGraph.nodes[buildContext.projectName]?.data;
|
||||||
return {
|
return {
|
||||||
root: joinPathFragments(offset, root),
|
root: joinPathFragments(offset, root),
|
||||||
sourceRoot: joinPathFragments(offset, sourceRoot),
|
sourceRoot: joinPathFragments(offset, sourceRoot),
|
||||||
@ -280,7 +280,7 @@ function withSchemaDefaults(options: any): BrowserBuilderSchema {
|
|||||||
function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
|
function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
|
||||||
const ctProjectConfig = ctExecutorContext.projectGraph.nodes[
|
const ctProjectConfig = ctExecutorContext.projectGraph.nodes[
|
||||||
ctExecutorContext.projectName
|
ctExecutorContext.projectName
|
||||||
].data as ProjectConfiguration;
|
]?.data as ProjectConfiguration;
|
||||||
// angular only supports `tailwind.config.{js,cjs}`
|
// angular only supports `tailwind.config.{js,cjs}`
|
||||||
const ctProjectTailwindConfig = join(
|
const ctProjectTailwindConfig = join(
|
||||||
ctExecutorContext.root,
|
ctExecutorContext.root,
|
||||||
|
|||||||
@ -13,6 +13,15 @@ export default defineConfig({
|
|||||||
});"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`e2e migrator cypress with project root at "" cypress version >=10 should create a cypress.config.ts file when it does not exist 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__dirname)
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`e2e migrator cypress with project root at "" cypress version >=10 should keep paths in the e2e config when they differ from the nx preset defaults 1`] = `
|
exports[`e2e migrator cypress with project root at "" cypress version >=10 should keep paths in the e2e config when they differ from the nx preset defaults 1`] = `
|
||||||
"import { defineConfig } from 'cypress';
|
"import { defineConfig } from 'cypress';
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
@ -41,11 +50,11 @@ exports[`e2e migrator cypress with project root at "" cypress version >=10 shoul
|
|||||||
"import { defineConfig } from 'cypress';
|
"import { defineConfig } from 'cypress';
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: {...nxE2EPreset(__dirname),
|
e2e: {...nxE2EPreset(__dirname),
|
||||||
baseUrl: 'http://localhost:4200'
|
baseUrl: 'http://localhost:4200'
|
||||||
},
|
},
|
||||||
});"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`e2e migrator cypress with project root at "" cypress version >=10 should update paths in the config 1`] = `
|
exports[`e2e migrator cypress with project root at "" cypress version >=10 should update paths in the config 1`] = `
|
||||||
@ -79,6 +88,15 @@ export default defineConfig({
|
|||||||
});"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`e2e migrator cypress with project root at "projects/app1" cypress version >=10 should create a cypress.config.ts file when it does not exist 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__dirname)
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`e2e migrator cypress with project root at "projects/app1" cypress version >=10 should keep paths in the e2e config when they differ from the nx preset defaults 1`] = `
|
exports[`e2e migrator cypress with project root at "projects/app1" cypress version >=10 should keep paths in the e2e config when they differ from the nx preset defaults 1`] = `
|
||||||
"import { defineConfig } from 'cypress';
|
"import { defineConfig } from 'cypress';
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
@ -107,11 +125,11 @@ exports[`e2e migrator cypress with project root at "projects/app1" cypress versi
|
|||||||
"import { defineConfig } from 'cypress';
|
"import { defineConfig } from 'cypress';
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: {...nxE2EPreset(__dirname),
|
e2e: {...nxE2EPreset(__dirname),
|
||||||
baseUrl: 'http://localhost:4200'
|
baseUrl: 'http://localhost:4200'
|
||||||
},
|
},
|
||||||
});"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`e2e migrator cypress with project root at "projects/app1" cypress version >=10 should update paths in the config 1`] = `
|
exports[`e2e migrator cypress with project root at "projects/app1" cypress version >=10 should update paths in the config 1`] = `
|
||||||
|
|||||||
@ -840,13 +840,8 @@ describe('e2e migrator', () => {
|
|||||||
'apps/app1-e2e/cypress.config.ts',
|
'apps/app1-e2e/cypress.config.ts',
|
||||||
'utf-8'
|
'utf-8'
|
||||||
);
|
);
|
||||||
expect(cypressConfig).toBe(`import { defineConfig } from 'cypress';
|
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
|
||||||
|
|
||||||
export default defineConfig({
|
expect(cypressConfig).toMatchSnapshot();
|
||||||
e2e: nxE2EPreset(__dirname)
|
|
||||||
});
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update e2e config with the nx preset', async () => {
|
it('should update e2e config with the nx preset', async () => {
|
||||||
@ -856,11 +851,11 @@ export default defineConfig({
|
|||||||
joinPathFragments(root, 'cypress.config.ts'),
|
joinPathFragments(root, 'cypress.config.ts'),
|
||||||
`import { defineConfig } from 'cypress';
|
`import { defineConfig } from 'cypress';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost:4200'
|
baseUrl: 'http://localhost:4200'
|
||||||
},
|
},
|
||||||
});`
|
});`
|
||||||
);
|
);
|
||||||
const project = addProject('app1', {
|
const project = addProject('app1', {
|
||||||
root,
|
root,
|
||||||
|
|||||||
@ -16,3 +16,33 @@ nx g cypress-project --name=my-app-e2e --base-url=http://localhost:1234
|
|||||||
You can also run the `cypress-project` generator against API projects like a [Nest API](/packages/nest/generators/application#@nrwl/nest:application).
|
You can also run the `cypress-project` generator against API projects like a [Nest API](/packages/nest/generators/application#@nrwl/nest:application).
|
||||||
If there is a URL to visit then you can test it with Cypress!
|
If there is a URL to visit then you can test it with Cypress!
|
||||||
{% /callout %}
|
{% /callout %}
|
||||||
|
|
||||||
|
## Using Cypress with Vite.js
|
||||||
|
|
||||||
|
Now, you can generate your Cypress project with Vite.js as the bundler:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g cypress-project --name=my-app-e2e --project=my-app --bundler=vite
|
||||||
|
```
|
||||||
|
|
||||||
|
This generator will pass the `bundler` information (`bundler: 'vite'`) to our `nxE2EPreset`, in your project's `cypress.config.ts` file (eg. `my-app-e2e/cypress.config.ts`).
|
||||||
|
|
||||||
|
### Customizing the Vite.js configuration
|
||||||
|
|
||||||
|
The `nxE2EPreset` will then use the `bundler` information to generate the correct settings for your Cypress project to use Vite.js. In the background, the way this works is that it's using a custom Vite preprocessor for your files, that's called on the `file:preprocessor` event. If you want to customize this behaviour, you can do so like this in your project's `cypress.config.ts` file:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
const config = nxE2EPreset(__filename, { bundler: 'vite' });
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...config,
|
||||||
|
setupNodeEvents(on, config): {
|
||||||
|
config.setupNodeEvents(on);
|
||||||
|
// Your settings here
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { workspaceRoot } from '@nrwl/devkit';
|
|||||||
import { dirname, join, relative } from 'path';
|
import { dirname, join, relative } from 'path';
|
||||||
import { lstatSync } from 'fs';
|
import { lstatSync } from 'fs';
|
||||||
|
|
||||||
|
import vitePreprocessor from '../src/plugins/preprocessor-vite';
|
||||||
|
|
||||||
interface BaseCypressPreset {
|
interface BaseCypressPreset {
|
||||||
videosFolder: string;
|
videosFolder: string;
|
||||||
screenshotsFolder: string;
|
screenshotsFolder: string;
|
||||||
@ -15,8 +17,10 @@ export interface NxComponentTestingOptions {
|
|||||||
* this is only when customized away from the default value of `component-test`
|
* this is only when customized away from the default value of `component-test`
|
||||||
* @example 'component-test'
|
* @example 'component-test'
|
||||||
*/
|
*/
|
||||||
ctTargetName: string;
|
ctTargetName?: string;
|
||||||
|
bundler?: 'vite' | 'webpack';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nxBaseCypressPreset(pathToConfig: string): BaseCypressPreset {
|
export function nxBaseCypressPreset(pathToConfig: string): BaseCypressPreset {
|
||||||
// prevent from placing path outside the root of the workspace
|
// prevent from placing path outside the root of the workspace
|
||||||
// if they pass in a file or directory
|
// if they pass in a file or directory
|
||||||
@ -58,12 +62,25 @@ export function nxBaseCypressPreset(pathToConfig: string): BaseCypressPreset {
|
|||||||
*
|
*
|
||||||
* @param pathToConfig will be used to construct the output paths for videos and screenshots
|
* @param pathToConfig will be used to construct the output paths for videos and screenshots
|
||||||
*/
|
*/
|
||||||
export function nxE2EPreset(pathToConfig: string) {
|
export function nxE2EPreset(
|
||||||
return {
|
pathToConfig: string,
|
||||||
|
options?: { bundler?: string }
|
||||||
|
) {
|
||||||
|
const baseConfig = {
|
||||||
...nxBaseCypressPreset(pathToConfig),
|
...nxBaseCypressPreset(pathToConfig),
|
||||||
fileServerFolder: '.',
|
fileServerFolder: '.',
|
||||||
supportFile: 'src/support/e2e.ts',
|
supportFile: 'src/support/e2e.ts',
|
||||||
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
||||||
fixturesFolder: 'src/fixtures',
|
fixturesFolder: 'src/fixtures',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options?.bundler === 'vite') {
|
||||||
|
return {
|
||||||
|
...baseConfig,
|
||||||
|
setupNodeEvents(on) {
|
||||||
|
on('file:preprocessor', vitePreprocessor());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return baseConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -193,14 +193,26 @@ Object {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Cypress Project > v10 for bundler:vite should pass the bundler info to nxE2EPreset in \`cypress.config.ts\` 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__dirname,
|
||||||
|
{
|
||||||
|
bundler: 'vite'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Cypress Project > v10 nested should set right path names in \`cypress.config.ts\` 1`] = `
|
exports[`Cypress Project > v10 nested should set right path names in \`cypress.config.ts\` 1`] = `
|
||||||
"import { defineConfig } from 'cypress';
|
"import { defineConfig } from 'cypress';
|
||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: nxE2EPreset(__dirname)
|
e2e: nxE2EPreset(__dirname)
|
||||||
});
|
});"
|
||||||
"
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Cypress Project > v10 nested should set right path names in \`tsconfig.e2e.json\` 1`] = `
|
exports[`Cypress Project > v10 nested should set right path names in \`tsconfig.e2e.json\` 1`] = `
|
||||||
@ -243,8 +255,7 @@ import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: nxE2EPreset(__dirname)
|
e2e: nxE2EPreset(__dirname)
|
||||||
});
|
});"
|
||||||
"
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Cypress Project > v10 should set right path names in \`tsconfig.e2e.json\` 1`] = `
|
exports[`Cypress Project > v10 should set right path names in \`tsconfig.e2e.json\` 1`] = `
|
||||||
|
|||||||
@ -189,6 +189,22 @@ describe('Cypress Project', () => {
|
|||||||
expect(tsConfig.extends).toBe('../../tsconfig.json');
|
expect(tsConfig.extends).toBe('../../tsconfig.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('for bundler:vite', () => {
|
||||||
|
it('should pass the bundler info to nxE2EPreset in `cypress.config.ts`', async () => {
|
||||||
|
await cypressProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
name: 'my-app-e2e',
|
||||||
|
project: 'my-app',
|
||||||
|
bundler: 'vite',
|
||||||
|
});
|
||||||
|
const cypressConfig = tree.read(
|
||||||
|
'apps/my-app-e2e/cypress.config.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(cypressConfig).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('nested', () => {
|
describe('nested', () => {
|
||||||
it('should set right path names in `cypress.config.ts`', async () => {
|
it('should set right path names in `cypress.config.ts`', async () => {
|
||||||
await cypressProjectGenerator(tree, {
|
await cypressProjectGenerator(tree, {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
extractLayoutDirectory,
|
extractLayoutDirectory,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
|
getProjects,
|
||||||
getWorkspaceLayout,
|
getWorkspaceLayout,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
logger,
|
logger,
|
||||||
@ -16,7 +17,6 @@ import {
|
|||||||
toJS,
|
toJS,
|
||||||
Tree,
|
Tree,
|
||||||
updateJson,
|
updateJson,
|
||||||
getProjects,
|
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
||||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
@ -32,6 +32,7 @@ import { filePathPrefix } from '../../utils/project-name';
|
|||||||
import {
|
import {
|
||||||
cypressVersion,
|
cypressVersion,
|
||||||
eslintPluginCypressVersion,
|
eslintPluginCypressVersion,
|
||||||
|
viteVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { cypressInitGenerator } from '../init/init';
|
import { cypressInitGenerator } from '../init/init';
|
||||||
// app
|
// app
|
||||||
@ -63,6 +64,7 @@ function createFiles(tree: Tree, options: CypressProjectSchema) {
|
|||||||
tree,
|
tree,
|
||||||
options.projectRoot
|
options.projectRoot
|
||||||
),
|
),
|
||||||
|
bundler: options.bundler,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -271,6 +273,19 @@ export async function cypressProjectGenerator(host: Tree, schema: Schema) {
|
|||||||
if (!cypressVersion) {
|
if (!cypressVersion) {
|
||||||
tasks.push(cypressInitGenerator(host, options));
|
tasks.push(cypressInitGenerator(host, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.bundler === 'vite') {
|
||||||
|
tasks.push(
|
||||||
|
addDependenciesToPackageJson(
|
||||||
|
host,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
vite: viteVersion,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
createFiles(host, options);
|
createFiles(host, options);
|
||||||
addProject(host, options);
|
addProject(host, options);
|
||||||
const installTask = await addLinter(host, options);
|
const installTask = await addLinter(host, options);
|
||||||
@ -320,6 +335,7 @@ function normalizeOptions(host: Tree, options: Schema): CypressProjectSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options.linter = options.linter || Linter.EsLint;
|
options.linter = options.linter || Linter.EsLint;
|
||||||
|
options.bundler = options.bundler || 'webpack';
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
// other generators depend on the rootProject flag down stream
|
// other generators depend on the rootProject flag down stream
|
||||||
|
|||||||
@ -2,5 +2,9 @@ import { defineConfig } from 'cypress';
|
|||||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: nxE2EPreset(__dirname)
|
e2e: nxE2EPreset(__dirname<% if (bundler === 'vite'){ %>,
|
||||||
|
{
|
||||||
|
bundler: 'vite'
|
||||||
|
}
|
||||||
|
<% } %>)
|
||||||
});
|
});
|
||||||
@ -12,4 +12,5 @@ export interface Schema {
|
|||||||
standaloneConfig?: boolean;
|
standaloneConfig?: boolean;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
rootProject?: boolean;
|
rootProject?: boolean;
|
||||||
|
bundler?: 'webpack' | 'vite' | 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,19 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Do not add dependencies to `package.json`."
|
"description": "Do not add dependencies to `package.json`."
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "Create a application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The Cypress bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["vite", "webpack", "none"],
|
||||||
|
"x-prompt": "Which Cypress bundler do you want to use?",
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
89
packages/cypress/src/plugins/preprocessor-vite.ts
Normal file
89
packages/cypress/src/plugins/preprocessor-vite.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Adapted from: https://github.com/mammadataei/cypress-vite
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import type { RollupOutput, RollupWatcher, WatcherOptions } from 'rollup';
|
||||||
|
|
||||||
|
type CypressPreprocessor = (
|
||||||
|
file: Record<string, any>
|
||||||
|
) => string | Promise<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cypress preprocessor for running e2e tests using vite.
|
||||||
|
*
|
||||||
|
* @param {string} userConfigPath
|
||||||
|
* @example
|
||||||
|
* setupNodeEvents(on) {
|
||||||
|
* on(
|
||||||
|
* 'file:preprocessor',
|
||||||
|
* vitePreprocessor(path.resolve(__dirname, './vite.config.ts')),
|
||||||
|
* )
|
||||||
|
* },
|
||||||
|
*/
|
||||||
|
function vitePreprocessor(userConfigPath?: string): CypressPreprocessor {
|
||||||
|
return async (file) => {
|
||||||
|
const { outputPath, filePath, shouldWatch } = file;
|
||||||
|
|
||||||
|
const fileName = path.basename(outputPath);
|
||||||
|
const filenameWithoutExtension = path.basename(
|
||||||
|
outputPath,
|
||||||
|
path.extname(outputPath)
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
logLevel: 'silent',
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
emptyOutDir: false,
|
||||||
|
minify: false,
|
||||||
|
outDir: path.dirname(outputPath),
|
||||||
|
sourcemap: true,
|
||||||
|
write: true,
|
||||||
|
watch: getWatcherConfig(shouldWatch),
|
||||||
|
lib: {
|
||||||
|
entry: filePath,
|
||||||
|
fileName: () => fileName,
|
||||||
|
formats: ['umd'],
|
||||||
|
name: filenameWithoutExtension,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { build } = require('vite');
|
||||||
|
|
||||||
|
const watcher = await build({
|
||||||
|
configFile: userConfigPath,
|
||||||
|
...defaultConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldWatch && isWatcher(watcher)) {
|
||||||
|
watcher.on('event', (event) => {
|
||||||
|
if (event.code === 'END') {
|
||||||
|
file.emit('rerun');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.code === 'ERROR') {
|
||||||
|
console.error(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
file.on('close', () => {
|
||||||
|
watcher.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputPath;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWatcherConfig(shouldWatch: boolean): WatcherOptions | null {
|
||||||
|
return shouldWatch ? {} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildResult = RollupWatcher | RollupOutput | RollupOutput[];
|
||||||
|
|
||||||
|
function isWatcher(watcher: BuildResult): watcher is RollupWatcher {
|
||||||
|
return (watcher as RollupWatcher).on !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vitePreprocessor;
|
||||||
@ -4,3 +4,4 @@ export const typesNodeVersion = '16.11.7';
|
|||||||
export const cypressVersion = '^11.0.0';
|
export const cypressVersion = '^11.0.0';
|
||||||
export const cypressWebpackVersion = '^2.0.0';
|
export const cypressWebpackVersion = '^2.0.0';
|
||||||
export const webpackHttpPluginVersion = '^5.5.0';
|
export const webpackHttpPluginVersion = '^5.5.0';
|
||||||
|
export const viteVersion = '^4.0.1';
|
||||||
|
|||||||
@ -16,12 +16,29 @@ nx g @nrwl/react:cypress-component-project --project=my-cool-react-project
|
|||||||
|
|
||||||
Running this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces.
|
Running this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces.
|
||||||
|
|
||||||
|
The following file will be added to projects where the Component Testing build target is using `webpack` for bundling:
|
||||||
|
|
||||||
```ts {% fileName="cypress.config.ts" %}
|
```ts {% fileName="cypress.config.ts" %}
|
||||||
import { defineConfig } from 'cypress';
|
import { defineConfig } from 'cypress';
|
||||||
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
component: nxComponentTestingPreset(__filename),
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'webpack',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The following file will be added to projects where the Component Testing build target is using `vite` for bundling:
|
||||||
|
|
||||||
|
```ts {% fileName="cypress.config.ts" %}
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'vite',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -33,12 +50,20 @@ import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing'
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
component: {
|
component: {
|
||||||
...nxComponentTestingPreset(__filename),
|
...nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'webpack',
|
||||||
|
}),
|
||||||
// extra options here
|
// extra options here
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## The `bundler` option
|
||||||
|
|
||||||
|
Component testing supports two different bundlers: `webpack` and `vite`. The Nx generator will pick up the bundler used in the specified project's build target. If the build target is using `@nrwl/webpack:webpack`, then the generator will use `webpack` as the bundler. If the build target is using `@nrwl/vite:build`, then the generator will use `vite` as the bundler.
|
||||||
|
|
||||||
|
You can manually set the bundler by passing `--bundler=webpack` or `--bundler=vite` to the generator, but that is not needed since the generator will pick up the correct bundler for you. However, if you want to use a different bundler than the one that is used in the build target, then you can manually set it using that flag.
|
||||||
|
|
||||||
## Specifying a Build Target
|
## Specifying a Build Target
|
||||||
|
|
||||||
Component testing requires a _build target_ to correctly run the component test dev server. This option can be manually specified with `--build-target=some-react-app:build`, but Nx will infer this usage from the [project graph](/concepts/mental-model#the-project-graph) if one isn't provided.
|
Component testing requires a _build target_ to correctly run the component test dev server. This option can be manually specified with `--build-target=some-react-app:build`, but Nx will infer this usage from the [project graph](/concepts/mental-model#the-project-graph) if one isn't provided.
|
||||||
|
|||||||
@ -14,11 +14,6 @@ import {
|
|||||||
Target,
|
Target,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import type { WebpackExecutorOptions } from '@nrwl/webpack/src/executors/webpack/schema';
|
|
||||||
import { normalizeOptions } from '@nrwl/webpack/src/executors/webpack/lib/normalize-options';
|
|
||||||
import { getWebpackConfig } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
|
||||||
import { resolveCustomWebpackConfig } from '@nrwl/webpack/src/utils/webpack/custom-webpack';
|
|
||||||
import { buildBaseWebpackConfig } from './webpack-fallback';
|
|
||||||
import {
|
import {
|
||||||
createExecutorContext,
|
createExecutorContext,
|
||||||
getProjectConfigByPath,
|
getProjectConfigByPath,
|
||||||
@ -45,7 +40,29 @@ import {
|
|||||||
export function nxComponentTestingPreset(
|
export function nxComponentTestingPreset(
|
||||||
pathToConfig: string,
|
pathToConfig: string,
|
||||||
options?: NxComponentTestingOptions
|
options?: NxComponentTestingOptions
|
||||||
) {
|
): {
|
||||||
|
specPattern: string;
|
||||||
|
devServer: {
|
||||||
|
framework?: 'react';
|
||||||
|
bundler?: 'vite' | 'webpack';
|
||||||
|
viteConfig?: any;
|
||||||
|
webpackConfig?: any;
|
||||||
|
};
|
||||||
|
videosFolder: string;
|
||||||
|
screenshotsFolder: string;
|
||||||
|
video: boolean;
|
||||||
|
chromeWebSecurity: boolean;
|
||||||
|
} {
|
||||||
|
if (options.bundler === 'vite') {
|
||||||
|
return {
|
||||||
|
...nxBaseCypressPreset(pathToConfig),
|
||||||
|
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
||||||
|
devServer: {
|
||||||
|
...({ framework: 'react', bundler: 'vite' } as const),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let webpackConfig;
|
let webpackConfig;
|
||||||
try {
|
try {
|
||||||
const graph = readCachedProjectGraph();
|
const graph = readCachedProjectGraph();
|
||||||
@ -88,11 +105,14 @@ export function nxComponentTestingPreset(
|
|||||||
Falling back to default webpack config.`
|
Falling back to default webpack config.`
|
||||||
);
|
);
|
||||||
logger.warn(e);
|
logger.warn(e);
|
||||||
|
|
||||||
|
const { buildBaseWebpackConfig } = require('./webpack-fallback');
|
||||||
webpackConfig = buildBaseWebpackConfig({
|
webpackConfig = buildBaseWebpackConfig({
|
||||||
tsConfigPath: 'cypress/tsconfig.cy.json',
|
tsConfigPath: 'cypress/tsconfig.cy.json',
|
||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...nxBaseCypressPreset(pathToConfig),
|
...nxBaseCypressPreset(pathToConfig),
|
||||||
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
||||||
@ -109,11 +129,8 @@ export function nxComponentTestingPreset(
|
|||||||
/**
|
/**
|
||||||
* apply the schema.json defaults from the @nrwl/web:webpack executor to the target options
|
* apply the schema.json defaults from the @nrwl/web:webpack executor to the target options
|
||||||
*/
|
*/
|
||||||
function withSchemaDefaults(
|
function withSchemaDefaults(target: Target, context: ExecutorContext) {
|
||||||
target: Target,
|
const options = readTargetOptions(target, context);
|
||||||
context: ExecutorContext
|
|
||||||
): WebpackExecutorOptions {
|
|
||||||
const options = readTargetOptions<WebpackExecutorOptions>(target, context);
|
|
||||||
|
|
||||||
options.compiler ??= 'babel';
|
options.compiler ??= 'babel';
|
||||||
options.deleteOutputPath ??= true;
|
options.deleteOutputPath ??= true;
|
||||||
@ -161,6 +178,16 @@ function buildTargetWebpack(
|
|||||||
parsed.target
|
parsed.target
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
normalizeOptions,
|
||||||
|
} = require('@nrwl/webpack/src/executors/webpack/lib/normalize-options');
|
||||||
|
const {
|
||||||
|
resolveCustomWebpackConfig,
|
||||||
|
} = require('@nrwl/webpack/src/utils/webpack/custom-webpack');
|
||||||
|
const {
|
||||||
|
getWebpackConfig,
|
||||||
|
} = require('@nrwl/webpack/src/executors/webpack/lib/get-webpack-config');
|
||||||
|
|
||||||
const options = normalizeOptions(
|
const options = normalizeOptions(
|
||||||
withSchemaDefaults(parsed, context),
|
withSchemaDefaults(parsed, context),
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getCSSModuleLocalIdent } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
||||||
import { Configuration } from 'webpack';
|
import { Configuration } from 'webpack';
|
||||||
|
import { getCSSModuleLocalIdent } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
||||||
|
|
||||||
export function buildBaseWebpackConfig({
|
export function buildBaseWebpackConfig({
|
||||||
tsConfigPath = 'tsconfig.cy.json',
|
tsConfigPath = 'tsconfig.cy.json',
|
||||||
|
|||||||
@ -1,5 +1,49 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`React:CypressComponentTestConfiguration should generate cypress component test config with --build-target 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'vite'
|
||||||
|
}) as any,
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`React:CypressComponentTestConfiguration should generate cypress component test config with project graph 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'vite'
|
||||||
|
}) as any,
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`React:CypressComponentTestConfiguration should generate cypress component test config with webpack 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'webpack'
|
||||||
|
}) as any,
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`React:CypressComponentTestConfiguration should generate cypress config with vite 1`] = `
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: 'vite'
|
||||||
|
}) as any,
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`React:CypressComponentTestConfiguration should generate tests for existing js components 1`] = `
|
exports[`React:CypressComponentTestConfiguration should generate tests for existing js components 1`] = `
|
||||||
"import * as React from 'react'
|
"import * as React from 'react'
|
||||||
import SomeCmp from './some-cmp'
|
import SomeCmp from './some-cmp'
|
||||||
|
|||||||
@ -30,6 +30,63 @@ describe('React:CypressComponentTestConfiguration', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate cypress config with vite', async () => {
|
||||||
|
mockedAssertCypressVersion.mockReturnValue();
|
||||||
|
|
||||||
|
await applicationGenerator(tree, {
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'scss',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
name: 'my-app',
|
||||||
|
bundler: 'vite',
|
||||||
|
});
|
||||||
|
await libraryGenerator(tree, {
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
name: 'some-lib',
|
||||||
|
skipFormat: true,
|
||||||
|
skipTsConfig: false,
|
||||||
|
style: 'scss',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
component: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {
|
||||||
|
'my-app': {
|
||||||
|
name: 'my-app',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
...readProjectConfiguration(tree, 'my-app'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'some-lib': {
|
||||||
|
name: 'some-lib',
|
||||||
|
type: 'lib',
|
||||||
|
data: {
|
||||||
|
...readProjectConfiguration(tree, 'some-lib'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
'my-app': [
|
||||||
|
{ type: DependencyType.static, source: 'my-app', target: 'some-lib' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await cypressComponentConfigGenerator(tree, {
|
||||||
|
project: 'some-lib',
|
||||||
|
generateTests: false,
|
||||||
|
buildTarget: 'my-app:build',
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
||||||
|
expect(config).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
it('should generate cypress component test config with --build-target', async () => {
|
it('should generate cypress component test config with --build-target', async () => {
|
||||||
mockedAssertCypressVersion.mockReturnValue();
|
mockedAssertCypressVersion.mockReturnValue();
|
||||||
|
|
||||||
@ -83,12 +140,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
||||||
expect(config).toContain(
|
expect(config).toMatchSnapshot();
|
||||||
"import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing"
|
|
||||||
);
|
|
||||||
expect(config).toContain(
|
|
||||||
'component: nxComponentTestingPreset(__filename),'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
readProjectConfiguration(tree, 'some-lib').targets['component-test']
|
readProjectConfiguration(tree, 'some-lib').targets['component-test']
|
||||||
@ -154,12 +206,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
||||||
expect(config).toContain(
|
expect(config).toMatchSnapshot();
|
||||||
"import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing"
|
|
||||||
);
|
|
||||||
expect(config).toContain(
|
|
||||||
'component: nxComponentTestingPreset(__filename),'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
readProjectConfiguration(tree, 'some-lib').targets['component-test']
|
readProjectConfiguration(tree, 'some-lib').targets['component-test']
|
||||||
@ -174,6 +221,71 @@ describe('React:CypressComponentTestConfiguration', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate cypress component test config with webpack', async () => {
|
||||||
|
mockedAssertCypressVersion.mockReturnValue();
|
||||||
|
await applicationGenerator(tree, {
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'scss',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
name: 'my-app',
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
await libraryGenerator(tree, {
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
name: 'some-lib',
|
||||||
|
skipFormat: true,
|
||||||
|
skipTsConfig: false,
|
||||||
|
style: 'scss',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
component: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {
|
||||||
|
'my-app': {
|
||||||
|
name: 'my-app',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
...readProjectConfiguration(tree, 'my-app'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'some-lib': {
|
||||||
|
name: 'some-lib',
|
||||||
|
type: 'lib',
|
||||||
|
data: {
|
||||||
|
...readProjectConfiguration(tree, 'some-lib'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
'my-app': [
|
||||||
|
{ type: DependencyType.static, source: 'my-app', target: 'some-lib' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await cypressComponentConfigGenerator(tree, {
|
||||||
|
project: 'some-lib',
|
||||||
|
generateTests: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = tree.read('libs/some-lib/cypress.config.ts', 'utf-8');
|
||||||
|
expect(config).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
readProjectConfiguration(tree, 'some-lib').targets['component-test']
|
||||||
|
).toEqual({
|
||||||
|
executor: '@nrwl/cypress:cypress',
|
||||||
|
options: {
|
||||||
|
cypressConfig: 'libs/some-lib/cypress.config.ts',
|
||||||
|
devServerTarget: 'my-app:build',
|
||||||
|
skipServe: true,
|
||||||
|
testingType: 'component',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
it('should generate tests for existing tsx components', async () => {
|
it('should generate tests for existing tsx components', async () => {
|
||||||
mockedAssertCypressVersion.mockReturnValue();
|
mockedAssertCypressVersion.mockReturnValue();
|
||||||
await applicationGenerator(tree, {
|
await applicationGenerator(tree, {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { nxVersion } from '../../utils/versions';
|
import { nxVersion } from '../../utils/versions';
|
||||||
import { addFiles } from './lib/add-files';
|
import { addFiles } from './lib/add-files';
|
||||||
import { updateProjectConfig } from './lib/update-configs';
|
import { FoundTarget, updateProjectConfig } from './lib/update-configs';
|
||||||
import { CypressComponentConfigurationSchema } from './schema.d';
|
import { CypressComponentConfigurationSchema } from './schema.d';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,8 +26,8 @@ export async function cypressComponentConfigGenerator(
|
|||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateProjectConfig(tree, options);
|
const found: FoundTarget = await updateProjectConfig(tree, options);
|
||||||
await addFiles(tree, projectConfig, options);
|
await addFiles(tree, projectConfig, options, found);
|
||||||
if (options.skipFormat) {
|
if (options.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,5 +2,7 @@ import { defineConfig } from 'cypress';
|
|||||||
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
component: nxComponentTestingPreset(__filename),
|
component: nxComponentTestingPreset(__filename, {
|
||||||
|
bundler: '<%= bundler %>'
|
||||||
|
}) as any,
|
||||||
});
|
});
|
||||||
@ -1,14 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
|
ensurePackage,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
|
logger,
|
||||||
|
parseTargetString,
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
visitNotIgnoredFiles,
|
visitNotIgnoredFiles,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
|
import { nxVersion } from 'nx/src/utils/versions';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import { getComponentNode } from '../../../utils/ast-utils';
|
import { getComponentNode } from '../../../utils/ast-utils';
|
||||||
import { componentTestGenerator } from '../../component-test/component-test';
|
import { componentTestGenerator } from '../../component-test/component-test';
|
||||||
import { CypressComponentConfigurationSchema } from '../schema';
|
import { CypressComponentConfigurationSchema } from '../schema';
|
||||||
|
import { FoundTarget } from './update-configs';
|
||||||
|
|
||||||
const allowedFileExt = new RegExp(/\.[jt]sx?/g);
|
const allowedFileExt = new RegExp(/\.[jt]sx?/g);
|
||||||
const isSpecFile = new RegExp(/(spec|test)\./g);
|
const isSpecFile = new RegExp(/(spec|test)\./g);
|
||||||
@ -16,7 +22,8 @@ const isSpecFile = new RegExp(/(spec|test)\./g);
|
|||||||
export async function addFiles(
|
export async function addFiles(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
projectConfig: ProjectConfiguration,
|
projectConfig: ProjectConfiguration,
|
||||||
options: CypressComponentConfigurationSchema
|
options: CypressComponentConfigurationSchema,
|
||||||
|
found: FoundTarget
|
||||||
) {
|
) {
|
||||||
const cypressConfigPath = joinPathFragments(
|
const cypressConfigPath = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
@ -26,15 +33,32 @@ export async function addFiles(
|
|||||||
tree.delete(cypressConfigPath);
|
tree.delete(cypressConfigPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actualBundler = getBundler(found, tree);
|
||||||
|
|
||||||
|
if (options.bundler && options.bundler !== actualBundler) {
|
||||||
|
logger.warn(
|
||||||
|
`You have specified ${options.bundler} as the bundler but this project is configured to use ${actualBundler}.
|
||||||
|
This may cause errors. If you are seeing errors, try removing the --bundler option.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(__dirname, '..', 'files'),
|
joinPathFragments(__dirname, '..', 'files'),
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
{
|
{
|
||||||
tpl: '',
|
tpl: '',
|
||||||
|
bundler: options.bundler ?? actualBundler,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
options.bundler === 'webpack' ||
|
||||||
|
(!options.bundler && actualBundler === 'webpack')
|
||||||
|
) {
|
||||||
|
await ensurePackage(tree, '@nrwl/webpack', nxVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.generateTests) {
|
if (options.generateTests) {
|
||||||
const filePaths = [];
|
const filePaths = [];
|
||||||
visitNotIgnoredFiles(tree, projectConfig.sourceRoot, (filePath) => {
|
visitNotIgnoredFiles(tree, projectConfig.sourceRoot, (filePath) => {
|
||||||
@ -52,6 +76,18 @@ export async function addFiles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBundler(found: FoundTarget, tree: Tree): 'vite' | 'webpack' {
|
||||||
|
if (found.target && found.config?.executor) {
|
||||||
|
return found.config.executor === '@nrwl/vite:build' ? 'vite' : 'webpack';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { target, project } = parseTargetString(found.target);
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project);
|
||||||
|
return projectConfig?.targets?.[target]?.executor === '@nrwl/vite:build'
|
||||||
|
? 'vite'
|
||||||
|
: 'webpack';
|
||||||
|
}
|
||||||
|
|
||||||
function isComponent(tree: Tree, filePath: string): boolean {
|
function isComponent(tree: Tree, filePath: string): boolean {
|
||||||
if (isSpecFile.test(filePath) || !allowedFileExt.test(filePath)) {
|
if (isSpecFile.test(filePath) || !allowedFileExt.test(filePath)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
|
TargetConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
updateProjectConfiguration,
|
updateProjectConfiguration,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { CypressComponentConfigurationSchema } from '../schema';
|
import { CypressComponentConfigurationSchema } from '../schema';
|
||||||
|
|
||||||
|
export interface FoundTarget {
|
||||||
|
config?: TargetConfiguration;
|
||||||
|
target: string;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateProjectConfig(
|
export async function updateProjectConfig(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: CypressComponentConfigurationSchema
|
options: CypressComponentConfigurationSchema
|
||||||
) {
|
): Promise<FoundTarget> {
|
||||||
const { findBuildConfig } = await import(
|
const { findBuildConfig } = await import(
|
||||||
'@nrwl/cypress/src/utils/find-target-options'
|
'@nrwl/cypress/src/utils/find-target-options'
|
||||||
);
|
);
|
||||||
@ -30,6 +36,8 @@ export async function updateProjectConfig(
|
|||||||
skipServe: true,
|
skipServe: true,
|
||||||
};
|
};
|
||||||
updateProjectConfiguration(tree, options.project, projectConfig);
|
updateProjectConfiguration(tree, options.project, projectConfig);
|
||||||
|
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assetValidConfig(config: unknown) {
|
function assetValidConfig(config: unknown) {
|
||||||
|
|||||||
@ -3,4 +3,5 @@ export interface CypressComponentConfigurationSchema {
|
|||||||
generateTests: boolean;
|
generateTests: boolean;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
buildTarget?: string;
|
buildTarget?: string;
|
||||||
|
bundler?: 'webpack' | 'vite';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Skip formatting files",
|
"description": "Skip formatting files",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use for Cypress Component Testing.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["vite", "webpack"],
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["project"],
|
"required": ["project"],
|
||||||
|
|||||||
@ -67,7 +67,10 @@
|
|||||||
},
|
},
|
||||||
"bundler": {
|
"bundler": {
|
||||||
"description": "The Storybook builder to use.",
|
"description": "The Storybook builder to use.",
|
||||||
"enum": ["vite", "webpack"]
|
"type": "string",
|
||||||
|
"enum": ["vite", "webpack"],
|
||||||
|
"x-prompt": "Which Storybook builder do you want to use?",
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"bundler": {
|
"bundler": {
|
||||||
"description": "The bundler to use.",
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
"enum": ["vite", "webpack"],
|
"enum": ["vite", "webpack"],
|
||||||
"x-prompt": "Which bundler do you want to use?",
|
"x-prompt": "Which bundler do you want to use?",
|
||||||
"default": "webpack"
|
"default": "webpack"
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export * from './src/utils/versions';
|
export * from './src/utils/versions';
|
||||||
|
export * from './src/utils/generator-utils';
|
||||||
export { viteConfigurationGenerator } from './src/generators/configuration/configuration';
|
export { viteConfigurationGenerator } from './src/generators/configuration/configuration';
|
||||||
export { vitestGenerator } from './src/generators/vitest/vitest-generator';
|
export { vitestGenerator } from './src/generators/vitest/vitest-generator';
|
||||||
|
|||||||
@ -2,15 +2,14 @@ import {
|
|||||||
applyChangesToString,
|
applyChangesToString,
|
||||||
ChangeType,
|
ChangeType,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
joinPathFragments,
|
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
workspaceRoot,
|
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
|
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
|
||||||
import { findNodes } from 'nx/src/utils/typescript';
|
import { findNodes } from 'nx/src/utils/typescript';
|
||||||
import ts = require('typescript');
|
import ts = require('typescript');
|
||||||
|
import { normalizeViteConfigFilePathWithTree } from '../../utils/generator-utils';
|
||||||
|
|
||||||
export async function removeProjectsFromViteTsConfigPaths(tree: Tree) {
|
export async function removeProjectsFromViteTsConfigPaths(tree: Tree) {
|
||||||
findAllProjectsWithViteConfig(tree);
|
findAllProjectsWithViteConfig(tree);
|
||||||
@ -22,11 +21,10 @@ export default removeProjectsFromViteTsConfigPaths;
|
|||||||
function findAllProjectsWithViteConfig(tree: Tree): void {
|
function findAllProjectsWithViteConfig(tree: Tree): void {
|
||||||
forEachExecutorOptions(tree, '@nrwl/vite:build', (options, project) => {
|
forEachExecutorOptions(tree, '@nrwl/vite:build', (options, project) => {
|
||||||
const projectConfiguration = readProjectConfiguration(tree, project);
|
const projectConfiguration = readProjectConfiguration(tree, project);
|
||||||
const viteConfig = normalizeConfigFilePathWithTree(
|
const viteConfig = normalizeViteConfigFilePathWithTree(
|
||||||
tree,
|
tree,
|
||||||
projectConfiguration.root,
|
projectConfiguration.root,
|
||||||
options?.['configFile'],
|
options?.['configFile']
|
||||||
workspaceRoot
|
|
||||||
);
|
);
|
||||||
if (viteConfig) {
|
if (viteConfig) {
|
||||||
const file = getTsSourceFile(tree, viteConfig);
|
const file = getTsSourceFile(tree, viteConfig);
|
||||||
@ -85,18 +83,3 @@ export function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
|
|||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeConfigFilePathWithTree(
|
|
||||||
tree: Tree,
|
|
||||||
projectRoot: string,
|
|
||||||
configFile?: string,
|
|
||||||
workspaceRoot?: string
|
|
||||||
): string {
|
|
||||||
return configFile
|
|
||||||
? joinPathFragments(`${workspaceRoot}/${configFile}`)
|
|
||||||
: tree.exists(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
|
||||||
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
|
||||||
: tree.exists(joinPathFragments(`${projectRoot}/vite.config.js`))
|
|
||||||
? joinPathFragments(`${projectRoot}/vite.config.js`)
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
|
|||||||
111
packages/vite/src/utils/generator-util.test.ts
Normal file
111
packages/vite/src/utils/generator-util.test.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||||
|
import {
|
||||||
|
findExistingTargets,
|
||||||
|
getViteConfigPathForProject,
|
||||||
|
} from './generator-utils';
|
||||||
|
import { mockReactAppGenerator, mockViteReactAppGenerator } from './test-utils';
|
||||||
|
describe('generator utils', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyV1Workspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getViteConfigPathForProject', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockViteReactAppGenerator(tree);
|
||||||
|
});
|
||||||
|
it('should return correct path for vite.config file if no configFile is set', () => {
|
||||||
|
const viteConfigPath = getViteConfigPathForProject(
|
||||||
|
tree,
|
||||||
|
'my-test-react-vite-app'
|
||||||
|
);
|
||||||
|
expect(viteConfigPath).toEqual(
|
||||||
|
'apps/my-test-react-vite-app/vite.config.ts'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct path for vite.config file if custom configFile is set', () => {
|
||||||
|
const projectConfig = readProjectConfiguration(
|
||||||
|
tree,
|
||||||
|
'my-test-react-vite-app'
|
||||||
|
);
|
||||||
|
updateProjectConfiguration(tree, 'my-test-react-vite-app', {
|
||||||
|
...projectConfig,
|
||||||
|
targets: {
|
||||||
|
...projectConfig.targets,
|
||||||
|
build: {
|
||||||
|
...projectConfig.targets.build,
|
||||||
|
options: {
|
||||||
|
...projectConfig.targets.build.options,
|
||||||
|
configFile: 'apps/my-test-react-vite-app/vite.config.custom.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tree.write(`apps/my-test-react-vite-app/vite.config.custom.ts`, '');
|
||||||
|
|
||||||
|
const viteConfigPath = getViteConfigPathForProject(
|
||||||
|
tree,
|
||||||
|
'my-test-react-vite-app'
|
||||||
|
);
|
||||||
|
expect(viteConfigPath).toEqual(
|
||||||
|
'apps/my-test-react-vite-app/vite.config.custom.ts'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct path for vite.config file given a target name', () => {
|
||||||
|
const projectConfig = readProjectConfiguration(
|
||||||
|
tree,
|
||||||
|
'my-test-react-vite-app'
|
||||||
|
);
|
||||||
|
updateProjectConfiguration(tree, 'my-test-react-vite-app', {
|
||||||
|
...projectConfig,
|
||||||
|
targets: {
|
||||||
|
...projectConfig.targets,
|
||||||
|
'other-build': {
|
||||||
|
...projectConfig.targets.build,
|
||||||
|
options: {
|
||||||
|
...projectConfig.targets.build.options,
|
||||||
|
configFile: 'apps/my-test-react-vite-app/vite.other.custom.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tree.write(`apps/my-test-react-vite-app/vite.other.custom.ts`, '');
|
||||||
|
|
||||||
|
const viteConfigPath = getViteConfigPathForProject(
|
||||||
|
tree,
|
||||||
|
'my-test-react-vite-app',
|
||||||
|
'other-build'
|
||||||
|
);
|
||||||
|
expect(viteConfigPath).toEqual(
|
||||||
|
'apps/my-test-react-vite-app/vite.other.custom.ts'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findExistingTargets', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockReactAppGenerator(tree);
|
||||||
|
});
|
||||||
|
it('should return the correct targets', () => {
|
||||||
|
const { targets } = readProjectConfiguration(tree, 'my-test-react-app');
|
||||||
|
|
||||||
|
const existingTargets = findExistingTargets(targets);
|
||||||
|
expect(existingTargets).toMatchObject({
|
||||||
|
buildTarget: 'build',
|
||||||
|
serveTarget: 'serve',
|
||||||
|
testTarget: 'test',
|
||||||
|
unsuppored: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -462,3 +462,38 @@ ${options.includeVitest ? '/// <reference types="vitest" />' : ''}
|
|||||||
|
|
||||||
tree.write(viteConfigPath, viteConfigContent);
|
tree.write(viteConfigPath, viteConfigContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeViteConfigFilePathWithTree(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
configFile?: string
|
||||||
|
): string {
|
||||||
|
return configFile && tree.exists(configFile)
|
||||||
|
? configFile
|
||||||
|
: tree.exists(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
||||||
|
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
||||||
|
: tree.exists(joinPathFragments(`${projectRoot}/vite.config.js`))
|
||||||
|
? joinPathFragments(`${projectRoot}/vite.config.js`)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getViteConfigPathForProject(
|
||||||
|
tree: Tree,
|
||||||
|
projectName: string,
|
||||||
|
target?: string
|
||||||
|
) {
|
||||||
|
let viteConfigPath: string | undefined;
|
||||||
|
const { targets, root } = readProjectConfiguration(tree, projectName);
|
||||||
|
if (target) {
|
||||||
|
viteConfigPath = targets[target]?.options?.configFile;
|
||||||
|
} else {
|
||||||
|
const buildTarget = Object.entries(targets).find(
|
||||||
|
([_targetName, targetConfig]) => {
|
||||||
|
return targetConfig.executor === '@nrwl/vite:build';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
viteConfigPath = buildTarget?.[1]?.options?.configFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeViteConfigFilePathWithTree(tree, root, viteConfigPath);
|
||||||
|
}
|
||||||
|
|||||||
@ -32,11 +32,7 @@ export async function getBuildAndSharedConfig(
|
|||||||
mode: options.mode ?? context.configurationName,
|
mode: options.mode ?? context.configurationName,
|
||||||
root: projectRoot,
|
root: projectRoot,
|
||||||
base: options.base,
|
base: options.base,
|
||||||
configFile: normalizeConfigFilePath(
|
configFile: normalizeViteConfigFilePath(projectRoot, options.configFile),
|
||||||
projectRoot,
|
|
||||||
options.configFile,
|
|
||||||
context.root
|
|
||||||
),
|
|
||||||
plugins: [replaceFiles(options.fileReplacements)],
|
plugins: [replaceFiles(options.fileReplacements)],
|
||||||
build: getViteBuildOptions(
|
build: getViteBuildOptions(
|
||||||
options as ViteDevServerExecutorOptions & ViteBuildExecutorOptions,
|
options as ViteDevServerExecutorOptions & ViteBuildExecutorOptions,
|
||||||
@ -45,13 +41,12 @@ export async function getBuildAndSharedConfig(
|
|||||||
} as InlineConfig);
|
} as InlineConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeConfigFilePath(
|
export function normalizeViteConfigFilePath(
|
||||||
projectRoot: string,
|
projectRoot: string,
|
||||||
configFile?: string,
|
configFile?: string
|
||||||
workspaceRoot?: string
|
|
||||||
): string {
|
): string {
|
||||||
return configFile
|
return configFile && existsSync(joinPathFragments(configFile))
|
||||||
? joinPathFragments(`${workspaceRoot}/${configFile}`)
|
? configFile
|
||||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
: existsSync(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
||||||
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
||||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.js`))
|
: existsSync(joinPathFragments(`${projectRoot}/vite.config.js`))
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const IGNORE_MATCHES_IN_PACKAGE = {
|
|||||||
'tailwindcss',
|
'tailwindcss',
|
||||||
],
|
],
|
||||||
cli: ['nx'],
|
cli: ['nx'],
|
||||||
cypress: ['cypress', '@angular-devkit/schematics', '@nrwl/cypress'],
|
cypress: ['cypress', '@angular-devkit/schematics', '@nrwl/cypress', 'vite'],
|
||||||
devkit: ['@angular-devkit/architect', 'rxjs', 'webpack'],
|
devkit: ['@angular-devkit/architect', 'rxjs', 'webpack'],
|
||||||
'eslint-plugin-nx': ['@angular-eslint/eslint-plugin'],
|
'eslint-plugin-nx': ['@angular-eslint/eslint-plugin'],
|
||||||
jest: [
|
jest: [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user