Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b06bc241ea | |||
| 58d089ced0 | |||
| 4554f65c6e | |||
| 39ae390692 | |||
| 81fb58b38a | |||
| d384415d7d | |||
| 2695e9764d | |||
| e09c05573c | |||
| 2fc5d33059 | |||
| 99b69b8281 | |||
| f559e675d1 |
@@ -34,6 +34,21 @@ The build target option can be changed later via updating the `devServerTarget`
|
||||
When using component testing make sure to set `skipServe: true` in the component test target options, otherwise `@nx/cypress` will attempt to run the build first which can slow down your component tests. `skipServe: true` is automatically set when using the `cypress-component-configuration` generator.
|
||||
{% /callout %}
|
||||
|
||||
## Configuration
|
||||
|
||||
When using the `cypress-component-configuration` generator, a helper function is used in the `cypress.config.ts` to setup the ideal settings for your project.
|
||||
|
||||
If you need to add additional configuration properties, you can spread the returned object from the helper function.
|
||||
|
||||
```ts {%filename="cypress.config.ts"}
|
||||
export default defineConfig({
|
||||
component: {
|
||||
...nxComponentTestingPreset(__filename),
|
||||
// add your own config here
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Testing Projects
|
||||
|
||||
Run `nx component-test your-lib` to execute the component tests with Cypress.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Below is an example of a GitLab pipeline setup for an Nx workspace - building and testing only what is affected.
|
||||
|
||||
```yaml
|
||||
image: node:16
|
||||
image: node:18
|
||||
|
||||
stages:
|
||||
- test
|
||||
@@ -63,8 +63,6 @@ Read more about [Distributed Task Execution (DTE)](/core-features/distribute-tas
|
||||
|
||||
```yaml
|
||||
image: node:18
|
||||
variables:
|
||||
CI: 'true'
|
||||
|
||||
# Creating template for DTE agents
|
||||
.dte-agent:
|
||||
|
||||
@@ -34,6 +34,21 @@ The build target option can be changed later via updating the `devServerTarget`
|
||||
When using component testing make sure to set `skipServe: true` in the component test target options, otherwise `@nx/cypress` will attempt to run the build first which can slow down your component tests. `skipServe: true` is automatically set when using the `cypress-component-configuration` generator.
|
||||
{% /callout %}
|
||||
|
||||
## Configuration
|
||||
|
||||
When using the `cypress-component-configuration` generator, a helper function is used in the `cypress.config.ts` to setup the ideal settings for your project.
|
||||
|
||||
If you need to add additional configuration properties, you can spread the returned object from the helper function.
|
||||
|
||||
```ts {%filename="cypress.config.ts"}
|
||||
export default defineConfig({
|
||||
component: {
|
||||
...nxComponentTestingPreset(__filename),
|
||||
// add your own config here
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Testing Projects
|
||||
|
||||
Run `nx component-test your-lib` to execute the component tests with Cypress.
|
||||
|
||||
@@ -14,8 +14,9 @@ The following environment variables are ones that you can set to change the beha
|
||||
| NX_PROFILE | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/recipes/other/performance-profiling) to visualize the performance of Nx across multiple processes. |
|
||||
| NX_PROJECT_GRAPH_CACHE_DIRECTORY | string | The project graph cache is stored in `node_modules/.cache/nx` by default. Set this variable to use a different directory. |
|
||||
| NX_PROJECT_GRAPH_MAX_WORKERS | number | The number of workers to use when calculating the project graph. |
|
||||
| NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. |
|
||||
| NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Not read if NX_TASKS_RUNNER is set. |
|
||||
| NX_SKIP_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache |
|
||||
| NX_TASKS_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Preferred over NX_RUNNER. |
|
||||
| NX_TASKS_RUNNER_DYNAMIC_OUTPUT | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version |
|
||||
| NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting |
|
||||
| NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. |
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
cleanupProject,
|
||||
createFile,
|
||||
ensureCypressInstallation,
|
||||
killPorts,
|
||||
killPort,
|
||||
newProject,
|
||||
readJson,
|
||||
runCLI,
|
||||
@@ -91,7 +91,7 @@ describe('env vars', () => {
|
||||
`e2e ${myapp}-e2e --no-watch --env.cliArg="i am from the cli args"`
|
||||
);
|
||||
expect(run1).toContain('All specs passed!');
|
||||
await killPorts(4200);
|
||||
await killPort(4200);
|
||||
// tests should not fail because of a config change
|
||||
updateFile(
|
||||
`apps/${myapp}-e2e/cypress.config.ts`,
|
||||
@@ -111,7 +111,7 @@ export default defineConfig({
|
||||
`e2e ${myapp}-e2e --no-watch --env.cliArg="i am from the cli args"`
|
||||
);
|
||||
expect(run2).toContain('All specs passed!');
|
||||
await killPorts(4200);
|
||||
await killPort(4200);
|
||||
|
||||
// make sure project.json env vars also work
|
||||
updateFile(
|
||||
@@ -140,10 +140,13 @@ describe('env vars', () => {
|
||||
const run3 = runCLI(`e2e ${myapp}-e2e --no-watch`);
|
||||
expect(run3).toContain('All specs passed!');
|
||||
|
||||
expect(await killPorts(4200)).toBeTruthy();
|
||||
expect(await killPort(4200)).toBeTruthy();
|
||||
}, 1000000);
|
||||
|
||||
it('should run e2e in parallel', () => {
|
||||
it('should run e2e in parallel', async () => {
|
||||
// ensure ports are free before running tests
|
||||
await killPort(4200);
|
||||
|
||||
const ngAppName = uniq('ng-app');
|
||||
runCLI(
|
||||
`generate @nx/angular:app ${ngAppName} --e2eTestRunner=cypress --linter=eslint --no-interactive`
|
||||
|
||||
@@ -40,6 +40,8 @@ describe('nx init (for React)', () => {
|
||||
expect(packageJson.devDependencies['@nx/jest']).toBeDefined();
|
||||
expect(packageJson.devDependencies['@nx/vite']).toBeUndefined();
|
||||
expect(packageJson.devDependencies['@nx/webpack']).toBeDefined();
|
||||
expect(packageJson.dependencies['redux']).toBeDefined();
|
||||
expect(packageJson.name).toEqual(appName);
|
||||
|
||||
runCLI(`build ${appName}`, {
|
||||
env: {
|
||||
@@ -149,6 +151,8 @@ describe('nx init (for React)', () => {
|
||||
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.devDependencies['@nx/jest']).toBeUndefined();
|
||||
expect(packageJson.dependencies['redux']).toBeDefined();
|
||||
expect(packageJson.name).toEqual(appName);
|
||||
|
||||
const viteConfig = readFile(`vite.config.js`);
|
||||
expect(viteConfig).toContain('port: 4200'); // default port
|
||||
@@ -186,6 +190,7 @@ function createReactApp(appName: string) {
|
||||
'react-dom': '^18.2.0',
|
||||
'react-scripts': '5.0.1',
|
||||
'web-vitals': '2.1.4',
|
||||
redux: '^3.6.0',
|
||||
},
|
||||
scripts: {
|
||||
start: 'react-scripts start',
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ensureCypressInstallation,
|
||||
newProject,
|
||||
runCLI,
|
||||
runCypressTests,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
@@ -146,18 +147,22 @@ export default Input;
|
||||
runCLI(
|
||||
`generate @nx/react:cypress-component-configuration --project=${appName} --generate-tests`
|
||||
);
|
||||
expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
}, 300_000);
|
||||
|
||||
it('should successfully component test lib being used in app', () => {
|
||||
runCLI(
|
||||
`generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests`
|
||||
);
|
||||
expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
}, 300_000);
|
||||
|
||||
it('should test buildable lib not being used in app', () => {
|
||||
@@ -184,9 +189,12 @@ describe(Input.name, () => {
|
||||
runCLI(
|
||||
`generate @nx/react:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build`
|
||||
);
|
||||
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
|
||||
// add tailwind
|
||||
runCLI(`generate @nx/react:setup-tailwind --project=${buildableLibName}`);
|
||||
@@ -213,9 +221,11 @@ ${content}`;
|
||||
}
|
||||
);
|
||||
|
||||
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
}, 300_000);
|
||||
|
||||
it('should work with async webpack config', () => {
|
||||
@@ -250,8 +260,41 @@ ${content}`;
|
||||
return config;
|
||||
});
|
||||
|
||||
const results = runCLI(`component-test ${appName}`);
|
||||
expect(results).toContain('I am from the custom async Webpack config');
|
||||
expect(results).toContain('All specs passed!');
|
||||
if (runCypressTests()) {
|
||||
const results = runCLI(`component-test ${appName}`);
|
||||
expect(results).toContain('I am from the custom async Webpack config');
|
||||
expect(results).toContain('All specs passed!');
|
||||
}
|
||||
});
|
||||
|
||||
// flaky bc of upstream issue https://github.com/cypress-io/cypress/issues/25913
|
||||
it.skip('should CT vite projects importing other projects', () => {
|
||||
const viteLibName = uniq('vite-lib');
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${viteLibName} --bundler=vite --no-interactive`
|
||||
);
|
||||
|
||||
updateFile(`libs/${viteLibName}/src/lib/${viteLibName}.tsx`, () => {
|
||||
return `import { Btn } from '@${projectName}/${usedInAppLibName}';
|
||||
|
||||
export function MyComponent() {
|
||||
return (
|
||||
<>
|
||||
<Btn text={'I am the app'}/>
|
||||
<p>hello</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default MyComponent;`;
|
||||
});
|
||||
|
||||
runCLI(
|
||||
`generate @nrwl/react:cypress-component-configuration --project=${viteLibName} --generate-tests --bundler=vite --build-target=${appName}:build`
|
||||
);
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${viteLibName}`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
|
||||
"version": "16.0.0",
|
||||
"version": "16.0.1",
|
||||
"granularPathspec": false,
|
||||
"command": {
|
||||
"publish": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
ClipboardDocumentIcon,
|
||||
InformationCircleIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import React, { ReactNode, useEffect, useState } from 'react';
|
||||
// @ts-ignore
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
// @ts-ignore
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { CodeOutput } from './fences/codeOutput.component';
|
||||
import { CodeOutput } from './fences/code-output.component';
|
||||
import { TerminalOutput } from './fences/terminal-output.component';
|
||||
|
||||
function resolveLanguage(lang: string) {
|
||||
@@ -74,7 +74,7 @@ export function Fence({
|
||||
children.includes('@nx/') || command.includes('@nx/');
|
||||
return (
|
||||
<div className="my-8 w-full">
|
||||
<div className="code-block group relative inline-flex w-auto min-w-[50%] max-w-full">
|
||||
<div className="code-block group relative w-full">
|
||||
<div>
|
||||
<CopyToClipboard
|
||||
text={children}
|
||||
@@ -84,7 +84,7 @@ export function Fence({
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="not-prose absolute top-0 right-0 z-10 flex rounded-tr-lg border border-slate-200 bg-slate-50/50 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:border-slate-700 dark:bg-slate-800"
|
||||
className="not-prose absolute top-0 right-0 z-10 flex rounded-tr-lg border border-slate-200 bg-slate-50/50 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60"
|
||||
>
|
||||
{copied ? (
|
||||
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
|
||||
@@ -106,12 +106,12 @@ export function Fence({
|
||||
/>
|
||||
{showRescopeMessage && (
|
||||
<a
|
||||
className="relative block rounded-b-md border border-green-100 bg-green-50 px-4 py-2 text-xs font-medium text-green-600 no-underline hover:underline dark:border-green-900 dark:bg-green-900/30 dark:text-green-400"
|
||||
className="relative block rounded-b-md border border-slate-200 bg-slate-50 px-4 py-2 text-xs font-medium no-underline hover:underline dark:border-slate-700 dark:bg-slate-800"
|
||||
href="/recipes/other/rescope"
|
||||
title="Nx 16 package name changes"
|
||||
>
|
||||
<CheckCircleIcon
|
||||
className="mr-2 inline-block h-5 w-5 text-green-500 dark:text-green-400"
|
||||
<InformationCircleIcon
|
||||
className="mr-2 inline-block h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Nx 15 and lower use @nrwl/ instead of @nx/
|
||||
|
||||
+5
-4
@@ -1,3 +1,4 @@
|
||||
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export function CodeOutput({
|
||||
@@ -11,10 +12,10 @@ export function CodeOutput({
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'hljs not-prose w-full overflow-x-auto border border-slate-200 bg-slate-50/50 font-mono text-sm dark:border-slate-700 dark:bg-slate-800/60 ' +
|
||||
(isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg')
|
||||
}
|
||||
className={cx(
|
||||
'hljs not-prose w-full overflow-x-auto border border-slate-200 bg-slate-50/50 font-mono text-sm dark:border-slate-700 dark:bg-slate-800/60',
|
||||
isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg'
|
||||
)}
|
||||
>
|
||||
{!!fileName && (
|
||||
<div className="flex border-b border-slate-200 bg-slate-50 px-4 py-2 italic text-slate-400 dark:border-slate-700 dark:bg-slate-800/80 dark:text-slate-500">
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export function TerminalOutput({
|
||||
@@ -13,10 +14,10 @@ export function TerminalOutput({
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'coding not-prose overflow-hidden border border-slate-200 bg-slate-50 font-mono text-sm leading-normal subpixel-antialiased dark:border-slate-700 dark:bg-slate-800 ' +
|
||||
(isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg')
|
||||
}
|
||||
className={cx(
|
||||
'hljs not-prose w-full overflow-x-auto border border-slate-200 bg-slate-50/50 font-mono text-sm dark:border-slate-700 dark:bg-slate-800/60',
|
||||
isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg'
|
||||
)}
|
||||
>
|
||||
<div className="relative flex justify-center border-b border-slate-200 bg-slate-100/50 p-2 text-slate-400 dark:border-slate-700 dark:bg-slate-700/50 dark:text-slate-500">
|
||||
<div className="absolute left-2 top-3 flex items-center gap-2">
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"@nrwl/next",
|
||||
"@nx/node",
|
||||
"@nrwl/node",
|
||||
"@nx/nx-plugin",
|
||||
"@nx/plugin",
|
||||
"@nrwl/nx-plugin",
|
||||
"@nx/react",
|
||||
"@nrwl/react",
|
||||
@@ -140,11 +140,11 @@
|
||||
"@nx/webpack",
|
||||
"@nrwl/webpack",
|
||||
{
|
||||
"package": "@nrwl/nx-cloud",
|
||||
"package": "nx-cloud",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"package": "nx-cloud",
|
||||
"package": "@nrwl/nx-cloud",
|
||||
"version": "latest"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -197,13 +197,15 @@ describe('report', () => {
|
||||
describe('findInstalledPackagesWeCareAbout', () => {
|
||||
it('should not list packages that are not installed', () => {
|
||||
const installed: [string, packageJsonUtils.PackageJson][] =
|
||||
packagesWeCareAbout.map((x) => [
|
||||
x,
|
||||
{
|
||||
name: x,
|
||||
version: '1.0.0',
|
||||
},
|
||||
]);
|
||||
packagesWeCareAbout
|
||||
.filter((x) => !x.startsWith('@nrwl'))
|
||||
.map((x) => [
|
||||
x,
|
||||
{
|
||||
name: x,
|
||||
version: '1.0.0',
|
||||
},
|
||||
]);
|
||||
const uninstalled: [string, packageJsonUtils.PackageJson][] = [
|
||||
installed.pop(),
|
||||
installed.pop(),
|
||||
@@ -225,6 +227,33 @@ describe('report', () => {
|
||||
expect(result).toContain(pkg);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not list @nrwl packages that are the same version as their equivalent @nx package', () => {
|
||||
jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation(
|
||||
provideMockPackages({
|
||||
'@nrwl/nx-plugin': { version: '16.0.0' },
|
||||
'@nx/plugin': { version: '16.0.0' },
|
||||
'@nrwl/linter': { version: '16.0.0' },
|
||||
'@nx/linter': { version: '16.0.0' },
|
||||
'@nrwl/workspace': { version: '16.0.0' },
|
||||
'@nx/workspace': { version: '16.0.2' },
|
||||
'@nrwl/tao': { version: '16.0.0' },
|
||||
'@nrwl/nx-cloud': { version: '16.0.0' },
|
||||
'nx-cloud': { version: '16.0.0' },
|
||||
})
|
||||
);
|
||||
|
||||
const result = findInstalledPackagesWeCareAbout().map((x) => x.package);
|
||||
expect(result).not.toContain('@nrwl/nx-plugin');
|
||||
expect(result).toContain('@nx/plugin');
|
||||
expect(result).not.toContain('@nrwl/linter');
|
||||
expect(result).toContain('@nx/linter');
|
||||
expect(result).toContain('@nrwl/workspace');
|
||||
expect(result).toContain('@nx/workspace');
|
||||
expect(result).toContain('@nrwl/tao');
|
||||
expect(result).toContain('nx-cloud');
|
||||
expect(result).not.toContain('@nrwl/nx-cloud');
|
||||
});
|
||||
});
|
||||
|
||||
describe('findMisalignedPackagesForPackage', () => {
|
||||
|
||||
@@ -275,11 +275,46 @@ export function findInstalledCommunityPlugins(): PackageJson[] {
|
||||
);
|
||||
}
|
||||
export function findInstalledPackagesWeCareAbout() {
|
||||
return packagesWeCareAbout.reduce((acc, next) => {
|
||||
const v = readPackageVersion(next);
|
||||
const packagesWeMayCareAbout: Record<string, string> = {};
|
||||
// TODO (v17): Remove workaround for hiding @nrwl packages when matching @nx package is found.
|
||||
const packageChangeMap: Record<string, string> = {
|
||||
'@nrwl/nx-plugin': '@nx/plugin',
|
||||
'@nx/plugin': '@nrwl/nx-plugin',
|
||||
'@nrwl/eslint-plugin-nx': '@nx/eslint-plugin',
|
||||
'@nx/eslint-plugin': '@nrwl/eslint-plugin-nx',
|
||||
'@nrwl/nx-cloud': 'nx-cloud',
|
||||
};
|
||||
|
||||
for (const pkg of packagesWeCareAbout) {
|
||||
const v = readPackageVersion(pkg);
|
||||
if (v) {
|
||||
acc.push({ package: next, version: v });
|
||||
// If its a @nrwl scoped package, keep the version if there is no
|
||||
// corresponding @nx scoped package, or it has a different version.
|
||||
if (pkg.startsWith('@nrwl/')) {
|
||||
const otherPackage =
|
||||
packageChangeMap[pkg] ?? pkg.replace('@nrwl/', '@nx/');
|
||||
const otherVersion = packagesWeMayCareAbout[otherPackage];
|
||||
if (!otherVersion || v !== otherVersion) {
|
||||
packagesWeMayCareAbout[pkg] = v;
|
||||
}
|
||||
// If its a @nx scoped package, always keep the version, and
|
||||
// remove the corresponding @nrwl scoped package if it exists.
|
||||
} else if (pkg.startsWith('@nx/')) {
|
||||
const otherPackage =
|
||||
packageChangeMap[pkg] ?? pkg.replace('@nx/', '@nrwl/');
|
||||
const otherVersion = packagesWeMayCareAbout[otherPackage];
|
||||
if (otherVersion && v === otherVersion) {
|
||||
delete packagesWeMayCareAbout[otherPackage];
|
||||
}
|
||||
packagesWeMayCareAbout[pkg] = v;
|
||||
} else {
|
||||
packagesWeMayCareAbout[pkg] = v;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, [] as { package: string; version: string }[]);
|
||||
}
|
||||
|
||||
return Object.entries(packagesWeMayCareAbout).map(([pkg, version]) => ({
|
||||
package: pkg,
|
||||
version,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ import { execSync } from 'child_process';
|
||||
import { copySync, moveSync, readdirSync, removeSync } from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { InitArgs } from '../../command-line/init';
|
||||
import { fileExists, readJsonFile } from '../../utils/fileutils';
|
||||
import { fileExists, readJsonFile, writeJsonFile } from '../../utils/fileutils';
|
||||
import { output } from '../../utils/output';
|
||||
import {
|
||||
detectPackageManager,
|
||||
getPackageManagerCommand,
|
||||
PackageManagerCommands,
|
||||
} from '../../utils/package-manager';
|
||||
import { PackageJson } from '../../utils/package-json';
|
||||
import { askAboutNxCloud, printFinalMessage } from '../utils';
|
||||
import { checkForCustomWebpackSetup } from './check-for-custom-webpack-setup';
|
||||
import { checkForUncommittedChanges } from './check-for-uncommitted-changes';
|
||||
@@ -76,7 +77,7 @@ async function normalizeOptions(options: Options): Promise<NormalizedOptions> {
|
||||
const appIsJs = !fileExists(`tsconfig.json`);
|
||||
|
||||
const reactAppName = readNameFromPackageJson();
|
||||
const packageJson = readJsonFile('package.json');
|
||||
const packageJson = readJsonFile(join(process.cwd(), 'package.json'));
|
||||
const deps = {
|
||||
...packageJson.dependencies,
|
||||
...packageJson.devDependencies,
|
||||
@@ -105,6 +106,13 @@ async function normalizeOptions(options: Options): Promise<NormalizedOptions> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* - Create a temp workspace
|
||||
* - Move all files to temp workspace
|
||||
* - Add bundler to temp workspace
|
||||
* - Move files back to root
|
||||
* - Clean up unused files
|
||||
*/
|
||||
async function reorgnizeWorkspaceStructure(options: NormalizedOptions) {
|
||||
createTempWorkspace(options);
|
||||
|
||||
@@ -162,6 +170,8 @@ async function reorgnizeWorkspaceStructure(options: NormalizedOptions) {
|
||||
}
|
||||
|
||||
function createTempWorkspace(options: NormalizedOptions) {
|
||||
removeSync('temp-workspace');
|
||||
|
||||
execSync(
|
||||
`npx ${
|
||||
options.npxYesFlagNeeded ? '-y' : ''
|
||||
@@ -187,12 +197,60 @@ function createTempWorkspace(options: NormalizedOptions) {
|
||||
removeSync('node_modules');
|
||||
}
|
||||
|
||||
function copyPackageJsonDepsFromTempWorkspace() {
|
||||
const repoRoot = process.cwd();
|
||||
let rootPackageJson = readJsonFile(join(repoRoot, 'package.json'));
|
||||
const tempWorkspacePackageJson = readJsonFile(
|
||||
join(repoRoot, 'temp-workspace', 'package.json')
|
||||
);
|
||||
|
||||
rootPackageJson = overridePackageDeps(
|
||||
'dependencies',
|
||||
rootPackageJson,
|
||||
tempWorkspacePackageJson
|
||||
);
|
||||
rootPackageJson = overridePackageDeps(
|
||||
'devDependencies',
|
||||
rootPackageJson,
|
||||
tempWorkspacePackageJson
|
||||
);
|
||||
rootPackageJson.scripts = {}; // remove existing scripts
|
||||
writeJsonFile(join(repoRoot, 'package.json'), rootPackageJson);
|
||||
writeJsonFile(
|
||||
join(repoRoot, 'temp-workspace', 'package.json'),
|
||||
rootPackageJson
|
||||
);
|
||||
}
|
||||
|
||||
function overridePackageDeps(
|
||||
depConfigName: 'dependencies' | 'devDependencies',
|
||||
base: PackageJson,
|
||||
override: PackageJson
|
||||
): PackageJson {
|
||||
if (!base[depConfigName]) {
|
||||
base[depConfigName] = override[depConfigName];
|
||||
return base;
|
||||
}
|
||||
const deps = override[depConfigName];
|
||||
Object.keys(deps).forEach((dep) => {
|
||||
if (base.dependencies?.[dep]) {
|
||||
delete base.dependencies[dep];
|
||||
}
|
||||
if (base.devDependencies?.[dep]) {
|
||||
delete base.devDependencies[dep];
|
||||
}
|
||||
base[depConfigName][dep] = deps[dep];
|
||||
});
|
||||
return base;
|
||||
}
|
||||
|
||||
function moveFilesToTempWorkspace(options: NormalizedOptions) {
|
||||
output.log({ title: '🚚 Moving your React app in your new Nx workspace' });
|
||||
|
||||
copyPackageJsonDepsFromTempWorkspace();
|
||||
const requiredCraFiles = [
|
||||
'project.json',
|
||||
options.isStandalone ? null : 'package.json',
|
||||
'package.json',
|
||||
'src',
|
||||
'public',
|
||||
options.appIsJs ? null : 'tsconfig.json',
|
||||
@@ -292,7 +350,7 @@ function cleanUpUnusedFilesAndAddConfigFiles(options: NormalizedOptions) {
|
||||
setupE2eProject(options.reactAppName);
|
||||
} else {
|
||||
removeSync(join('apps', `${options.reactAppName}-e2e`));
|
||||
execSync(`${options.pmc.rm} @nx/cypress eslint-plugin-cypress`);
|
||||
execSync(`${options.pmc.rm} cypress @nx/cypress eslint-plugin-cypress`);
|
||||
}
|
||||
|
||||
if (options.isStandalone) {
|
||||
|
||||
@@ -185,100 +185,191 @@ describe('splitArgs', () => {
|
||||
});
|
||||
|
||||
it('should set base and head based on environment variables in affected mode, if they are not provided directly on the command', () => {
|
||||
const originalNxBase = process.env.NX_BASE;
|
||||
process.env.NX_BASE = 'envVarSha1';
|
||||
const originalNxHead = process.env.NX_HEAD;
|
||||
process.env.NX_HEAD = 'envVarSha2';
|
||||
withEnvironment(
|
||||
{
|
||||
NX_BASE: 'envVarSha1',
|
||||
NX_HEAD: 'envVarSha2',
|
||||
},
|
||||
() => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'envVarSha1',
|
||||
head: 'envVarSha2',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'envVarSha1',
|
||||
head: 'envVarSha2',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
head: 'directlyOnCommandSha1', // higher priority than $NX_HEAD
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'envVarSha1',
|
||||
head: 'directlyOnCommandSha1',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
head: 'directlyOnCommandSha1', // higher priority than $NX_HEAD
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'envVarSha1',
|
||||
head: 'directlyOnCommandSha1',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
base: 'directlyOnCommandSha2', // higher priority than $NX_BASE
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'directlyOnCommandSha2',
|
||||
head: 'envVarSha2',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
|
||||
// Reset process data
|
||||
process.env.NX_BASE = originalNxBase;
|
||||
process.env.NX_HEAD = originalNxHead;
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
base: 'directlyOnCommandSha2', // higher priority than $NX_BASE
|
||||
},
|
||||
'affected',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
base: 'directlyOnCommandSha2',
|
||||
head: 'envVarSha2',
|
||||
skipNxCache: false,
|
||||
parallel: 3,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should set runner based on environment variables, if it is not provided directly on the command', () => {
|
||||
const originalRunner = process.env.NX_RUNNER;
|
||||
process.env.NX_RUNNER = 'some-env-runner-name';
|
||||
describe('--runner environment handling', () => {
|
||||
it('should set runner based on environment NX_RUNNER, if it is not provided directly on the command', () => {
|
||||
withEnvironment({ NX_RUNNER: 'some-env-runner-name' }, () => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{
|
||||
tasksRunnerOptions: {
|
||||
'some-env-runner-name': { runner: '' },
|
||||
},
|
||||
}
|
||||
).nxArgs.runner
|
||||
).toEqual('some-env-runner-name');
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
runner: 'directlyOnCommand', // higher priority than $NX_RUNNER
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{
|
||||
tasksRunnerOptions: {
|
||||
'some-env-runner-name': { runner: '' },
|
||||
},
|
||||
}
|
||||
).nxArgs.runner
|
||||
).toEqual('directlyOnCommand');
|
||||
});
|
||||
});
|
||||
|
||||
it('should set runner based on environment NX_TASKS_RUNNER, if it is not provided directly on the command', () => {
|
||||
withEnvironment({ NX_TASKS_RUNNER: 'some-env-runner-name' }, () => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{
|
||||
tasksRunnerOptions: {
|
||||
'some-env-runner-name': { runner: '' },
|
||||
},
|
||||
}
|
||||
).nxArgs.runner
|
||||
).toEqual('some-env-runner-name');
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
runner: 'directlyOnCommand', // higher priority than $NX_RUNNER
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{
|
||||
tasksRunnerOptions: {
|
||||
'some-env-runner-name': { runner: '' },
|
||||
},
|
||||
}
|
||||
).nxArgs.runner
|
||||
).toEqual('directlyOnCommand');
|
||||
});
|
||||
});
|
||||
|
||||
it('should prefer NX_TASKS_RUNNER', () => {
|
||||
withEnvironment(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
NX_TASKS_RUNNER: 'some-env-runner-name',
|
||||
NX_RUNNER: 'some-other-runner',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs.runner
|
||||
).toEqual('some-env-runner-name');
|
||||
() => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{
|
||||
tasksRunnerOptions: {
|
||||
'some-env-runner-name': { runner: '' },
|
||||
'some-other-runner': { runner: '' },
|
||||
},
|
||||
}
|
||||
).nxArgs.runner
|
||||
).toEqual('some-env-runner-name');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
it('should ignore runners based on environment, if it is valid', () => {
|
||||
withEnvironment(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
runner: 'directlyOnCommand', // higher priority than $NX_RUNNER
|
||||
NX_TASKS_RUNNER: 'some-env-runner-name',
|
||||
NX_RUNNER: 'some-other-runner',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs.runner
|
||||
).toEqual('directlyOnCommand');
|
||||
|
||||
// Reset process data
|
||||
process.env.NX_RUNNER = originalRunner;
|
||||
() => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndOverrides(
|
||||
{
|
||||
__overrides_unparsed__: ['--notNxArg', 'true', '--override'],
|
||||
$0: '',
|
||||
},
|
||||
'run-one',
|
||||
{} as any,
|
||||
{} as any
|
||||
).nxArgs.runner
|
||||
).not.toBeDefined();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--parallel', () => {
|
||||
@@ -389,3 +480,15 @@ describe('splitArgs', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function withEnvironment(env: Record<string, string>, callback: () => void) {
|
||||
const originalValues: Record<string, string> = {};
|
||||
for (const key in env) {
|
||||
originalValues[key] = process.env[key];
|
||||
process.env[key] = env[key];
|
||||
}
|
||||
callback();
|
||||
for (const key in env) {
|
||||
process.env[key] = originalValues[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,16 +181,7 @@ export function splitArgsIntoNxArgsAndOverrides(
|
||||
nxArgs.skipNxCache = process.env.NX_SKIP_NX_CACHE === 'true';
|
||||
}
|
||||
|
||||
if (!nxArgs.runner && process.env.NX_RUNNER) {
|
||||
nxArgs.runner = process.env.NX_RUNNER;
|
||||
if (options.printWarnings) {
|
||||
output.note({
|
||||
title: `No explicit --runner argument provided, but found environment variable NX_RUNNER so using its value: ${output.bold(
|
||||
`${nxArgs.runner}`
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
normalizeNxArgsRunner(nxArgs, nxJson, options);
|
||||
|
||||
if (args['parallel'] === 'false' || args['parallel'] === false) {
|
||||
nxArgs['parallel'] = 1;
|
||||
@@ -210,6 +201,58 @@ export function splitArgsIntoNxArgsAndOverrides(
|
||||
return { nxArgs, overrides } as any;
|
||||
}
|
||||
|
||||
function normalizeNxArgsRunner(
|
||||
nxArgs: RawNxArgs,
|
||||
nxJson: NxJsonConfiguration<string[] | '*'>,
|
||||
options: { printWarnings: boolean }
|
||||
) {
|
||||
if (!nxArgs.runner) {
|
||||
// TODO: Remove NX_RUNNER environment variable support in Nx v17
|
||||
for (const envKey of ['NX_TASKS_RUNNER', 'NX_RUNNER']) {
|
||||
const runner = process.env[envKey];
|
||||
if (runner) {
|
||||
const runnerExists = nxJson.tasksRunnerOptions?.[runner];
|
||||
if (options.printWarnings) {
|
||||
if (runnerExists) {
|
||||
output.note({
|
||||
title: `No explicit --runner argument provided, but found environment variable ${envKey} so using its value: ${output.bold(
|
||||
`${runner}`
|
||||
)}`,
|
||||
});
|
||||
} else if (
|
||||
nxArgs.verbose ||
|
||||
process.env.NX_VERBOSE_LOGGING === 'true'
|
||||
) {
|
||||
output.warn({
|
||||
title: `Could not find ${output.bold(
|
||||
`${runner}`
|
||||
)} within \`nx.json\` tasksRunnerOptions.`,
|
||||
bodyLines: [
|
||||
`${output.bold(`${runner}`)} was set by ${envKey}`,
|
||||
``,
|
||||
`To suppress this message, either:`,
|
||||
` - provide a valid task runner with --runner`,
|
||||
` - ensure NX_TASKS_RUNNER matches a task runner defined in nx.json`,
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
if (runnerExists) {
|
||||
// TODO: Remove in v17
|
||||
if (envKey === 'NX_RUNNER' && options.printWarnings) {
|
||||
output.warn({
|
||||
title:
|
||||
'NX_RUNNER is deprecated, please use NX_TASKS_RUNNER instead.',
|
||||
});
|
||||
}
|
||||
nxArgs.runner = runner;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function parseFiles(options: NxArgs): { files: string[] } {
|
||||
const { files, uncommitted, untracked, base, head } = options;
|
||||
|
||||
|
||||
@@ -54,6 +54,18 @@ describe('findMatchingProjects', () => {
|
||||
},
|
||||
};
|
||||
|
||||
it('should return no projects when passed no patterns', () => {
|
||||
expect(findMatchingProjects([], projectGraph)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return no projects when passed empty string', () => {
|
||||
expect(findMatchingProjects([''], projectGraph)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not throw when a pattern is empty string', () => {
|
||||
expect(findMatchingProjects(['', 'a'], projectGraph)).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should expand "*"', () => {
|
||||
expect(findMatchingProjects(['*'], projectGraph)).toEqual([
|
||||
'test-project',
|
||||
|
||||
@@ -35,12 +35,20 @@ export function findMatchingProjects(
|
||||
patterns: string[] = [],
|
||||
projects: ProjectNodeMap
|
||||
): string[] {
|
||||
if (!patterns.length || patterns.filter((p) => p.length).length === 0) {
|
||||
return []; // Short circuit if called with no patterns
|
||||
}
|
||||
|
||||
const projectNames = keys(projects);
|
||||
|
||||
const selectedProjects: Set<string> = new Set();
|
||||
const excludedProjects: Set<string> = new Set();
|
||||
|
||||
for (const stringPattern of patterns) {
|
||||
if (!stringPattern.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pattern = parseStringPattern(stringPattern, projects);
|
||||
|
||||
// Handle wildcard with short-circuit, as its a common case with potentially
|
||||
@@ -254,16 +262,19 @@ function isValidPatternType(type: string): type is ProjectPatternType {
|
||||
export const getMatchingStringsWithCache = (() => {
|
||||
// Map< Pattern, Map< Item, Result >>
|
||||
const minimatchCache = new Map<string, Map<string, boolean>>();
|
||||
const regexCache = new Map<string, RegExp>();
|
||||
return (pattern: string, items: string[]) => {
|
||||
if (!minimatchCache.has(pattern)) {
|
||||
minimatchCache.set(pattern, new Map());
|
||||
}
|
||||
const patternCache = minimatchCache.get(pattern)!;
|
||||
let matcher = null;
|
||||
if (!regexCache.has(pattern)) {
|
||||
regexCache.set(pattern, minimatch.makeRe(pattern));
|
||||
}
|
||||
const matcher = regexCache.get(pattern);
|
||||
return items.filter((item) => {
|
||||
let entry = patternCache.get(item);
|
||||
if (entry === undefined || entry === null) {
|
||||
matcher ??= minimatch.makeRe(pattern);
|
||||
entry = item === pattern ? true : matcher.test(item);
|
||||
patternCache.set(item, entry);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
import type { CypressExecutorOptions } from '@nx/cypress/src/executors/cypress/cypress.impl';
|
||||
import {
|
||||
ExecutorContext,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
parseTargetString,
|
||||
ProjectGraph,
|
||||
@@ -19,7 +20,8 @@ import {
|
||||
getProjectConfigByPath,
|
||||
} from '@nx/cypress/src/utils/ct-helpers';
|
||||
|
||||
import type { Configuration } from 'webpack';
|
||||
import { existsSync, lstatSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
type ViteDevServer = {
|
||||
framework: 'react';
|
||||
bundler: 'vite';
|
||||
@@ -66,6 +68,36 @@ export function nxComponentTestingPreset(
|
||||
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
|
||||
devServer: {
|
||||
...({ framework: 'react', bundler: 'vite' } as const),
|
||||
viteConfig: async () => {
|
||||
const normalizedPath = ['.ts', '.js'].some((ext) =>
|
||||
pathToConfig.endsWith(ext)
|
||||
)
|
||||
? pathToConfig
|
||||
: dirname(pathToConfig);
|
||||
const viteConfigPath = findViteConfig(normalizedPath);
|
||||
|
||||
const { mergeConfig, loadConfigFromFile, searchForWorkspaceRoot } =
|
||||
(await import('vite')) as typeof import('vite');
|
||||
|
||||
const resolved = await loadConfigFromFile(
|
||||
{
|
||||
mode: 'watch',
|
||||
command: 'serve',
|
||||
},
|
||||
viteConfigPath
|
||||
);
|
||||
return mergeConfig(resolved.config, {
|
||||
server: {
|
||||
fs: {
|
||||
allow: [
|
||||
searchForWorkspaceRoot(normalizedPath),
|
||||
workspaceRoot,
|
||||
joinPathFragments(workspaceRoot, 'node_modules/vite'),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -232,3 +264,12 @@ function buildTargetWebpack(
|
||||
};
|
||||
}
|
||||
}
|
||||
function findViteConfig(projectRootFullPath: string): string {
|
||||
const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts'];
|
||||
|
||||
for (const ext of allowsExt) {
|
||||
if (existsSync(join(projectRootFullPath, `vite.config.${ext}`))) {
|
||||
return join(projectRootFullPath, `vite.config.${ext}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,13 @@ export async function addFiles(
|
||||
addDependenciesToPackageJson(tree, {}, { '@nx/webpack': nxVersion });
|
||||
}
|
||||
|
||||
if (
|
||||
options.bundler === 'vite' ||
|
||||
(!options.bundler && actualBundler === 'vite')
|
||||
) {
|
||||
addDependenciesToPackageJson(tree, {}, { '@nx/vite': nxVersion });
|
||||
}
|
||||
|
||||
if (options.generateTests) {
|
||||
const filePaths = [];
|
||||
visitNotIgnoredFiles(tree, projectConfig.sourceRoot, (filePath) => {
|
||||
|
||||
@@ -384,6 +384,15 @@ describe('lib', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('--globalCss', () => {
|
||||
it('should not generate .module styles', async () => {
|
||||
await libraryGenerator(tree, { ...defaultSchema, globalCss: true });
|
||||
|
||||
expect(tree.exists('libs/my-lib/src/lib/my-lib.css'));
|
||||
expect(tree.exists('libs/my-lib/src/lib/my-lib.module.css')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('--unit-test-runner none', () => {
|
||||
it('should not generate test configuration', async () => {
|
||||
await libraryGenerator(tree, {
|
||||
|
||||
@@ -141,6 +141,7 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
|
||||
pascalCaseFiles: options.pascalCaseFiles,
|
||||
inSourceTests: options.inSourceTests,
|
||||
skipFormat: true,
|
||||
globalCss: options.globalCss,
|
||||
});
|
||||
tasks.push(componentTask);
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ const IGNORE_MATCHES_IN_PACKAGE = {
|
||||
'url-loader',
|
||||
'webpack',
|
||||
'webpack-merge',
|
||||
// used via the CT react plugin installed via vite plugin
|
||||
'vite',
|
||||
],
|
||||
'react-native': ['@nx/storybook'],
|
||||
rollup: ['@swc/core'],
|
||||
|
||||
Reference in New Issue
Block a user