Compare commits
65 Commits
check-java
...
15.9.7
| Author | SHA1 | Date | |
|---|---|---|---|
| 73407b61cc | |||
| 3a85b546a7 | |||
| d5c9f5139c | |||
| 57aa1335fc | |||
| f770780e7c | |||
| 2e2a2131b1 | |||
| 06c3e5c9e1 | |||
| 8293700ad4 | |||
| d4946f002c | |||
| 16a2b91106 | |||
| 8f6ec8c438 | |||
| 226cd68921 | |||
| 80792b60b9 | |||
| 33303fe107 | |||
| a62db8a989 | |||
| 7008532aad | |||
| 2742ed1e12 | |||
| ddbf33bbd6 | |||
| 6ed88eff68 | |||
| 0ab5be86c8 | |||
| 2ca198b00b | |||
| 1f714fd722 | |||
| 857bde7a7b | |||
| 426f22a46f | |||
| 037919a033 | |||
| b6d1cad56a | |||
| e9725e91b5 | |||
| fafe2e451b | |||
| 121c6bd3c3 | |||
| 9dfc66cc99 | |||
| 44a0c83860 | |||
| 33a691ba36 | |||
| ffe3fb1de6 | |||
| 7abdfd4fb3 | |||
| 8c4d791309 | |||
| b6e55d8e95 | |||
| f552169f1a | |||
| 2baf547612 | |||
| 4f67552bf3 | |||
| 6fd9038959 | |||
| 5989783f1b | |||
| 2361aea4ad | |||
| cb5f4fda7e | |||
| 86e9c031eb | |||
| ec0dff3e3e | |||
| c46f81b551 | |||
| 0867dbaa96 | |||
| 926908e314 | |||
| 8754cd2c75 | |||
| f960776563 | |||
| ac448874f7 | |||
| 5000fa2bac | |||
| b6ae2da4c6 | |||
| 995f1a8cdb | |||
| 9a0ab09716 | |||
| 2b5e7d7d79 | |||
| 3bcc072182 | |||
| 20cb87b6df | |||
| 1ccff1c424 | |||
| d6bb263c2e | |||
| d6b80b3239 | |||
| c4b602a251 | |||
| 6f5dbd9321 | |||
| fcaf69caf1 | |||
| 6b398d6f14 |
@@ -3723,6 +3723,22 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "karma",
|
||||
"path": "/packages/angular/generators/karma",
|
||||
"name": "karma",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "karma-project",
|
||||
"path": "/packages/angular/generators/karma-project",
|
||||
"name": "karma-project",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "library",
|
||||
"path": "/packages/angular/generators/library",
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||
"name": "angular",
|
||||
"packageName": "@nrwl/angular",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"documents": {
|
||||
"/packages/angular/documents/overview": {
|
||||
"id": "overview",
|
||||
"name": "Overview",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"file": "generated/packages/angular/documents/overview",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
@@ -31,7 +31,7 @@
|
||||
"/packages/angular/documents/angular-nx-version-matrix": {
|
||||
"id": "angular-nx-version-matrix",
|
||||
"name": "Angular and Nx Version Matrix",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"file": "generated/packages/angular/documents/angular-nx-version-matrix",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
@@ -198,6 +198,24 @@
|
||||
"path": "/packages/angular/generators/init",
|
||||
"type": "generator"
|
||||
},
|
||||
"/packages/angular/generators/karma": {
|
||||
"description": "Adds Karma configuration to a workspace.",
|
||||
"file": "generated/packages/angular/generators/karma.json",
|
||||
"hidden": false,
|
||||
"name": "karma",
|
||||
"originalFilePath": "/packages/angular/src/generators/karma/schema.json",
|
||||
"path": "/packages/angular/generators/karma",
|
||||
"type": "generator"
|
||||
},
|
||||
"/packages/angular/generators/karma-project": {
|
||||
"description": "Adds Karma configuration to a project.",
|
||||
"file": "generated/packages/angular/generators/karma-project.json",
|
||||
"hidden": false,
|
||||
"name": "karma-project",
|
||||
"originalFilePath": "/packages/angular/src/generators/karma-project/schema.json",
|
||||
"path": "/packages/angular/generators/karma-project",
|
||||
"type": "generator"
|
||||
},
|
||||
"/packages/angular/generators/library": {
|
||||
"description": "Creates an Angular library.",
|
||||
"file": "generated/packages/angular/generators/library.json",
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
"source": "/packages/add-nx-to-monorepo/src"
|
||||
},
|
||||
{
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"documents": [
|
||||
{
|
||||
"id": "overview",
|
||||
"name": "Overview",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"file": "generated/packages/angular/documents/overview",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
@@ -27,7 +27,7 @@
|
||||
{
|
||||
"id": "angular-nx-version-matrix",
|
||||
"name": "Angular and Nx Version Matrix",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"file": "generated/packages/angular/documents/angular-nx-version-matrix",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
@@ -192,6 +192,24 @@
|
||||
"path": "angular/generators/init",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Adds Karma configuration to a workspace.",
|
||||
"file": "generated/packages/angular/generators/karma.json",
|
||||
"hidden": false,
|
||||
"name": "karma",
|
||||
"originalFilePath": "/packages/angular/src/generators/karma/schema.json",
|
||||
"path": "angular/generators/karma",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Adds Karma configuration to a project.",
|
||||
"file": "generated/packages/angular/generators/karma-project.json",
|
||||
"hidden": false,
|
||||
"name": "karma-project",
|
||||
"originalFilePath": "/packages/angular/src/generators/karma-project/schema.json",
|
||||
"path": "angular/generators/karma-project",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Creates an Angular library.",
|
||||
"file": "generated/packages/angular/generators/library.json",
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"description": "The HTML selector to use for this directive."
|
||||
},
|
||||
"standalone": {
|
||||
"description": "Whether the generated directive is standalone.",
|
||||
"description": "Whether the generated directive is standalone. _Note: This is only supported in Angular versions >= 14.1.0_.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"properties": {
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest",
|
||||
"x-priority": "important"
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -153,8 +153,7 @@
|
||||
},
|
||||
"thirdParty": {
|
||||
"type": "boolean",
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages).",
|
||||
"default": true
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages)."
|
||||
},
|
||||
"dependenciesFieldType": {
|
||||
"type": "string",
|
||||
|
||||
@@ -56,11 +56,17 @@ You can generate Storybook configuration for an individual project with this com
|
||||
nx g @nrwl/storybook:configuration project-name --storybook7Configuration --storybook7UiFramework=@storybook/react-webpack5
|
||||
```
|
||||
|
||||
Make sure to use the framework-specific generators if your project is using Angular, React, Next.js or React Native: [`@nrwl/angular:storybook-configuration`](/packages/angular/generators/storybook-configuration), [`@nrwl/react:storybook-configuration`](/packages/react/generators/storybook-configuration), [`@nrwl/react-native:storybook-configuration`](/packages/react-native/generators/storybook-configuration), for example:
|
||||
|
||||
```shell
|
||||
nx g @nrwl/angular:storybook-configuration my-angular-project --storybook7Configuration
|
||||
```
|
||||
|
||||
{% callout type="info" title="For Nx versions <15.9" %}
|
||||
The flag is called `storybook7betaConfiguration` for Nx versions <15.9.
|
||||
{% /callout %}
|
||||
|
||||
In the field `storybook7UiFramework` you must choose one of the following Storybook frameworks:
|
||||
If you are NOT using a framework-specific generator, in the field `storybook7UiFramework` you must choose one of the following Storybook frameworks:
|
||||
|
||||
- `@storybook/angular`
|
||||
- `@storybook/html-webpack5`
|
||||
@@ -81,7 +87,7 @@ In the field `storybook7UiFramework` you must choose one of the following Storyb
|
||||
|
||||
In Storybook 7, [the `framework` field in `.storybook/main.js|ts` is mandatory](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-field-mandatory). You must choose one of the above frameworks when setting up your application.
|
||||
|
||||
If you are using one of the framework-specific generators (e.g. [`@nrwl/angular:storybook-configuration`](/packages/angular/generators/storybook-configuration), or [`@nrwl/react:storybook-configuration`](/packages/react/generators/storybook-configuration) for React and Nextjs projects, or [`@nrwl/react-native:storybook-configuration`](/packages/react-native/generators/storybook-configuration), the generator will automatically choose the correct framework for you.
|
||||
If you are using one of the framework-specific generators, the generator will automatically choose the correct framework for you.
|
||||
|
||||
Choosing one of these frameworks will have the following effects on your workspace:
|
||||
|
||||
@@ -89,7 +95,7 @@ Choosing one of these frameworks will have the following effects on your workspa
|
||||
|
||||
2. Nx will generate a root `.storybook` folder and a project-level `.storybook` folder (located under `libs/your-project/.storybook` or `apps/your-project/.storybook`) containing the essential configuration files for Storybook.
|
||||
|
||||
3. If you are working on an Angular, a React or a React Native project (and you choose `@storybook/angular`, `@storybook/react` or `@storybook/react-native`) the Nx generator will also generate stories for all the components in your project.
|
||||
3. If you are working on an Angular, a React or a React Native project and you use one of the framework-specific generators, Nx will also generate stories for all the components in your project.
|
||||
|
||||
4. Nx will create new `targets` in your project's `project.json`, called `storybook` and `build-storybook`, containing all the necessary configuration to serve and build Storybook.
|
||||
|
||||
|
||||
@@ -56,11 +56,17 @@ You can generate Storybook configuration for an individual project with this com
|
||||
nx g @nrwl/storybook:configuration project-name --storybook7Configuration --storybook7UiFramework=@storybook/react-webpack5
|
||||
```
|
||||
|
||||
Make sure to use the framework-specific generators if your project is using Angular, React, Next.js or React Native: [`@nrwl/angular:storybook-configuration`](/packages/angular/generators/storybook-configuration), [`@nrwl/react:storybook-configuration`](/packages/react/generators/storybook-configuration), [`@nrwl/react-native:storybook-configuration`](/packages/react-native/generators/storybook-configuration), for example:
|
||||
|
||||
```shell
|
||||
nx g @nrwl/angular:storybook-configuration my-angular-project --storybook7Configuration
|
||||
```
|
||||
|
||||
{% callout type="info" title="For Nx versions <15.9" %}
|
||||
The flag is called `storybook7betaConfiguration` for Nx versions <15.9.
|
||||
{% /callout %}
|
||||
|
||||
In the field `storybook7UiFramework` you must choose one of the following Storybook frameworks:
|
||||
If you are NOT using a framework-specific generator, in the field `storybook7UiFramework` you must choose one of the following Storybook frameworks:
|
||||
|
||||
- `@storybook/angular`
|
||||
- `@storybook/html-webpack5`
|
||||
@@ -81,7 +87,7 @@ In the field `storybook7UiFramework` you must choose one of the following Storyb
|
||||
|
||||
In Storybook 7, [the `framework` field in `.storybook/main.js|ts` is mandatory](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-field-mandatory). You must choose one of the above frameworks when setting up your application.
|
||||
|
||||
If you are using one of the framework-specific generators (e.g. [`@nrwl/angular:storybook-configuration`](/packages/angular/generators/storybook-configuration), or [`@nrwl/react:storybook-configuration`](/packages/react/generators/storybook-configuration) for React and Nextjs projects, or [`@nrwl/react-native:storybook-configuration`](/packages/react-native/generators/storybook-configuration), the generator will automatically choose the correct framework for you.
|
||||
If you are using one of the framework-specific generators, the generator will automatically choose the correct framework for you.
|
||||
|
||||
Choosing one of these frameworks will have the following effects on your workspace:
|
||||
|
||||
@@ -89,7 +95,7 @@ Choosing one of these frameworks will have the following effects on your workspa
|
||||
|
||||
2. Nx will generate a root `.storybook` folder and a project-level `.storybook` folder (located under `libs/your-project/.storybook` or `apps/your-project/.storybook`) containing the essential configuration files for Storybook.
|
||||
|
||||
3. If you are working on an Angular, a React or a React Native project (and you choose `@storybook/angular`, `@storybook/react` or `@storybook/react-native`) the Nx generator will also generate stories for all the components in your project.
|
||||
3. If you are working on an Angular, a React or a React Native project and you use one of the framework-specific generators, Nx will also generate stories for all the components in your project.
|
||||
|
||||
4. Nx will create new `targets` in your project's `project.json`, called `storybook` and `build-storybook`, containing all the necessary configuration to serve and build Storybook.
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
updateFile,
|
||||
updateProjectConfig,
|
||||
removeFile,
|
||||
checkFilesExist,
|
||||
} from '../../utils';
|
||||
import { names } from '@nrwl/devkit';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('Angular Cypress Component Tests', () => {
|
||||
let projectName: string;
|
||||
@@ -114,6 +116,20 @@ describe('Angular Cypress Component Tests', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should use root level tailwinds config', () => {
|
||||
useRootLevelTailwindConfig(
|
||||
join('libs', buildableLibName, 'tailwind.config.js')
|
||||
);
|
||||
checkFilesExist('tailwind.config.js');
|
||||
checkFilesDoNotExist(`libs/${buildableLibName}/tailwind.config.js`);
|
||||
|
||||
if (runCypressTests()) {
|
||||
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
|
||||
'All specs passed!'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function createApp(appName: string) {
|
||||
@@ -386,3 +402,21 @@ function updateBuilableLibTestsToAssertAppStyles(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function useRootLevelTailwindConfig(existingConfigPath: string) {
|
||||
createFile(
|
||||
'tailwind.config.js',
|
||||
`const { join } = require('path');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [join(__dirname, '**/*.{html,js,ts}')],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
`
|
||||
);
|
||||
removeFile(existingConfigPath);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,9 @@ describe('expo', () => {
|
||||
expect(prebuildResult.combinedOutput).toContain('Config synced');
|
||||
});
|
||||
|
||||
it('should install', async () => {
|
||||
// TODO(emily): expo-cli always fetches the latest version of react native
|
||||
// re-enable it when expo-cli is fixed
|
||||
xit('should install', async () => {
|
||||
// run install command
|
||||
const installResults = await runCLIAsync(
|
||||
`install ${appName} --no-interactive --check`
|
||||
|
||||
@@ -134,8 +134,9 @@ export function x() {
|
||||
|
||||
// update .swcrc to use path mappings
|
||||
updateJson(`libs/${lib}/.swcrc`, (json) => {
|
||||
json.jsc.baseUrl = '.';
|
||||
json.jsc.paths = {
|
||||
'src/*': ['src/*'],
|
||||
'~/*': ['./src/*'],
|
||||
};
|
||||
return json;
|
||||
});
|
||||
@@ -144,7 +145,7 @@ export function x() {
|
||||
updateFile(`libs/${lib}/src/lib/${lib}.ts`, () => {
|
||||
return `
|
||||
// @ts-ignore
|
||||
import { x } from 'src/x';
|
||||
import { x } from '~/x';
|
||||
|
||||
export function myLib() {
|
||||
console.log(x());
|
||||
@@ -154,8 +155,8 @@ myLib();
|
||||
`;
|
||||
});
|
||||
|
||||
// now run build
|
||||
runCLI(`build ${lib}`);
|
||||
// now run build without type checking (since we're using path mappings not in tsconfig)
|
||||
runCLI(`build ${lib} --skipTypeCheck`);
|
||||
|
||||
// invoke the lib with node
|
||||
const result = execSync(`node dist/libs/${lib}/src/lib/${lib}.js`, {
|
||||
|
||||
@@ -37,9 +37,18 @@ describe('nx init (for React)', () => {
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.devDependencies['@nrwl/jest']).toBeDefined();
|
||||
expect(packageJson.devDependencies['@nrwl/vite']).toBeUndefined();
|
||||
expect(packageJson.devDependencies['@nrwl/webpack']).toBeDefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/jest'] ||
|
||||
packageJson.devDependencies['@nx/jest']
|
||||
).toBeDefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/vite'] ||
|
||||
packageJson.devDependencies['@nx/vite']
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/webpack'] ||
|
||||
packageJson.devDependencies['@nx/webpack']
|
||||
).toBeDefined();
|
||||
|
||||
runCLI(`build ${appName}`, {
|
||||
env: {
|
||||
@@ -68,9 +77,18 @@ describe('nx init (for React)', () => {
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.devDependencies['@nrwl/jest']).toBeUndefined();
|
||||
expect(packageJson.devDependencies['@nrwl/vite']).toBeDefined();
|
||||
expect(packageJson.devDependencies['@nrwl/webpack']).toBeUndefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/jest'] ||
|
||||
packageJson.devDependencies['@nx/jest']
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/vite'] ||
|
||||
packageJson.devDependencies['@nx/vite']
|
||||
).toBeDefined();
|
||||
expect(
|
||||
packageJson.devDependencies['@nrwl/webpack'] ||
|
||||
packageJson.devDependencies['@nx/webpack']
|
||||
).toBeUndefined();
|
||||
|
||||
const viteConfig = readFile(`apps/${appName}/vite.config.js`);
|
||||
expect(viteConfig).toContain('port: 4200'); // default port
|
||||
|
||||
@@ -13,8 +13,9 @@ import { ChildProcess, exec, execSync, ExecSyncOptions } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import * as isCI from 'is-ci';
|
||||
import { Workspaces } from '../../packages/nx/src/config/workspaces';
|
||||
import { updateFile } from './file-utils';
|
||||
import { exists, updateFile } from './file-utils';
|
||||
import { logError, stripConsoleColors } from './log-utils';
|
||||
import { existsSync } from 'fs-extra';
|
||||
|
||||
export interface RunCmdOpts {
|
||||
silenceError?: boolean;
|
||||
@@ -118,6 +119,7 @@ export function getPackageManagerCommand({
|
||||
} {
|
||||
const npmMajorVersion = getNpmMajorVersion();
|
||||
const publishedVersion = getPublishedVersion();
|
||||
const isPnpmWorkspace = existsSync(join(path, 'pnpm-workspace.yaml'));
|
||||
|
||||
return {
|
||||
npm: {
|
||||
@@ -158,8 +160,8 @@ export function getPackageManagerCommand({
|
||||
runUninstalledPackage: 'pnpm dlx',
|
||||
install: 'pnpm i',
|
||||
ciInstall: 'pnpm install --frozen-lockfile',
|
||||
addProd: `pnpm add`,
|
||||
addDev: `pnpm add -D`,
|
||||
addProd: isPnpmWorkspace ? 'pnpm add -w' : 'pnpm add',
|
||||
addDev: isPnpmWorkspace ? 'pnpm add -Dw' : 'pnpm add -D',
|
||||
list: 'npm ls --depth 10',
|
||||
runLerna: `pnpm exec lerna`,
|
||||
},
|
||||
|
||||
@@ -292,6 +292,8 @@ export default defineConfig({
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['junit'],
|
||||
outputFile: 'junit.xml',
|
||||
coverage: {
|
||||
enabled: true,
|
||||
reportsDirectory: 'coverage',
|
||||
@@ -313,6 +315,7 @@ export default defineConfig({
|
||||
expect(results).toContain(
|
||||
`Successfully ran target test for project ${lib}`
|
||||
);
|
||||
expect(results).toContain(`JUNIT report written`);
|
||||
}, 100_000);
|
||||
|
||||
it('should be able to run tests with inSourceTests set to true', async () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||
'^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
maxWorkers: 1,
|
||||
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||
globals: {},
|
||||
displayName: 'e2e-workspace-create-npm',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
|
||||
@@ -1,33 +1,48 @@
|
||||
import {
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
getSelectedPackageManager,
|
||||
packageInstall,
|
||||
readJson,
|
||||
runCLI,
|
||||
runCommand,
|
||||
runCreateWorkspace,
|
||||
uniq,
|
||||
} from '@nrwl/e2e/utils';
|
||||
|
||||
describe('create-nx-workspace --preset=npm', () => {
|
||||
let wsName;
|
||||
const wsName = uniq('npm');
|
||||
|
||||
let orginalGlobCache;
|
||||
|
||||
beforeAll(() => {
|
||||
orginalGlobCache = process.env.NX_PROJECT_GLOB_CACHE;
|
||||
// glob cache is causing previous projects to show in Workspace for maxWorkers overrides
|
||||
// which fails due to files no longer being available
|
||||
process.env.NX_PROJECT_GLOB_CACHE = 'false';
|
||||
|
||||
beforeEach(() => {
|
||||
wsName = uniq('npm');
|
||||
runCreateWorkspace(wsName, {
|
||||
preset: 'npm',
|
||||
packageManager: getSelectedPackageManager(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => cleanupProject());
|
||||
afterEach(() => {
|
||||
// cleanup previous projects
|
||||
runCommand(`rm -rf packages/** tsconfig.base.json`);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.NX_PROJECT_GLOB_CACHE = orginalGlobCache;
|
||||
cleanupProject({ skipReset: true });
|
||||
});
|
||||
|
||||
it('should add angular application', () => {
|
||||
packageInstall('@nrwl/angular', wsName);
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/angular:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
}, 1_000_000);
|
||||
@@ -37,9 +52,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/angular:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/angular:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -48,32 +61,13 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
});
|
||||
}, 1_000_000);
|
||||
|
||||
it('should add workspace library', () => {
|
||||
packageInstall('@nrwl/workspace', wsName);
|
||||
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() =>
|
||||
runCLI(
|
||||
`generate @nrwl/workspace:library ${libName} --skipPackageJson --no-interactive`
|
||||
)
|
||||
).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
expect(tsconfig.compilerOptions.paths).toEqual({
|
||||
[libName]: [`packages/${libName}/src/index.ts`],
|
||||
});
|
||||
});
|
||||
|
||||
it('should add js library', () => {
|
||||
packageInstall('@nrwl/js', wsName);
|
||||
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() =>
|
||||
runCLI(
|
||||
`generate @nrwl/js:library ${libName} --skipPackageJson --no-interactive`
|
||||
)
|
||||
runCLI(`generate @nrwl/js:library ${libName} --no-interactive`)
|
||||
).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -88,9 +82,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() =>
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --skipPackageJson --no-interactive`
|
||||
)
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`)
|
||||
).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
@@ -101,9 +93,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
@@ -114,9 +104,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -131,9 +119,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/next:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/next:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
@@ -144,9 +130,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/next:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/next:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -162,7 +146,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/react-native:app ${appName} --install=false --skipPackageJson --no-interactive`
|
||||
`generate @nrwl/react-native:app ${appName} --install=false --no-interactive`
|
||||
);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
@@ -174,9 +158,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/react-native:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/react-native:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -191,9 +173,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/node:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/node:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
@@ -204,9 +184,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/node:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/node:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -221,9 +199,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/nest:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/nest:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
@@ -234,9 +210,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/nest:lib ${libName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/nest:lib ${libName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
const tsconfig = readJson(`tsconfig.base.json`);
|
||||
@@ -251,9 +225,7 @@ describe('create-nx-workspace --preset=npm', () => {
|
||||
const appName = uniq('my-app');
|
||||
|
||||
expect(() => {
|
||||
runCLI(
|
||||
`generate @nrwl/express:app ${appName} --skipPackageJson --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/express:app ${appName} --no-interactive`);
|
||||
}).not.toThrowError();
|
||||
checkFilesExist('tsconfig.base.json');
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
|
||||
"version": "15.9.2",
|
||||
"version": "15.9.7",
|
||||
"granularPathspec": false,
|
||||
"command": {
|
||||
"publish": {
|
||||
|
||||
@@ -32,6 +32,14 @@ describe('nx-dev: Packages Section', () => {
|
||||
path: '/packages/angular/generators/convert-tslint-to-eslint',
|
||||
},
|
||||
{ title: '@nrwl/angular:init', path: '/packages/angular/generators/init' },
|
||||
{
|
||||
title: '@nrwl/angular:karma',
|
||||
path: '/packages/angular/generators/karma',
|
||||
},
|
||||
{
|
||||
title: '@nrwl/angular:karma-project',
|
||||
path: '/packages/angular/generators/karma-project',
|
||||
},
|
||||
{
|
||||
title: '@nrwl/angular:library',
|
||||
path: '/packages/angular/generators/library',
|
||||
|
||||
+3
-3
@@ -115,7 +115,7 @@
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"@types/react-router-dom": "5.1.7",
|
||||
"@types/semver": "^7.3.8",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@types/tar-stream": "^2.2.2",
|
||||
"@types/tmp": "^0.2.0",
|
||||
"@types/yargs": "^17.0.10",
|
||||
@@ -228,7 +228,7 @@
|
||||
"rxjs-for-await": "0.0.2",
|
||||
"sass": "1.55.0",
|
||||
"sass-loader": "^12.2.0",
|
||||
"semver": "7.3.4",
|
||||
"semver": "7.5.4",
|
||||
"source-map": "0.7.3",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"source-map-support": "0.5.19",
|
||||
@@ -287,7 +287,7 @@
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.7",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"@yarnpkg/parsers": "^3.0.0-rc.18",
|
||||
"@yarnpkg/parsers": "3.0.0-rc.46",
|
||||
"@zkochan/js-yaml": "0.0.6",
|
||||
"axios": "1.0.0",
|
||||
"classnames": "^2.3.1",
|
||||
|
||||
@@ -50,6 +50,16 @@
|
||||
"description": "Initializes the `@nrwl/angular` plugin.",
|
||||
"hidden": true
|
||||
},
|
||||
"karma": {
|
||||
"factory": "./src/generators/karma/compat",
|
||||
"schema": "./src/generators/karma/schema.json",
|
||||
"description": "Adds Karma configuration to a workspace."
|
||||
},
|
||||
"karma-project": {
|
||||
"factory": "./src/generators/karma-project/compat",
|
||||
"schema": "./src/generators/karma-project/schema.json",
|
||||
"description": "Adds Karma configuration to a project."
|
||||
},
|
||||
"library": {
|
||||
"factory": "./src/generators/library/library.compat#librarySchematic",
|
||||
"schema": "./src/generators/library/schema.json",
|
||||
@@ -207,6 +217,16 @@
|
||||
"description": "Initializes the `@nrwl/angular` plugin.",
|
||||
"hidden": true
|
||||
},
|
||||
"karma": {
|
||||
"factory": "./src/generators/karma/karma",
|
||||
"schema": "./src/generators/karma/schema.json",
|
||||
"description": "Adds Karma configuration to a workspace."
|
||||
},
|
||||
"karma-project": {
|
||||
"factory": "./src/generators/karma-project/karma-project",
|
||||
"schema": "./src/generators/karma-project/schema.json",
|
||||
"description": "Adds Karma configuration to a project."
|
||||
},
|
||||
"library": {
|
||||
"factory": "./src/generators/library/library",
|
||||
"schema": "./src/generators/library/schema.json",
|
||||
|
||||
@@ -7,6 +7,8 @@ export * from './src/generators/convert-tslint-to-eslint/convert-tslint-to-eslin
|
||||
export * from './src/generators/directive/directive';
|
||||
export * from './src/generators/host/host';
|
||||
export * from './src/generators/init/init';
|
||||
export * from './src/generators/karma-project/karma-project';
|
||||
export * from './src/generators/karma/karma';
|
||||
export * from './src/generators/library-secondary-entry-point/library-secondary-entry-point';
|
||||
export * from './src/generators/library/library';
|
||||
export * from './src/generators/move/move';
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"factory": "./src/migrations/update-15-2-0/update-workspace-config"
|
||||
},
|
||||
"update-platform-server-exports": {
|
||||
"cli": "ng",
|
||||
"cli": "nx",
|
||||
"version": "15.2.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=15.0.0"
|
||||
@@ -130,7 +130,7 @@
|
||||
"factory": "./src/migrations/update-15-2-0/remove-platform-server-exports"
|
||||
},
|
||||
"update-karma-main-file": {
|
||||
"cli": "ng",
|
||||
"cli": "nx",
|
||||
"version": "15.2.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=15.0.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@nrwl/angular",
|
||||
"version": "0.0.1",
|
||||
"private": false,
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Cypress and Karma. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Upgrading AngularJS applications \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nrwl/nx.git",
|
||||
@@ -50,7 +50,7 @@
|
||||
"ignore": "^5.0.4",
|
||||
"magic-string": "~0.26.2",
|
||||
"minimatch": "3.0.5",
|
||||
"semver": "7.3.4",
|
||||
"semver": "7.5.4",
|
||||
"ts-node": "10.9.1",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"webpack": "^5.75.0",
|
||||
|
||||
@@ -19,11 +19,11 @@ import {
|
||||
readCachedProjectGraph,
|
||||
readTargetOptions,
|
||||
stripIndents,
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { existsSync, lstatSync, mkdirSync, writeFileSync } from 'fs';
|
||||
import { dirname, join, relative, sep } from 'path';
|
||||
import type { BrowserBuilderSchema } from '../src/builders/webpack-browser/schema';
|
||||
import { gte } from 'semver';
|
||||
|
||||
/**
|
||||
* Angular nx preset for Cypress Component Testing
|
||||
@@ -78,11 +78,14 @@ ${e.stack ? e.stack : e}`
|
||||
Has project config? ${!!graph.nodes?.[buildTarget.project]?.data}`);
|
||||
}
|
||||
|
||||
const fromWorkspaceRoot = relative(workspaceRoot, pathToConfig);
|
||||
const fromWorkspaceRoot = relative(ctContext.root, pathToConfig);
|
||||
const normalizedFromWorkspaceRootPath = lstatSync(pathToConfig).isFile()
|
||||
? dirname(fromWorkspaceRoot)
|
||||
: fromWorkspaceRoot;
|
||||
const offset = offsetFromRoot(normalizedFromWorkspaceRootPath);
|
||||
const offset = isOffsetNeeded(ctContext, ctProjectConfig)
|
||||
? offsetFromRoot(normalizedFromWorkspaceRootPath)
|
||||
: undefined;
|
||||
|
||||
const buildContext = createExecutorContext(
|
||||
graph,
|
||||
graph.nodes[buildTarget.project]?.data.targets,
|
||||
@@ -101,6 +104,15 @@ ${e.stack ? e.stack : e}`
|
||||
...nxBaseCypressPreset(pathToConfig),
|
||||
// NOTE: cannot use a glob pattern since it will break cypress generated tsconfig.
|
||||
specPattern: ['src/**/*.cy.ts', 'src/**/*.cy.js'],
|
||||
// cypress defaults to a relative path from the workspaceRoot instead of projectRoot
|
||||
// set as absolute path in case this changes internally to cypress, this path isn't OS dependent
|
||||
indexHtmlFile: joinPathFragments(
|
||||
ctContext.root,
|
||||
ctProjectConfig.root,
|
||||
'cypress',
|
||||
'support',
|
||||
'component-index.html'
|
||||
),
|
||||
devServer: {
|
||||
// cypress uses string union type,
|
||||
// need to use const to prevent typing to string
|
||||
@@ -156,8 +168,12 @@ function getBuildableTarget(ctContext: ExecutorContext) {
|
||||
function normalizeBuildTargetOptions(
|
||||
buildContext: ExecutorContext,
|
||||
ctContext: ExecutorContext,
|
||||
offset: string
|
||||
): { root: string; sourceRoot: string; buildOptions: BrowserBuilderSchema } {
|
||||
offset?: string
|
||||
): {
|
||||
root: string;
|
||||
sourceRoot: string;
|
||||
buildOptions: BrowserBuilderSchema & { workspaceRoot: string };
|
||||
} {
|
||||
const options = readTargetOptions<BrowserBuilderSchema>(
|
||||
{
|
||||
project: buildContext.projectName,
|
||||
@@ -168,39 +184,45 @@ function normalizeBuildTargetOptions(
|
||||
);
|
||||
const buildOptions = withSchemaDefaults(options);
|
||||
|
||||
// polyfill entries might be local files or files that are resolved from node_modules
|
||||
// like zone.js.
|
||||
// prevents error from webpack saying can't find <offset>/zone.js.
|
||||
const handlePolyfillPath = (polyfill: string) => {
|
||||
const maybeFullPath = join(workspaceRoot, polyfill.split('/').join(sep));
|
||||
if (existsSync(maybeFullPath)) {
|
||||
return joinPathFragments(offset, polyfill);
|
||||
}
|
||||
return polyfill;
|
||||
};
|
||||
// paths need to be unix paths for angular devkit
|
||||
buildOptions.polyfills =
|
||||
Array.isArray(buildOptions.polyfills) && buildOptions.polyfills.length > 0
|
||||
? (buildOptions.polyfills as string[]).map((p) => handlePolyfillPath(p))
|
||||
: handlePolyfillPath(buildOptions.polyfills as string);
|
||||
|
||||
buildOptions.main = joinPathFragments(offset, buildOptions.main);
|
||||
buildOptions.index =
|
||||
typeof buildOptions.index === 'string'
|
||||
? joinPathFragments(offset, buildOptions.index)
|
||||
: {
|
||||
...buildOptions.index,
|
||||
input: joinPathFragments(offset, buildOptions.index.input),
|
||||
};
|
||||
// cypress creates a tsconfig if one isn't preset
|
||||
// that contains all the support required for angular and component tests
|
||||
delete buildOptions.tsConfig;
|
||||
|
||||
buildOptions.fileReplacements = buildOptions.fileReplacements.map((fr) => {
|
||||
fr.replace = joinPathFragments(offset, fr.replace);
|
||||
fr.with = joinPathFragments(offset, fr.with);
|
||||
return fr;
|
||||
});
|
||||
if (offset) {
|
||||
// polyfill entries might be local files or files that are resolved from node_modules
|
||||
// like zone.js.
|
||||
// prevents error from webpack saying can't find <offset>/zone.js.
|
||||
const handlePolyfillPath = (polyfill: string) => {
|
||||
const maybeFullPath = join(ctContext.root, polyfill.split('/').join(sep));
|
||||
if (existsSync(maybeFullPath)) {
|
||||
return joinPathFragments(offset, polyfill);
|
||||
}
|
||||
return polyfill;
|
||||
};
|
||||
// paths need to be unix paths for angular devkit
|
||||
if (buildOptions.polyfills) {
|
||||
buildOptions.polyfills =
|
||||
Array.isArray(buildOptions.polyfills) &&
|
||||
buildOptions.polyfills.length > 0
|
||||
? (buildOptions.polyfills as string[]).map((p) =>
|
||||
handlePolyfillPath(p)
|
||||
)
|
||||
: handlePolyfillPath(buildOptions.polyfills as string);
|
||||
}
|
||||
buildOptions.main = joinPathFragments(offset, buildOptions.main);
|
||||
buildOptions.index =
|
||||
typeof buildOptions.index === 'string'
|
||||
? joinPathFragments(offset, buildOptions.index)
|
||||
: {
|
||||
...buildOptions.index,
|
||||
input: joinPathFragments(offset, buildOptions.index.input),
|
||||
};
|
||||
buildOptions.fileReplacements = buildOptions.fileReplacements.map((fr) => {
|
||||
fr.replace = joinPathFragments(offset, fr.replace);
|
||||
fr.with = joinPathFragments(offset, fr.with);
|
||||
return fr;
|
||||
});
|
||||
}
|
||||
|
||||
// if the ct project isn't being used in the build project
|
||||
// then we don't want to have the assets/scripts/styles be included to
|
||||
@@ -213,29 +235,31 @@ function normalizeBuildTargetOptions(
|
||||
ctContext.projectName
|
||||
)
|
||||
) {
|
||||
buildOptions.assets = buildOptions.assets.map((asset) => {
|
||||
return typeof asset === 'string'
|
||||
? joinPathFragments(offset, asset)
|
||||
: { ...asset, input: joinPathFragments(offset, asset.input) };
|
||||
});
|
||||
buildOptions.styles = buildOptions.styles.map((style) => {
|
||||
return typeof style === 'string'
|
||||
? joinPathFragments(offset, style)
|
||||
: { ...style, input: joinPathFragments(offset, style.input) };
|
||||
});
|
||||
buildOptions.scripts = buildOptions.scripts.map((script) => {
|
||||
return typeof script === 'string'
|
||||
? joinPathFragments(offset, script)
|
||||
: { ...script, input: joinPathFragments(offset, script.input) };
|
||||
});
|
||||
if (buildOptions.stylePreprocessorOptions?.includePaths.length > 0) {
|
||||
buildOptions.stylePreprocessorOptions = {
|
||||
includePaths: buildOptions.stylePreprocessorOptions.includePaths.map(
|
||||
(path) => {
|
||||
return joinPathFragments(offset, path);
|
||||
}
|
||||
),
|
||||
};
|
||||
if (offset) {
|
||||
buildOptions.assets = buildOptions.assets.map((asset) => {
|
||||
return typeof asset === 'string'
|
||||
? joinPathFragments(offset, asset)
|
||||
: { ...asset, input: joinPathFragments(offset, asset.input) };
|
||||
});
|
||||
buildOptions.styles = buildOptions.styles.map((style) => {
|
||||
return typeof style === 'string'
|
||||
? joinPathFragments(offset, style)
|
||||
: { ...style, input: joinPathFragments(offset, style.input) };
|
||||
});
|
||||
buildOptions.scripts = buildOptions.scripts.map((script) => {
|
||||
return typeof script === 'string'
|
||||
? joinPathFragments(offset, script)
|
||||
: { ...script, input: joinPathFragments(offset, script.input) };
|
||||
});
|
||||
if (buildOptions.stylePreprocessorOptions?.includePaths.length > 0) {
|
||||
buildOptions.stylePreprocessorOptions = {
|
||||
includePaths: buildOptions.stylePreprocessorOptions.includePaths.map(
|
||||
(path) => {
|
||||
return joinPathFragments(offset, path);
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const stylePath = getTempStylesForTailwind(ctContext);
|
||||
@@ -256,9 +280,15 @@ Note: this may fail, setting the correct 'sourceRoot' for ${buildContext.project
|
||||
}
|
||||
|
||||
return {
|
||||
root: joinPathFragments(offset, config.root),
|
||||
sourceRoot: joinPathFragments(offset, config.sourceRoot),
|
||||
buildOptions,
|
||||
root: offset ? joinPathFragments(offset, config.root) : config.root,
|
||||
sourceRoot: offset
|
||||
? joinPathFragments(offset, config.sourceRoot)
|
||||
: config.sourceRoot,
|
||||
buildOptions: {
|
||||
...buildOptions,
|
||||
// this property is only valid for cy v12.9.0+
|
||||
workspaceRoot: offset ? undefined : ctContext.root,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -309,13 +339,14 @@ function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
|
||||
ctProjectConfig.root,
|
||||
'tailwind.config'
|
||||
);
|
||||
const isTailWindInCtProject =
|
||||
existsSync(ctProjectTailwindConfig + '.js') ||
|
||||
existsSync(ctProjectTailwindConfig + '.cjs');
|
||||
const exts = ['js', 'cjs'];
|
||||
const isTailWindInCtProject = exts.some((ext) =>
|
||||
existsSync(`${ctProjectTailwindConfig}.${ext}`)
|
||||
);
|
||||
const rootTailwindPath = join(ctExecutorContext.root, 'tailwind.config');
|
||||
const isTailWindInRoot =
|
||||
existsSync(rootTailwindPath + '.js') ||
|
||||
existsSync(rootTailwindPath + '.cjs');
|
||||
const isTailWindInRoot = exts.some((ext) =>
|
||||
existsSync(`${rootTailwindPath}.${ext}`)
|
||||
);
|
||||
|
||||
if (isTailWindInRoot || isTailWindInCtProject) {
|
||||
const pathToStyle = getTempTailwindPath(ctExecutorContext);
|
||||
@@ -339,3 +370,46 @@ function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isOffsetNeeded(
|
||||
ctExecutorContext: ExecutorContext,
|
||||
ctProjectConfig: ProjectConfiguration
|
||||
) {
|
||||
try {
|
||||
const { version = null } = require('cypress/package.json');
|
||||
|
||||
const supportsWorkspaceRoot = !!version && gte(version, '12.9.0');
|
||||
|
||||
// if using cypress <v12.9.0 then we require the offset
|
||||
if (!supportsWorkspaceRoot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
ctProjectConfig.projectType === 'library' &&
|
||||
// angular will only see this config if the library root is the build project config root
|
||||
// otherwise it will be set to the buildTarget root which is the app root where this config doesn't exist
|
||||
// causing tailwind styles from the libs project root to not work
|
||||
['js', 'cjs'].some((ext) =>
|
||||
existsSync(
|
||||
join(
|
||||
ctExecutorContext.root,
|
||||
ctProjectConfig.root,
|
||||
`tailwind.config.${ext}`
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
logger.error(e);
|
||||
}
|
||||
// unable to determine if we don't require an offset
|
||||
// safest to assume we do
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,6 +584,27 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('karma', () => {
|
||||
it('should generate a karma config', async () => {
|
||||
await generateApp(appTree, 'myApp', {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
});
|
||||
|
||||
expect(appTree.exists('apps/my-app/tsconfig.spec.json')).toBeTruthy();
|
||||
expect(appTree.exists('apps/my-app/karma.conf.js')).toBeTruthy();
|
||||
expect(
|
||||
readProjectConfiguration(appTree, 'my-app').targets.test.executor
|
||||
).toEqual('@angular-devkit/build-angular:karma');
|
||||
const tsconfigAppJson = readJson(
|
||||
appTree,
|
||||
'apps/my-app/tsconfig.app.json'
|
||||
);
|
||||
expect(tsconfigAppJson.compilerOptions.outDir).toEqual(
|
||||
'../../dist/out-tsc'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('none', () => {
|
||||
it('should not generate test configuration', async () => {
|
||||
await generateApp(appTree, 'myApp', {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { NormalizedSchema } from './normalized-schema';
|
||||
import { jestProjectGenerator } from '@nrwl/jest';
|
||||
|
||||
import { UnitTestRunner } from '../../../utils/test-runners';
|
||||
import karmaProjectGenerator from '../../karma-project/karma-project';
|
||||
|
||||
export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) {
|
||||
if (options.unitTestRunner === UnitTestRunner.Jest) {
|
||||
@@ -15,5 +16,10 @@ export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) {
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
skipFormat: true,
|
||||
});
|
||||
} else if (options.unitTestRunner === UnitTestRunner.Karma) {
|
||||
await karmaProjectGenerator(host, {
|
||||
project: options.name,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -542,6 +542,47 @@ describe('component Generator', () => {
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should throw an error when there are more than one candidate modules that the component can be added to', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'lib1', {
|
||||
projectType: 'library',
|
||||
sourceRoot: 'libs/lib1/src',
|
||||
root: 'libs/lib1',
|
||||
});
|
||||
tree.write(
|
||||
'libs/lib1/src/lib/lib.module.ts',
|
||||
`
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
exports: []
|
||||
})
|
||||
export class LibModule {}`
|
||||
);
|
||||
tree.write(
|
||||
'libs/lib1/src/lib/lib2.module.ts',
|
||||
`
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
exports: []
|
||||
})
|
||||
export class Lib2Module {}`
|
||||
);
|
||||
|
||||
// ACT & ASSERT
|
||||
await expect(
|
||||
componentGenerator(tree, {
|
||||
name: 'example',
|
||||
project: 'lib1',
|
||||
path: 'libs/lib1/src/lib',
|
||||
})
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('secondary entry points', () => {
|
||||
|
||||
@@ -74,7 +74,9 @@ function findModule(
|
||||
if (filteredMatches.length == 1) {
|
||||
return filteredMatches[0];
|
||||
} else if (filteredMatches.length > 1) {
|
||||
return null;
|
||||
throw new Error(
|
||||
"Found more than one candidate module to add the component to. Please specify which module the component should be added to by using the '--module' option."
|
||||
);
|
||||
}
|
||||
|
||||
dir = dirname(dir);
|
||||
|
||||
@@ -12,10 +12,11 @@ describe('directive generator', () => {
|
||||
addProjectConfiguration(tree, 'test', {
|
||||
root: 'test',
|
||||
sourceRoot: 'test/src',
|
||||
projectType: 'application',
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'test/src/test.module.ts',
|
||||
'test/src/app/test.module.ts',
|
||||
`import {NgModule} from "@angular/core";
|
||||
@NgModule({
|
||||
imports: [],
|
||||
@@ -33,11 +34,13 @@ describe('directive generator', () => {
|
||||
await generateDirectiveWithDefaultOptions(tree);
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('test/src/test.directive.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/test.directive.spec.ts', 'utf-8')
|
||||
tree.read('test/src/app/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/app/test.directive.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should import the directive correctly when flat=false', async () => {
|
||||
@@ -48,12 +51,12 @@ describe('directive generator', () => {
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
tree.read('test/src/test/test.directive.ts', 'utf-8')
|
||||
tree.read('test/src/app/test/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/test/test.directive.spec.ts', 'utf-8')
|
||||
tree.read('test/src/app/test/test.directive.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not import the directive when standalone=true', async () => {
|
||||
@@ -63,11 +66,13 @@ describe('directive generator', () => {
|
||||
await generateDirectiveWithDefaultOptions(tree, { standalone: true });
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('test/src/test.directive.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/test.directive.spec.ts', 'utf-8')
|
||||
tree.read('test/src/app/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/app/test.directive.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should import the directive correctly when flat=false and path is nested deeper', async () => {
|
||||
@@ -76,17 +81,20 @@ describe('directive generator', () => {
|
||||
// ACT
|
||||
await generateDirectiveWithDefaultOptions(tree, {
|
||||
flat: false,
|
||||
path: 'test/src/my-directives',
|
||||
path: 'test/src/app/my-directives',
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8')
|
||||
tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8')
|
||||
tree.read(
|
||||
'test/src/app/my-directives/test/test.directive.spec.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should export the directive correctly when flat=false and path is nested deeper', async () => {
|
||||
@@ -95,18 +103,21 @@ describe('directive generator', () => {
|
||||
// ACT
|
||||
await generateDirectiveWithDefaultOptions(tree, {
|
||||
flat: false,
|
||||
path: 'test/src/my-directives',
|
||||
path: 'test/src/app/my-directives',
|
||||
export: true,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8')
|
||||
tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8')
|
||||
tree.read(
|
||||
'test/src/app/my-directives/test/test.directive.spec.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not import the directive when skipImport=true', async () => {
|
||||
@@ -115,18 +126,21 @@ describe('directive generator', () => {
|
||||
// ACT
|
||||
await generateDirectiveWithDefaultOptions(tree, {
|
||||
flat: false,
|
||||
path: 'test/src/my-directives',
|
||||
path: 'test/src/app/my-directives',
|
||||
skipImport: true,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8')
|
||||
tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8')
|
||||
tree.read(
|
||||
'test/src/app/my-directives/test/test.directive.spec.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not generate test file when skipTests=true', async () => {
|
||||
@@ -135,18 +149,18 @@ describe('directive generator', () => {
|
||||
// ACT
|
||||
await generateDirectiveWithDefaultOptions(tree, {
|
||||
flat: false,
|
||||
path: 'test/src/my-directives',
|
||||
path: 'test/src/app/my-directives',
|
||||
skipTests: true,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8')
|
||||
tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.exists('test/src/my-directives/test/test.directive.spec.ts')
|
||||
tree.exists('test/src/app/my-directives/test/test.directive.spec.ts')
|
||||
).toBeFalsy();
|
||||
expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,58 +1,40 @@
|
||||
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
names,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import type { Schema } from './schema';
|
||||
import { checkPathUnderProjectRoot } from '../utils/path';
|
||||
import { addToNgModule, findModule } from '../utils';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
import { normalizeOptions, validateOptions } from './lib';
|
||||
import type { Schema } from './schema';
|
||||
|
||||
export async function directiveGenerator(tree: Tree, schema: Schema) {
|
||||
const projects = getProjects(tree);
|
||||
if (!projects.has(schema.project)) {
|
||||
throw new Error(`Project "${schema.project}" does not exist!`);
|
||||
}
|
||||
validateOptions(tree, schema);
|
||||
const options = normalizeOptions(tree, schema);
|
||||
|
||||
checkPathUnderProjectRoot(tree, schema.project, schema.path);
|
||||
const directiveNames = names(options.name);
|
||||
|
||||
const project = readProjectConfiguration(
|
||||
tree,
|
||||
schema.project
|
||||
) as ProjectConfiguration & { prefix?: string };
|
||||
|
||||
const path = schema.path ?? `${project.sourceRoot}`;
|
||||
const directiveNames = names(schema.name);
|
||||
const selector =
|
||||
schema.selector ??
|
||||
buildSelector(tree, schema.name, schema.prefix ?? project.prefix);
|
||||
|
||||
const pathToGenerateFiles = schema.flat
|
||||
const pathToGenerateFiles = options.flat
|
||||
? './files/__directiveFileName__'
|
||||
: './files';
|
||||
await generateFiles(
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, pathToGenerateFiles),
|
||||
path,
|
||||
options.path,
|
||||
{
|
||||
selector,
|
||||
selector: options.selector,
|
||||
directiveClassName: directiveNames.className,
|
||||
directiveFileName: directiveNames.fileName,
|
||||
standalone: schema.standalone,
|
||||
standalone: options.standalone,
|
||||
tpl: '',
|
||||
}
|
||||
);
|
||||
|
||||
if (schema.skipTests) {
|
||||
if (options.skipTests) {
|
||||
const pathToSpecFile = joinPathFragments(
|
||||
path,
|
||||
`${!schema.flat ? `${directiveNames.fileName}/` : ``}${
|
||||
options.path,
|
||||
`${!options.flat ? `${directiveNames.fileName}/` : ``}${
|
||||
directiveNames.fileName
|
||||
}.directive.spec.ts`
|
||||
);
|
||||
@@ -60,31 +42,24 @@ export async function directiveGenerator(tree: Tree, schema: Schema) {
|
||||
tree.delete(pathToSpecFile);
|
||||
}
|
||||
|
||||
if (!schema.skipImport && !schema.standalone) {
|
||||
const modulePath = findModule(tree, path, schema.module);
|
||||
if (!options.skipImport && !options.standalone) {
|
||||
const modulePath = findModule(tree, options.path, options.module);
|
||||
addToNgModule(
|
||||
tree,
|
||||
path,
|
||||
options.path,
|
||||
modulePath,
|
||||
directiveNames.fileName,
|
||||
`${directiveNames.className}Directive`,
|
||||
`${directiveNames.fileName}.directive`,
|
||||
'declarations',
|
||||
schema.flat,
|
||||
schema.export
|
||||
options.flat,
|
||||
options.export
|
||||
);
|
||||
}
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
|
||||
function buildSelector(tree: Tree, name: string, prefix: string) {
|
||||
let selector = names(name).fileName;
|
||||
const selectorPrefix = names(prefix ?? readNxJson(tree).npmScope).fileName;
|
||||
|
||||
return names(`${selectorPrefix}-${selector}`).propertyName;
|
||||
}
|
||||
|
||||
export default directiveGenerator;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './normalize-options';
|
||||
export * from './validate-options';
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
joinPathFragments,
|
||||
names,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import { parseName } from '../../utils/names';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function normalizeOptions(tree: Tree, options: Schema) {
|
||||
const { prefix, projectType, root, sourceRoot } = readProjectConfiguration(
|
||||
tree,
|
||||
options.project
|
||||
) as ProjectConfiguration & { prefix?: string };
|
||||
|
||||
const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src');
|
||||
const { name, path: namePath } = parseName(options.name);
|
||||
|
||||
const path =
|
||||
options.path ??
|
||||
joinPathFragments(
|
||||
projectSourceRoot,
|
||||
projectType === 'application' ? 'app' : 'lib',
|
||||
namePath
|
||||
);
|
||||
|
||||
const selector =
|
||||
options.selector ?? buildSelector(tree, name, options.prefix, prefix);
|
||||
|
||||
return {
|
||||
...options,
|
||||
name,
|
||||
path,
|
||||
projectRoot: root,
|
||||
projectSourceRoot,
|
||||
selector,
|
||||
};
|
||||
}
|
||||
|
||||
function buildSelector(
|
||||
tree: Tree,
|
||||
name: string,
|
||||
prefix: string,
|
||||
projectPrefix: string
|
||||
): string {
|
||||
let selector = name;
|
||||
prefix ??= projectPrefix ?? readNxJson(tree).npmScope;
|
||||
if (prefix) {
|
||||
selector = `${prefix}-${selector}`;
|
||||
}
|
||||
|
||||
return names(selector).propertyName;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import { getProjects, stripIndents } from '@nrwl/devkit';
|
||||
import { lt } from 'semver';
|
||||
import { checkPathUnderProjectRoot } from '../../utils/path';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function validateOptions(tree: Tree, options: Schema): void {
|
||||
const projects = getProjects(tree);
|
||||
if (!projects.has(options.project)) {
|
||||
throw new Error(`Project "${options.project}" does not exist!`);
|
||||
}
|
||||
|
||||
checkPathUnderProjectRoot(tree, options.project, options.path);
|
||||
|
||||
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
||||
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
|
||||
throw new Error(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using "${installedAngularVersionInfo.version}".
|
||||
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@
|
||||
"description": "The HTML selector to use for this directive."
|
||||
},
|
||||
"standalone": {
|
||||
"description": "Whether the generated directive is standalone.",
|
||||
"description": "Whether the generated directive is standalone. _Note: This is only supported in Angular versions >= 14.1.0_.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
extractLayoutDirectory,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
runTasksInSerial,
|
||||
@@ -39,7 +40,8 @@ export async function host(tree: Tree, options: Schema) {
|
||||
});
|
||||
}
|
||||
|
||||
const appName = normalizeProjectName(options.name, options.directory);
|
||||
const { projectDirectory } = extractLayoutDirectory(options.directory);
|
||||
const appName = normalizeProjectName(options.name, projectDirectory);
|
||||
|
||||
const appInstallTask = await applicationGenerator(tree, {
|
||||
...options,
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -118,6 +118,62 @@ describe('init', () => {
|
||||
});
|
||||
|
||||
describe('--unit-test-runner', () => {
|
||||
describe('karma', () => {
|
||||
it('should add karma dependencies', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const { devDependencies } = readJson(tree, 'package.json');
|
||||
|
||||
// ASSERT
|
||||
expect(devDependencies['karma']).toBeDefined();
|
||||
expect(devDependencies['karma-chrome-launcher']).toBeDefined();
|
||||
expect(devDependencies['karma-coverage']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine-html-reporter']).toBeDefined();
|
||||
expect(devDependencies['jasmine-core']).toBeDefined();
|
||||
expect(devDependencies['jasmine-spec-reporter']).toBeDefined();
|
||||
expect(devDependencies['@types/jasmine']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should add karma configuration', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const hasKarmaConfigFile = tree.exists('karma.conf.js');
|
||||
|
||||
// ASSERT
|
||||
expect(hasKarmaConfigFile).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set defaults', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const { generators } = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||
|
||||
// ASSERT
|
||||
expect(generators['@nrwl/angular:application'].unitTestRunner).toEqual(
|
||||
'karma'
|
||||
);
|
||||
expect(generators['@nrwl/angular:library'].unitTestRunner).toEqual(
|
||||
'karma'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('jest', () => {
|
||||
it('should add jest dependencies', async () => {
|
||||
// ACT
|
||||
@@ -470,6 +526,79 @@ bar
|
||||
});
|
||||
|
||||
describe('--unit-test-runner', () => {
|
||||
describe('karma', () => {
|
||||
it('should add karma dependencies', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const { devDependencies } = readJson(tree, 'package.json');
|
||||
|
||||
// ASSERT
|
||||
expect(devDependencies['karma']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.karmaVersion
|
||||
);
|
||||
expect(devDependencies['karma-chrome-launcher']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.karmaChromeLauncherVersion
|
||||
);
|
||||
expect(devDependencies['karma-coverage']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.karmaCoverageVersion
|
||||
);
|
||||
expect(devDependencies['karma-jasmine']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.karmaJasmineVersion
|
||||
);
|
||||
expect(devDependencies['karma-jasmine-html-reporter']).toEqual(
|
||||
backwardCompatibleVersions.angularV14
|
||||
.karmaJasmineHtmlReporterVersion
|
||||
);
|
||||
expect(devDependencies['jasmine-core']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.jasmineCoreVersion
|
||||
);
|
||||
expect(devDependencies['jasmine-spec-reporter']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.jasmineSpecReporterVersion
|
||||
);
|
||||
expect(devDependencies['@types/jasmine']).toEqual(
|
||||
backwardCompatibleVersions.angularV14.typesJasmineVersion
|
||||
);
|
||||
});
|
||||
|
||||
it('should add karma configuration', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const hasKarmaConfigFile = tree.exists('karma.conf.js');
|
||||
|
||||
// ASSERT
|
||||
expect(hasKarmaConfigFile).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set defaults', async () => {
|
||||
// ACT
|
||||
await init(tree, {
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
const { generators } = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
generators['@nrwl/angular:application'].unitTestRunner
|
||||
).toEqual('karma');
|
||||
expect(generators['@nrwl/angular:library'].unitTestRunner).toEqual(
|
||||
'karma'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('jest', () => {
|
||||
it('should add jest dependencies', async () => {
|
||||
// ACT
|
||||
|
||||
@@ -14,6 +14,7 @@ import { jestInitGenerator } from '@nrwl/jest';
|
||||
import { Linter } from '@nrwl/linter';
|
||||
import { initGenerator as jsInitGenerator } from '@nrwl/js';
|
||||
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||
import { karmaGenerator } from '../karma/karma';
|
||||
import {
|
||||
addDependenciesToPackageJsonIfDontExist,
|
||||
getInstalledPackageVersion,
|
||||
@@ -171,6 +172,10 @@ async function addUnitTestRunner(
|
||||
jestPresetAngularVersion: string
|
||||
): Promise<GeneratorCallback> {
|
||||
switch (options.unitTestRunner) {
|
||||
case UnitTestRunner.Karma:
|
||||
return await karmaGenerator(tree, {
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
});
|
||||
case UnitTestRunner.Jest:
|
||||
if (!options.skipPackageJson) {
|
||||
addDependenciesToPackageJsonIfDontExist(
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"properties": {
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest",
|
||||
"x-priority": "important"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { convertNxGenerator } from '@nrwl/devkit';
|
||||
import { karmaProjectGenerator } from './karma-project';
|
||||
|
||||
export default convertNxGenerator(karmaProjectGenerator);
|
||||
@@ -0,0 +1,16 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/6.4/config/configuration-file.html
|
||||
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('<%= offsetFromRoot %>karma.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
config.set({
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '<%= offsetFromRoot %>coverage/<%= projectRoot %>')
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
"types": ["jasmine", "node"]
|
||||
},
|
||||
"include": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/6.4/config/configuration-file.html
|
||||
|
||||
const { constants } = require('karma');
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: join(__dirname, 'coverage/<%= projectName %>'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: constants.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/6.4/config/configuration-file.html
|
||||
|
||||
const { join } = require('path');<% if (rootProjectWithConfigSet) { %>
|
||||
const setBaseKarmaConfig = require('<%= offsetFromRoot %>karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
setBaseKarmaConfig(config);
|
||||
|
||||
config.set({
|
||||
coverageReporter: {
|
||||
dir: join(__dirname, '<%= offsetFromRoot %>coverage/<%= projectRoot %>')
|
||||
}
|
||||
});
|
||||
};<% } else { %>
|
||||
const getBaseKarmaConfig = require('<%= offsetFromRoot %>karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig(config);
|
||||
|
||||
config.set({
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '<%= offsetFromRoot %>coverage/<%= projectRoot %>')
|
||||
}
|
||||
});
|
||||
};<% } %>
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { GeneratorCallback, Tree } from '@nrwl/devkit';
|
||||
import { formatFiles } from '@nrwl/devkit';
|
||||
import { checkProjectTestTarget } from './lib/check-test-target';
|
||||
import { generateKarmaProjectFiles } from './lib/generate-karma-project-files';
|
||||
import { updateTsConfigs } from './lib/update-tsconfig';
|
||||
import { updateWorkspaceConfig } from './lib/update-workspace-config';
|
||||
import type { KarmaProjectOptions } from './schema';
|
||||
import { karmaGenerator } from '../karma/karma';
|
||||
|
||||
export async function karmaProjectGenerator(
|
||||
tree: Tree,
|
||||
options: KarmaProjectOptions
|
||||
): Promise<GeneratorCallback> {
|
||||
const installTask = await karmaGenerator(tree, options);
|
||||
checkProjectTestTarget(tree, options.project);
|
||||
generateKarmaProjectFiles(tree, options.project);
|
||||
updateTsConfigs(tree, options.project);
|
||||
updateWorkspaceConfig(tree, options.project);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
return installTask;
|
||||
}
|
||||
|
||||
export default karmaProjectGenerator;
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import { readProjectConfiguration } from '@nrwl/devkit';
|
||||
|
||||
export function checkProjectTestTarget(tree: Tree, project: string): void {
|
||||
const projectConfig = readProjectConfiguration(tree, project);
|
||||
if (projectConfig.targets.test) {
|
||||
throw new Error(`"${project}" already has a test target.`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
generateFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
offsetFromRoot,
|
||||
readProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import { v14TestFile } from './v14-test-file';
|
||||
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
|
||||
|
||||
export function generateKarmaProjectFiles(tree: Tree, project: string): void {
|
||||
const projectConfig = readProjectConfiguration(tree, project);
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files', 'common'),
|
||||
projectConfig.root,
|
||||
{
|
||||
tmpl: '',
|
||||
projectRoot: projectConfig.root,
|
||||
offsetFromRoot: offsetFromRoot(projectConfig.root),
|
||||
}
|
||||
);
|
||||
|
||||
if (projectConfig.root === '' || projectConfig.root === '.') {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files', 'root-project'),
|
||||
projectConfig.root,
|
||||
{
|
||||
tmpl: '',
|
||||
projectName: project,
|
||||
}
|
||||
);
|
||||
} else if (isWorkspaceWithProjectAtRoot(tree)) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(
|
||||
__dirname,
|
||||
'..',
|
||||
'files',
|
||||
'workspace-with-root-project'
|
||||
),
|
||||
projectConfig.root,
|
||||
{
|
||||
tmpl: '',
|
||||
projectRoot: projectConfig.root,
|
||||
offsetFromRoot: offsetFromRoot(projectConfig.root),
|
||||
rootProjectWithConfigSet: isUsingConfigSetInBaseKarmaConfig(tree),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const installedAngularVersion = getInstalledAngularVersionInfo(tree);
|
||||
if (installedAngularVersion.major === 14) {
|
||||
tree.write(
|
||||
joinPathFragments(projectConfig.sourceRoot, 'test.ts'),
|
||||
v14TestFile({ isLibrary: projectConfig.projectType === 'library' })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function isWorkspaceWithProjectAtRoot(tree: Tree): boolean {
|
||||
const projects = getProjects(tree);
|
||||
for (const [, project] of projects) {
|
||||
if (project.root === '.' || project.root === '') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isUsingConfigSetInBaseKarmaConfig(tree: Tree) {
|
||||
if (!tree.exists('karma.conf.js')) {
|
||||
return false;
|
||||
}
|
||||
ensureTypescript();
|
||||
const { tsquery } = require('@phenomnomnominal/tsquery');
|
||||
|
||||
const CONFIG_SET_SELECTOR =
|
||||
'PropertyAccessExpression:has(Identifier[name=config], Identifier[name=set])';
|
||||
|
||||
const ast = tsquery.ast(tree.read('karma.conf.js', 'utf-8'));
|
||||
const nodes = tsquery(ast, CONFIG_SET_SELECTOR, { visitAllChildren: true });
|
||||
|
||||
return nodes.length > 0;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
updateJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
|
||||
export function updateTsConfigs(tree: Tree, project: string): void {
|
||||
const projectConfig = readProjectConfiguration(tree, project);
|
||||
|
||||
updateJson(
|
||||
tree,
|
||||
joinPathFragments(projectConfig.root, 'tsconfig.json'),
|
||||
(json) => {
|
||||
return {
|
||||
...json,
|
||||
references: [
|
||||
...(json.references || []),
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const installedAngularVersion = getInstalledAngularVersionInfo(tree);
|
||||
let extraFiles: string[] =
|
||||
installedAngularVersion.major === 14 ? ['src/test.ts'] : [];
|
||||
if (
|
||||
projectConfig.projectType == 'application' &&
|
||||
projectConfig.targets.build?.options?.polyfills &&
|
||||
typeof projectConfig.targets.build.options.polyfills === 'string'
|
||||
) {
|
||||
let polyfillsPath = projectConfig.targets.build.options.polyfills;
|
||||
polyfillsPath = polyfillsPath.startsWith(projectConfig.root)
|
||||
? polyfillsPath.replace(`${projectConfig.root}/`, '')
|
||||
: polyfillsPath;
|
||||
extraFiles = [...extraFiles, polyfillsPath];
|
||||
}
|
||||
|
||||
if (!extraFiles.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateJson(
|
||||
tree,
|
||||
joinPathFragments(projectConfig.root, 'tsconfig.spec.json'),
|
||||
(json) => {
|
||||
return {
|
||||
...json,
|
||||
files: [...(json.files ?? []), ...extraFiles],
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
updateProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
|
||||
export function updateWorkspaceConfig(tree: Tree, project: string): void {
|
||||
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
||||
const projectConfig = readProjectConfiguration(tree, project);
|
||||
projectConfig.targets.test = {
|
||||
executor: '@angular-devkit/build-angular:karma',
|
||||
options: {
|
||||
...(installedAngularVersionInfo.major === 14
|
||||
? { main: joinPathFragments(projectConfig.sourceRoot, 'test.ts') }
|
||||
: {}),
|
||||
tsConfig: joinPathFragments(projectConfig.root, 'tsconfig.spec.json'),
|
||||
karmaConfig: joinPathFragments(projectConfig.root, 'karma.conf.js'),
|
||||
polyfills: ['zone.js', 'zone.js/testing'],
|
||||
},
|
||||
};
|
||||
|
||||
if (projectConfig.projectType === 'application') {
|
||||
const polyfills = projectConfig.targets.build?.options?.polyfills;
|
||||
let polyfillsPath =
|
||||
polyfills && typeof polyfills === 'string' ? polyfills : undefined;
|
||||
|
||||
projectConfig.targets.test.options = {
|
||||
...projectConfig.targets.test.options,
|
||||
polyfills: polyfillsPath ?? projectConfig.targets.test.options.polyfills,
|
||||
styles: [],
|
||||
scripts: [],
|
||||
assets: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
projectConfig.targets.lint &&
|
||||
projectConfig.targets.lint.executor ===
|
||||
'@angular-devkit/build-angular:tslint'
|
||||
) {
|
||||
projectConfig.targets.lint.options.tsConfig = [
|
||||
...projectConfig.targets.lint.options.tsConfig,
|
||||
joinPathFragments(projectConfig.root, 'tsconfig.spec.json'),
|
||||
];
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, project, projectConfig);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
export const v14TestFile = (opts: {
|
||||
isLibrary: boolean;
|
||||
}) => `// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
${
|
||||
opts.isLibrary
|
||||
? `
|
||||
import 'zone.js/dist/zone';
|
||||
`
|
||||
: ``
|
||||
}
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);`;
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface KarmaProjectOptions {
|
||||
project: string;
|
||||
skipFormat?: boolean;
|
||||
skipPackageJson?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxKarmaProjectGenerator",
|
||||
"title": "Create Karma Configuration for a project",
|
||||
"description": "Add Karma configuration to an angular project.",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"x-dropdown": "projects",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"description": "Skip updating package.json.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["project"],
|
||||
"examplesFile": "../../../docs/karma-project-examples.md"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
const { join } = require('path');
|
||||
const { constants } = require('karma');
|
||||
|
||||
module.exports = () => {
|
||||
return {
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: join(__dirname, './coverage'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: constants.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
generateFiles,
|
||||
joinPathFragments,
|
||||
readJson,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { backwardCompatibleVersions } from '../../../utils/backward-compatible-versions';
|
||||
import { addDependenciesToPackageJsonIfDontExist } from '../../utils/version-utils';
|
||||
import { GeneratorOptions } from './schema';
|
||||
|
||||
function addTestInputs(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
const productionFileSet = nxJson.namedInputs?.production;
|
||||
if (productionFileSet) {
|
||||
productionFileSet.push(
|
||||
// Exclude spec files from production fileset
|
||||
'!{projectRoot}/**/*.spec.[jt]s',
|
||||
// Remove tsconfig.spec.json
|
||||
'!{projectRoot}/tsconfig.spec.json',
|
||||
// Remove karma.conf.js
|
||||
'!{projectRoot}/karma.conf.js'
|
||||
);
|
||||
// Dedupe and set
|
||||
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
||||
}
|
||||
|
||||
// Test targets depend on all their project's sources + production sources of dependencies
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults.test ??= {};
|
||||
nxJson.targetDefaults.test.inputs ??= [
|
||||
'default',
|
||||
productionFileSet ? '^production' : '^default',
|
||||
];
|
||||
nxJson.targetDefaults.test.inputs.push('{workspaceRoot}/karma.conf.js');
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
|
||||
export function karmaGenerator(tree: Tree, options: GeneratorOptions) {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
|
||||
if (!tree.exists('karma.conf.js')) {
|
||||
generateFiles(tree, joinPathFragments(__dirname, 'files'), '.', {
|
||||
tmpl: '',
|
||||
});
|
||||
|
||||
addTestInputs(tree);
|
||||
}
|
||||
|
||||
if (options.skipPackageJson || packageJson.devDependencies['karma']) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
return addDependenciesToPackageJsonIfDontExist(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
karma: backwardCompatibleVersions.angularV14.karmaVersion,
|
||||
'karma-chrome-launcher':
|
||||
backwardCompatibleVersions.angularV14.karmaChromeLauncherVersion,
|
||||
'karma-coverage':
|
||||
backwardCompatibleVersions.angularV14.karmaCoverageVersion,
|
||||
'karma-jasmine':
|
||||
backwardCompatibleVersions.angularV14.karmaJasmineVersion,
|
||||
'karma-jasmine-html-reporter':
|
||||
backwardCompatibleVersions.angularV14.karmaJasmineHtmlReporterVersion,
|
||||
'jasmine-core': backwardCompatibleVersions.angularV14.jasmineCoreVersion,
|
||||
'jasmine-spec-reporter':
|
||||
backwardCompatibleVersions.angularV14.jasmineSpecReporterVersion,
|
||||
'@types/jasmine':
|
||||
backwardCompatibleVersions.angularV14.typesJasmineVersion,
|
||||
'@types/node': backwardCompatibleVersions.angularV14.typesNodeVersion,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default karmaGenerator;
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GeneratorOptions {
|
||||
skipPackageJson?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { convertNxGenerator } from '@nrwl/devkit';
|
||||
import { karmaGenerator } from './karma';
|
||||
|
||||
export default convertNxGenerator(karmaGenerator);
|
||||
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
const { join } = require('path');
|
||||
const { constants } = require('karma');
|
||||
|
||||
module.exports = () => {
|
||||
return {
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: join(__dirname, './coverage'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: constants.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import * as devkit from '@nrwl/devkit';
|
||||
import { NxJsonConfiguration, readJson, updateJson } from '@nrwl/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
import { karmaGenerator } from './karma';
|
||||
|
||||
describe('karma', () => {
|
||||
let tree: devkit.Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should do nothing when karma is already installed and karma.conf.js exists', async () => {
|
||||
jest.spyOn(devkit, 'generateFiles');
|
||||
jest.spyOn(devkit, 'addDependenciesToPackageJson');
|
||||
devkit.updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { karma: '~5.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('karma.conf.js', '');
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(devkit.generateFiles).not.toHaveBeenCalled();
|
||||
expect(devkit.addDependenciesToPackageJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create karma.conf.js when karma is installed', async () => {
|
||||
jest.spyOn(devkit, 'generateFiles');
|
||||
jest.spyOn(devkit, 'addDependenciesToPackageJson');
|
||||
devkit.updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { karma: '~5.0.0' };
|
||||
return json;
|
||||
});
|
||||
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(devkit.generateFiles).toHaveBeenCalled();
|
||||
expect(devkit.addDependenciesToPackageJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add karma dependencies', async () => {
|
||||
karmaGenerator(tree, {});
|
||||
|
||||
const { devDependencies } = devkit.readJson(tree, 'package.json');
|
||||
expect(devDependencies['karma']).toBeDefined();
|
||||
expect(devDependencies['karma-chrome-launcher']).toBeDefined();
|
||||
expect(devDependencies['karma-coverage']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine-html-reporter']).toBeDefined();
|
||||
expect(devDependencies['jasmine-core']).toBeDefined();
|
||||
expect(devDependencies['jasmine-spec-reporter']).toBeDefined();
|
||||
expect(devDependencies['@types/jasmine']).toBeDefined();
|
||||
expect(devDependencies['@types/node']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should add karma configuration', async () => {
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(tree.exists('karma.conf.js')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add inputs for test targets', async () => {
|
||||
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
||||
json.namedInputs ??= {};
|
||||
json.namedInputs.production = ['default', '^production'];
|
||||
return json;
|
||||
});
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/karma.conf.js'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/tsconfig.spec.json'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/**/*.spec.[jt]s'
|
||||
);
|
||||
expect(nxJson.targetDefaults.test).toEqual({
|
||||
inputs: ['default', '^production', '{workspaceRoot}/karma.conf.js'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
generateFiles,
|
||||
joinPathFragments,
|
||||
readJson,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
jasmineCoreVersion,
|
||||
jasmineSpecReporterVersion,
|
||||
karmaChromeLauncherVersion,
|
||||
karmaCoverageVersion,
|
||||
karmaJasmineHtmlReporterVersion,
|
||||
karmaJasmineVersion,
|
||||
karmaVersion,
|
||||
typesJasmineVersion,
|
||||
typesNodeVersion,
|
||||
} from '../../utils/versions';
|
||||
import {
|
||||
addDependenciesToPackageJsonIfDontExist,
|
||||
getGeneratorDirectoryForInstalledAngularVersion,
|
||||
} from '../utils/version-utils';
|
||||
import { GeneratorOptions } from './schema';
|
||||
|
||||
function addTestInputs(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
const productionFileSet = nxJson.namedInputs?.production;
|
||||
if (productionFileSet) {
|
||||
productionFileSet.push(
|
||||
// Exclude spec files from production fileset
|
||||
'!{projectRoot}/**/*.spec.[jt]s',
|
||||
// Remove tsconfig.spec.json
|
||||
'!{projectRoot}/tsconfig.spec.json',
|
||||
// Remove karma.conf.js
|
||||
'!{projectRoot}/karma.conf.js'
|
||||
);
|
||||
// Dedupe and set
|
||||
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
||||
}
|
||||
|
||||
// Test targets depend on all their project's sources + production sources of dependencies
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults.test ??= {};
|
||||
nxJson.targetDefaults.test.inputs ??= [
|
||||
'default',
|
||||
productionFileSet ? '^production' : '^default',
|
||||
];
|
||||
nxJson.targetDefaults.test.inputs.push('{workspaceRoot}/karma.conf.js');
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
|
||||
export async function karmaGenerator(tree: Tree, options: GeneratorOptions) {
|
||||
const generatorDirectory =
|
||||
getGeneratorDirectoryForInstalledAngularVersion(tree);
|
||||
if (generatorDirectory) {
|
||||
let previousGenerator = await import(
|
||||
join(__dirname, generatorDirectory, 'karma')
|
||||
);
|
||||
await previousGenerator.default(tree, options);
|
||||
return;
|
||||
}
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
|
||||
if (!tree.exists('karma.conf.js')) {
|
||||
generateFiles(tree, joinPathFragments(__dirname, 'files'), '.', {
|
||||
tmpl: '',
|
||||
});
|
||||
|
||||
addTestInputs(tree);
|
||||
}
|
||||
|
||||
if (options.skipPackageJson || packageJson.devDependencies['karma']) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
return addDependenciesToPackageJsonIfDontExist(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
karma: karmaVersion,
|
||||
'karma-chrome-launcher': karmaChromeLauncherVersion,
|
||||
'karma-coverage': karmaCoverageVersion,
|
||||
'karma-jasmine': karmaJasmineVersion,
|
||||
'karma-jasmine-html-reporter': karmaJasmineHtmlReporterVersion,
|
||||
'jasmine-core': jasmineCoreVersion,
|
||||
'jasmine-spec-reporter': jasmineSpecReporterVersion,
|
||||
'@types/jasmine': typesJasmineVersion,
|
||||
'@types/node': typesNodeVersion,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default karmaGenerator;
|
||||
@@ -0,0 +1,97 @@
|
||||
import * as devkit from '@nrwl/devkit';
|
||||
import { NxJsonConfiguration, readJson, updateJson } from '@nrwl/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
import { karmaGenerator } from './karma';
|
||||
|
||||
describe('karma', () => {
|
||||
let tree: devkit.Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
...json.dependencies,
|
||||
'@angular/core': '14.1.0',
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
describe('support angular v14', () => {
|
||||
it('should do nothing when karma is already installed and karma.conf.js exists', async () => {
|
||||
jest.spyOn(devkit, 'generateFiles');
|
||||
jest.spyOn(devkit, 'addDependenciesToPackageJson');
|
||||
devkit.updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { karma: '~5.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('karma.conf.js', '');
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(devkit.generateFiles).not.toHaveBeenCalled();
|
||||
expect(devkit.addDependenciesToPackageJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create karma.conf.js when karma is installed', async () => {
|
||||
jest.spyOn(devkit, 'generateFiles');
|
||||
jest.spyOn(devkit, 'addDependenciesToPackageJson');
|
||||
devkit.updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { karma: '~5.0.0' };
|
||||
return json;
|
||||
});
|
||||
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(devkit.generateFiles).toHaveBeenCalled();
|
||||
expect(devkit.addDependenciesToPackageJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add karma dependencies', async () => {
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
const { dependencies, devDependencies } = devkit.readJson(
|
||||
tree,
|
||||
'package.json'
|
||||
);
|
||||
expect(devDependencies['karma']).toBeDefined();
|
||||
expect(devDependencies['karma-chrome-launcher']).toBeDefined();
|
||||
expect(devDependencies['karma-coverage']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine']).toBeDefined();
|
||||
expect(devDependencies['karma-jasmine-html-reporter']).toBeDefined();
|
||||
expect(devDependencies['jasmine-core']).toBeDefined();
|
||||
expect(devDependencies['jasmine-spec-reporter']).toBeDefined();
|
||||
expect(devDependencies['@types/jasmine']).toBeDefined();
|
||||
expect(devDependencies['@types/node']).toBeDefined();
|
||||
expect(dependencies['@angular/core']).toEqual('14.1.0');
|
||||
});
|
||||
|
||||
it('should add karma configuration', async () => {
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
expect(tree.exists('karma.conf.js')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add inputs for test targets', async () => {
|
||||
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
||||
json.namedInputs ??= {};
|
||||
json.namedInputs.production = ['default', '^production'];
|
||||
return json;
|
||||
});
|
||||
await karmaGenerator(tree, {});
|
||||
|
||||
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/karma.conf.js'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/tsconfig.spec.json'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/**/*.spec.[jt]s'
|
||||
);
|
||||
expect(nxJson.targetDefaults.test).toEqual({
|
||||
inputs: ['default', '^production', '{workspaceRoot}/karma.conf.js'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GeneratorOptions {
|
||||
skipPackageJson?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"$id": "NxAngularKarmaGenerator",
|
||||
"title": "Add Karma Configuration to the workspace.",
|
||||
"description": "Add Karma configuration to an Nx workspace.",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"examples": [
|
||||
{
|
||||
"command": "nx g @nrwl/angular:karma",
|
||||
"description": "Add a base karma configuration to the workspace."
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add dependencies to `package.json`.",
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": []
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",<% if(unitTesting) { %>
|
||||
"src/**/*.spec.ts",<% if(unitTesting === 'jest') { %>
|
||||
"src/test-setup.ts",<% } %>
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
offsetFromRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { NormalizedSchema } from './normalized-schema';
|
||||
import { UnitTestRunner } from '../../../utils/test-runners';
|
||||
import { getRootTsConfigFileName } from '@nrwl/js';
|
||||
|
||||
export function createFiles(
|
||||
@@ -30,7 +29,7 @@ export function createFiles(
|
||||
libFileName: options.libraryOptions.fileName,
|
||||
libClassName: libNames.className,
|
||||
libPropertyName: libNames.propertyName,
|
||||
unitTesting: options.libraryOptions.unitTestRunner !== UnitTestRunner.None,
|
||||
unitTesting: options.libraryOptions.unitTestRunner,
|
||||
rootTsConfig: joinPathFragments(rootOffset, getRootTsConfigFileName(tree)),
|
||||
skipModule: options.libraryOptions.skipModule,
|
||||
projectRoot: options.libraryOptions.projectRoot,
|
||||
|
||||
@@ -355,6 +355,21 @@ describe('lib', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should leave the excludes alone when unitTestRunner is karma', async () => {
|
||||
// ACT
|
||||
await runLibraryGeneratorWithOpts({
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.lib.json');
|
||||
expect(tsconfigJson.exclude).toEqual([
|
||||
'src/**/*.spec.ts',
|
||||
'jest.config.ts',
|
||||
'src/**/*.test.ts',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove the excludes when unitTestRunner is none', async () => {
|
||||
// ACT
|
||||
await runLibraryGeneratorWithOpts({
|
||||
|
||||
@@ -13,6 +13,7 @@ import { lt } from 'semver';
|
||||
import init from '../../generators/init/init';
|
||||
import { E2eTestRunner } from '../../utils/test-runners';
|
||||
import addLintingGenerator from '../add-linting/add-linting';
|
||||
import karmaProjectGenerator from '../karma-project/karma-project';
|
||||
import setupTailwindGenerator from '../setup-tailwind/setup-tailwind';
|
||||
import {
|
||||
getInstalledAngularVersionInfo,
|
||||
@@ -131,6 +132,11 @@ async function addUnitTestRunner(
|
||||
skipFormat: true,
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
});
|
||||
} else if (options.unitTestRunner === 'karma') {
|
||||
await karmaProjectGenerator(host, {
|
||||
project: options.name,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"title": "Create a library",
|
||||
"description": "Creates an Angular library.",
|
||||
"type": "object",
|
||||
|
||||
"cli": "nx",
|
||||
"properties": {
|
||||
"name": {
|
||||
@@ -95,7 +96,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
extractLayoutDirectory,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
runTasksInSerial,
|
||||
@@ -30,7 +31,8 @@ export async function remote(tree: Tree, options: Schema) {
|
||||
);
|
||||
}
|
||||
|
||||
const appName = normalizeProjectName(options.name, options.directory);
|
||||
const { projectDirectory } = extractLayoutDirectory(options.directory);
|
||||
const appName = normalizeProjectName(options.name, projectDirectory);
|
||||
const port = options.port ?? findNextAvailablePort(tree);
|
||||
|
||||
const appInstallTask = await applicationGenerator(tree, {
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"enum": ["karma", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
|
||||
@@ -34,12 +34,20 @@ export function updateHostAppRoutes(tree: Tree, options: Schema) {
|
||||
`
|
||||
);
|
||||
|
||||
const pathToHostRootRoutingFile = joinPathFragments(
|
||||
let pathToHostRootRoutingFile = joinPathFragments(
|
||||
sourceRoot,
|
||||
'app/app.routes.ts'
|
||||
);
|
||||
|
||||
const hostRootRoutingFile = tree.read(pathToHostRootRoutingFile, 'utf-8');
|
||||
let hostRootRoutingFile = tree.read(pathToHostRootRoutingFile, 'utf-8');
|
||||
|
||||
if (!hostRootRoutingFile) {
|
||||
pathToHostRootRoutingFile = joinPathFragments(
|
||||
sourceRoot,
|
||||
'app/app-routing.module.ts'
|
||||
);
|
||||
hostRootRoutingFile = tree.read(pathToHostRootRoutingFile, 'utf-8');
|
||||
}
|
||||
|
||||
let sourceFile = tsModule.createSourceFile(
|
||||
pathToHostRootRoutingFile,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { normalizePath } from '@nrwl/devkit';
|
||||
|
||||
export type NameInfo = { name: string; path: string };
|
||||
|
||||
export function parseName(rawName: string): NameInfo {
|
||||
const parsedName = normalizePath(rawName).split('/');
|
||||
const name = parsedName.pop();
|
||||
const path = parsedName.join('/');
|
||||
|
||||
return { name, path };
|
||||
}
|
||||
@@ -19,18 +19,9 @@ describe('Migration to update target and add useDefineForClassFields', () => {
|
||||
});
|
||||
await generateTestApplication(tree, {
|
||||
name: 'karma',
|
||||
unitTestRunner: UnitTestRunner.None,
|
||||
unitTestRunner: UnitTestRunner.Karma,
|
||||
});
|
||||
|
||||
const karmaProject = readProjectConfiguration(tree, 'karma');
|
||||
karmaProject.targets.test = {
|
||||
executor: '@angular-devkit/build-angular:karma',
|
||||
options: {
|
||||
tsConfig: 'apps/karma/tsconfig.spec.json',
|
||||
},
|
||||
};
|
||||
updateProjectConfiguration(tree, 'karma', karmaProject);
|
||||
|
||||
// Create tsconfigs
|
||||
const compilerOptions = { target: 'es2015', module: 'es2020' };
|
||||
const configWithExtends = {
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~15.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~15.0.0',
|
||||
'@angular-devkit/build-angular': '~15.0.0',
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -36,9 +36,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~15.0.0",
|
||||
"@angular-devkit/core": "~15.0.0",
|
||||
"@angular-devkit/schematics": "~15.0.0",
|
||||
"@angular/cli": "~15.0.0",
|
||||
"@schematics/angular": "~15.0.0",
|
||||
}
|
||||
`);
|
||||
@@ -53,7 +53,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~14.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~14.0.0',
|
||||
'@angular-devkit/build-angular': '~14.0.0',
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -69,9 +69,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~14.0.0",
|
||||
"@angular-devkit/core": "~14.0.0",
|
||||
"@angular-devkit/schematics": "~14.0.0",
|
||||
"@angular/cli": "~14.0.0",
|
||||
"@schematics/angular": "~14.0.0",
|
||||
}
|
||||
`);
|
||||
@@ -86,7 +86,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~15.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~15.0.0',
|
||||
'@angular-devkit/build-angular': '~15.0.0',
|
||||
'@angular-devkit/core': '~15.0.0',
|
||||
'@angular-devkit/schematics': '~15.0.0',
|
||||
'@schematics/angular': '~15.0.0',
|
||||
@@ -105,9 +105,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~15.0.0",
|
||||
"@angular-devkit/core": "~15.0.0",
|
||||
"@angular-devkit/schematics": "~15.0.0",
|
||||
"@angular/cli": "~15.0.0",
|
||||
"@schematics/angular": "~15.0.0",
|
||||
}
|
||||
`);
|
||||
@@ -122,7 +122,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~14.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~14.0.0',
|
||||
'@angular-devkit/build-angular': '~14.0.0',
|
||||
'@angular-devkit/core': '~14.0.0',
|
||||
'@angular-devkit/schematics': '~14.0.0',
|
||||
'@schematics/angular': '~14.0.0',
|
||||
@@ -141,9 +141,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~14.0.0",
|
||||
"@angular-devkit/core": "~14.0.0",
|
||||
"@angular-devkit/schematics": "~14.0.0",
|
||||
"@angular/cli": "~14.0.0",
|
||||
"@schematics/angular": "~14.0.0",
|
||||
}
|
||||
`);
|
||||
@@ -158,7 +158,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~15.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~15.0.0',
|
||||
'@angular-devkit/build-angular': '~15.0.0',
|
||||
'@angular-devkit/core': '~15.0.0',
|
||||
'@schematics/angular': '~15.0.0',
|
||||
},
|
||||
@@ -176,9 +176,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~15.0.0",
|
||||
"@angular-devkit/core": "~15.0.0",
|
||||
"@angular-devkit/schematics": "~15.0.0",
|
||||
"@angular/cli": "~15.0.0",
|
||||
"@schematics/angular": "~15.0.0",
|
||||
}
|
||||
`);
|
||||
@@ -193,7 +193,7 @@ describe('installed-required-packages', () => {
|
||||
'@angular/core': '~14.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/cli': '~14.0.0',
|
||||
'@angular-devkit/build-angular': '~14.0.0',
|
||||
'@angular-devkit/core': '~14.0.0',
|
||||
'@schematics/angular': '~14.0.0',
|
||||
},
|
||||
@@ -211,9 +211,9 @@ describe('installed-required-packages', () => {
|
||||
`);
|
||||
expect(pkgJson.devDependencies).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"@angular-devkit/build-angular": "~14.0.0",
|
||||
"@angular-devkit/core": "~14.0.0",
|
||||
"@angular-devkit/schematics": "~14.0.0",
|
||||
"@angular/cli": "~14.0.0",
|
||||
"@schematics/angular": "~14.0.0",
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -25,8 +25,8 @@ export default async function (tree: Tree) {
|
||||
);
|
||||
|
||||
const angularCliVersion =
|
||||
pkgJson.devDependencies?.['@angular/cli'] ??
|
||||
pkgJson.dependencies?.['@angular/cli'] ??
|
||||
pkgJson.devDependencies?.['@angular-devkit/build-angular'] ??
|
||||
pkgJson.dependencies?.['@angular-devkit/build-angular'] ??
|
||||
angularDevkitVersion;
|
||||
|
||||
const filteredPackages: Record<string, string> = packagesToInstall
|
||||
|
||||
@@ -35,6 +35,15 @@ export const backwardCompatibleVersions: Record<
|
||||
autoprefixerVersion: '^10.4.0',
|
||||
tsNodeVersion: '10.9.1',
|
||||
jestPresetAngularVersion: '~12.2.3',
|
||||
karmaVersion: '~6.4.0',
|
||||
karmaChromeLauncherVersion: '~3.1.0',
|
||||
karmaCoverageVersion: '~2.2.0',
|
||||
karmaJasmineVersion: '~5.1.0',
|
||||
karmaJasmineHtmlReporterVersion: '~2.0.0',
|
||||
jasmineCoreVersion: '~4.2.0',
|
||||
jasmineSpecReporterVersion: '~7.0.0',
|
||||
typesJasmineVersion: '~4.0.0',
|
||||
typesJasminewd2Version: '~2.0.3',
|
||||
typesNodeVersion: '16.11.7',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
},
|
||||
|
||||
@@ -60,10 +60,10 @@ export async function getModuleFederationConfig(
|
||||
|
||||
if (mfConfig.shared) {
|
||||
dependencies.workspaceLibraries = dependencies.workspaceLibraries.filter(
|
||||
(lib) => mfConfig.shared(lib.importKey, {})
|
||||
(lib) => mfConfig.shared(lib.importKey, {}) !== false
|
||||
);
|
||||
dependencies.npmPackages = dependencies.npmPackages.filter((pkg) =>
|
||||
mfConfig.shared(pkg, {})
|
||||
dependencies.npmPackages = dependencies.npmPackages.filter(
|
||||
(pkg) => mfConfig.shared(pkg, {}) !== false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export enum UnitTestRunner {
|
||||
Karma = 'karma',
|
||||
Jest = 'jest',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export enum E2eTestRunner {
|
||||
Cypress = 'cypress',
|
||||
None = 'none',
|
||||
|
||||
@@ -26,5 +26,14 @@ export const autoprefixerVersion = '^10.4.0';
|
||||
export const tsNodeVersion = '10.9.1';
|
||||
|
||||
export const jestPresetAngularVersion = '~13.0.0';
|
||||
export const karmaVersion = '~6.4.0';
|
||||
export const karmaChromeLauncherVersion = '~3.1.0';
|
||||
export const karmaCoverageVersion = '~2.2.0';
|
||||
export const karmaJasmineVersion = '~5.1.0';
|
||||
export const karmaJasmineHtmlReporterVersion = '~2.0.0';
|
||||
export const jasmineCoreVersion = '~4.2.0';
|
||||
export const jasmineSpecReporterVersion = '~7.0.0';
|
||||
export const typesJasmineVersion = '~4.0.0';
|
||||
export const typesJasminewd2Version = '~2.0.3';
|
||||
export const typesNodeVersion = '16.11.7';
|
||||
export const jasmineMarblesVersion = '^0.9.2';
|
||||
|
||||
@@ -11,7 +11,7 @@ export function createGlobPatternsForDependencies(
|
||||
) {
|
||||
try {
|
||||
return jsGenerateGlobs(dirPath, fileGlobPattern);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
/**
|
||||
* It should not be possible to reach this point when the utility is invoked as part of the normal
|
||||
* lifecycle of Nx executors. However, other tooling, such as the VSCode Tailwind IntelliSense plugin
|
||||
@@ -22,7 +22,8 @@ export function createGlobPatternsForDependencies(
|
||||
* fundamently unavailable in this tailwind-specific context.
|
||||
*/
|
||||
console.warn(
|
||||
'\n[createGlobPatternsForDependencies] WARNING: There was no ProjectGraph available to read from, returning an empty array of glob patterns\n'
|
||||
'\nWARNING: There was an error creating glob patterns, returning an empty array\n' +
|
||||
`${e.message}\n`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ export function spawnAndWait(command: string, args: string[], cwd: string) {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, NX_DAEMON: 'false' },
|
||||
shell: true,
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
childProcess.on('exit', (code) => {
|
||||
@@ -33,7 +35,8 @@ export function execAndWait(command: string, cwd: string) {
|
||||
if (error) {
|
||||
const logFile = join(cwd, 'error.log');
|
||||
writeFileSync(logFile, `${stdout}\n${stderr}`);
|
||||
rej(new CreateNxWorkspaceError(stderr, error.code, logFile));
|
||||
const message = stderr && stderr.trim().length ? stderr : stdout;
|
||||
rej(new CreateNxWorkspaceError(message, error.code, logFile));
|
||||
} else {
|
||||
res({ code: 0, stdout });
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export class CreateNxWorkspaceError extends Error {
|
||||
}
|
||||
|
||||
export function mapErrorToBodyLines(error: Error): string[] {
|
||||
const errorLines = error.message?.split('\n').filter((line) => !!line);
|
||||
const errorLines = error.message?.split('\n').filter((line) => !!line.trim());
|
||||
if (errorLines.length < 3) {
|
||||
const lines = [`Error: ${error.message}`];
|
||||
if (process.env.NX_VERBOSE_LOGGING) {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"@nrwl/workspace": "file:../workspace",
|
||||
"@phenomnomnominal/tsquery": "4.1.1",
|
||||
"dotenv": "~10.0.0",
|
||||
"semver": "7.3.4",
|
||||
"semver": "7.5.4",
|
||||
"detect-port": "^1.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,35 +1,57 @@
|
||||
// Adapted from: https://github.com/mammadataei/cypress-vite
|
||||
/**
|
||||
* https://github.com/mammadataei/cypress-vite
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Mohammad Ataei
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**/
|
||||
|
||||
import * as path from 'path';
|
||||
import { dirname, basename, extname } from 'path';
|
||||
import type { RollupOutput, RollupWatcher, WatcherOptions } from 'rollup';
|
||||
import type { InlineConfig } from 'vite';
|
||||
|
||||
type CypressPreprocessor = (
|
||||
file: Record<string, any>
|
||||
) => string | Promise<string>;
|
||||
|
||||
type BuildResult = RollupWatcher | RollupOutput | RollupOutput[];
|
||||
|
||||
const cache = new Map<string, 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')),
|
||||
* )
|
||||
* },
|
||||
* Use Vite as a file preprocess for Cypress test files.
|
||||
* This preprocessor shouldn't be used directly.
|
||||
* Instead, use the nxE2EPreset(__filename, { bundler: 'vite' }) function instead.
|
||||
*/
|
||||
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)
|
||||
);
|
||||
if (cache.has(filePath)) {
|
||||
return cache.get(filePath);
|
||||
}
|
||||
|
||||
const defaultConfig = {
|
||||
const fileName = basename(outputPath);
|
||||
const filenameWithoutExtension = basename(outputPath, extname(outputPath));
|
||||
|
||||
const defaultConfig: InlineConfig = {
|
||||
logLevel: 'silent',
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
@@ -37,7 +59,7 @@ function vitePreprocessor(userConfigPath?: string): CypressPreprocessor {
|
||||
build: {
|
||||
emptyOutDir: false,
|
||||
minify: false,
|
||||
outDir: path.dirname(outputPath),
|
||||
outDir: dirname(outputPath),
|
||||
sourcemap: true,
|
||||
write: true,
|
||||
watch: getWatcherConfig(shouldWatch),
|
||||
@@ -49,41 +71,47 @@ function vitePreprocessor(userConfigPath?: string): CypressPreprocessor {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
cache.set(filePath, outputPath);
|
||||
|
||||
const { build } = require('vite');
|
||||
|
||||
const watcher = await build({
|
||||
const watcher = (await build({
|
||||
configFile: userConfigPath,
|
||||
...defaultConfig,
|
||||
})) as BuildResult;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (shouldWatch && isWatcher(watcher)) {
|
||||
watcher.on('event', (event) => {
|
||||
if (event.code === 'END') {
|
||||
resolve(outputPath);
|
||||
file.emit('rerun');
|
||||
}
|
||||
|
||||
if (event.code === 'ERROR') {
|
||||
console.error(event);
|
||||
reject(new Error(event.error.message));
|
||||
}
|
||||
});
|
||||
|
||||
file.on('close', () => {
|
||||
cache.delete(filePath);
|
||||
watcher.close();
|
||||
});
|
||||
} else {
|
||||
resolve(outputPath);
|
||||
}
|
||||
});
|
||||
|
||||
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 isWatcher(maybeWatcher: any): maybeWatcher is RollupWatcher {
|
||||
return maybeWatcher.on !== undefined;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -39,7 +39,7 @@ export function getTempTailwindPath(context: ExecutorContext) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the childProjectName is a decendent of the parentProjectName
|
||||
* Checks if the childProjectName is a descendent of the parentProjectName
|
||||
* in the project graph
|
||||
**/
|
||||
export function isCtProjectUsingBuildProject(
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"ignore": "^5.0.4",
|
||||
"tmp": "~0.2.1",
|
||||
"tslib": "^2.3.0",
|
||||
"semver": "7.3.4"
|
||||
"semver": "7.5.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"nx": ">= 14.1 <= 16"
|
||||
|
||||
@@ -11,8 +11,13 @@ import { requireNx } from '../../nx';
|
||||
import { dirSync } from 'tmp';
|
||||
import { join } from 'path';
|
||||
|
||||
const { readJson, updateJson, getPackageManagerCommand, workspaceRoot } =
|
||||
requireNx();
|
||||
const {
|
||||
readJson,
|
||||
updateJson,
|
||||
getPackageManagerCommand,
|
||||
workspaceRoot,
|
||||
detectPackageManager,
|
||||
} = requireNx();
|
||||
|
||||
const UNIDENTIFIED_VERSION = 'UNIDENTIFIED_VERSION';
|
||||
const NON_SEMVER_TAGS = {
|
||||
@@ -448,9 +453,16 @@ export function ensurePackage<T extends any = any>(
|
||||
const tempDir = dirSync().name;
|
||||
|
||||
console.log(`Fetching ${pkg}...`);
|
||||
execSync(`${getPackageManagerCommand().addDev} ${pkg}@${requiredVersion}`, {
|
||||
const packageManager = detectPackageManager();
|
||||
let addCommand = getPackageManagerCommand(packageManager).addDev;
|
||||
if (packageManager === 'pnpm') {
|
||||
addCommand = 'pnpm add -D'; // we need to ensure that we are not using workspace command
|
||||
}
|
||||
|
||||
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
|
||||
execSync(`${addCommand} ${pkg}@${requiredVersion}`, {
|
||||
cwd: tempDir,
|
||||
stdio: 'ignore',
|
||||
stdio: isVerbose ? 'inherit' : 'ignore',
|
||||
});
|
||||
|
||||
addToNodePath(join(workspaceRoot, 'node_modules'));
|
||||
|
||||
@@ -43,6 +43,7 @@ describe('normalizeOptions', () => {
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
thirdParty: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,6 +70,7 @@ describe('normalizeOptions', () => {
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
singleEntry: false,
|
||||
external: [],
|
||||
thirdParty: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,6 +96,7 @@ describe('normalizeOptions', () => {
|
||||
outputFileName: 'test.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
thirdParty: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -108,6 +111,7 @@ describe('normalizeOptions', () => {
|
||||
generatePackageJson: true,
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
outputFileName: 'test.js',
|
||||
thirdParty: false,
|
||||
},
|
||||
context
|
||||
)
|
||||
@@ -135,6 +139,24 @@ describe('normalizeOptions', () => {
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
thirdParty: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should override thirdParty if bundle:false', () => {
|
||||
expect(
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
generatePackageJson: true,
|
||||
bundle: false,
|
||||
thirdParty: true,
|
||||
assets: [],
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual(expect.objectContaining({ thirdParty: false }));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
EsBuildExecutorOptions,
|
||||
NormalizedEsBuildExecutorOptions,
|
||||
} from '../schema';
|
||||
import { ExecutorContext, joinPathFragments } from '@nrwl/devkit';
|
||||
import { ExecutorContext, joinPathFragments, logger } from '@nrwl/devkit';
|
||||
import chalk = require('chalk');
|
||||
|
||||
export function normalizeOptions(
|
||||
options: EsBuildExecutorOptions,
|
||||
@@ -20,6 +21,21 @@ export function normalizeOptions(
|
||||
),
|
||||
];
|
||||
|
||||
if (!options.bundle && options.thirdParty) {
|
||||
logger.info(
|
||||
chalk.yellow(
|
||||
`Your build has conflicting options, ${chalk.bold(
|
||||
'bundle:false'
|
||||
)} and ${chalk.bold(
|
||||
'thirdParty:true'
|
||||
)}. Your package.json depedencies might not be generated correctly so we added an update ${chalk.bold(
|
||||
'thirdParty:false'
|
||||
)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const thirdParty = !options.bundle ? false : options.thirdParty;
|
||||
if (options.additionalEntryPoints?.length > 0) {
|
||||
const { outputFileName, ...rest } = options;
|
||||
if (outputFileName) {
|
||||
@@ -29,6 +45,7 @@ export function normalizeOptions(
|
||||
}
|
||||
return {
|
||||
...rest,
|
||||
thirdParty,
|
||||
assets,
|
||||
external: options.external ?? [],
|
||||
singleEntry: false,
|
||||
@@ -40,6 +57,7 @@ export function normalizeOptions(
|
||||
} else {
|
||||
return {
|
||||
...options,
|
||||
thirdParty,
|
||||
assets,
|
||||
external: options.external ?? [],
|
||||
singleEntry: true,
|
||||
|
||||
@@ -128,8 +128,7 @@
|
||||
},
|
||||
"thirdParty": {
|
||||
"type": "boolean",
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages).",
|
||||
"default": true
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages)."
|
||||
},
|
||||
"dependenciesFieldType": {
|
||||
"type": "string",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@typescript-eslint/utils": "^5.36.1",
|
||||
"chalk": "^4.1.0",
|
||||
"confusing-browser-globals": "^1.0.9",
|
||||
"semver": "7.3.4"
|
||||
"semver": "7.5.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -317,7 +317,11 @@ export default createESLintRule<Options, MessageIds>({
|
||||
if (
|
||||
!allowCircularSelfDependency &&
|
||||
!isRelativePath(imp) &&
|
||||
!isAngularSecondaryEntrypoint(imp, sourceFilePath)
|
||||
!isAngularSecondaryEntrypoint(
|
||||
imp,
|
||||
sourceFilePath,
|
||||
sourceProject.data.root
|
||||
)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
|
||||
@@ -198,19 +198,33 @@ export function getRelativeImportPath(exportedMember, filePath, basePath) {
|
||||
|
||||
const modulePath = (exportDeclaration as any).moduleSpecifier.text;
|
||||
|
||||
let moduleFilePath = joinPathFragments(
|
||||
dirname(filePath),
|
||||
`${modulePath}.ts`
|
||||
);
|
||||
if (!existsSync(moduleFilePath)) {
|
||||
// might be a tsx file
|
||||
let moduleFilePath;
|
||||
if (modulePath.endsWith('.js') || modulePath.endsWith('.jsx')) {
|
||||
moduleFilePath = joinPathFragments(dirname(filePath), modulePath);
|
||||
if (!existsSync(moduleFilePath)) {
|
||||
const tsifiedModulePath = modulePath.replace(/\.js(x?)$/, '.ts$1');
|
||||
moduleFilePath = joinPathFragments(
|
||||
dirname(filePath),
|
||||
`${tsifiedModulePath}`
|
||||
);
|
||||
}
|
||||
} else if (modulePath.endsWith('.ts') || modulePath.endsWith('.tsx')) {
|
||||
moduleFilePath = joinPathFragments(dirname(filePath), modulePath);
|
||||
} else {
|
||||
moduleFilePath = joinPathFragments(
|
||||
dirname(filePath),
|
||||
`${modulePath}.tsx`
|
||||
`${modulePath}.ts`
|
||||
);
|
||||
if (!existsSync(moduleFilePath)) {
|
||||
// might be a tsx file
|
||||
moduleFilePath = joinPathFragments(
|
||||
dirname(filePath),
|
||||
`${modulePath}.tsx`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!existsSync(moduleFilePath)) {
|
||||
// might be a index.ts
|
||||
// might be an index.ts
|
||||
moduleFilePath = joinPathFragments(
|
||||
dirname(filePath),
|
||||
`${modulePath}/index.ts`
|
||||
|
||||
@@ -401,6 +401,7 @@ describe('isAngularSecondaryEntrypoint', () => {
|
||||
],
|
||||
'@project/features': ['libs/features/src/index.ts'],
|
||||
'@project/features/*': ['libs/features/*/random/folder/api.ts'],
|
||||
'@project/buildable': ['libs/buildable/src/index.ts'],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -421,25 +422,55 @@ describe('isAngularSecondaryEntrypoint', () => {
|
||||
lib: { entryFile: 'random/folder/api.ts' },
|
||||
}),
|
||||
'libs/features/secondary/random/folder/api.ts': 'const bla = "foo"',
|
||||
'libs/buildable/ng-package.json': JSON.stringify({
|
||||
lib: { entryFile: 'src/index.ts' },
|
||||
}),
|
||||
};
|
||||
vol.fromJSON(fsJson, '/root');
|
||||
});
|
||||
|
||||
it('should return true for secondary entrypoints', () => {
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint('@project/standard', 'apps/app.ts')
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/standard',
|
||||
'apps/app.ts',
|
||||
'libs/standard'
|
||||
)
|
||||
).toBe(false);
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint('@project/standard/secondary', 'apps/app.ts')
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/standard/secondary',
|
||||
'apps/app.ts',
|
||||
'libs/standard'
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint('@project/standard/tertiary', 'apps/app.ts')
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/standard/tertiary',
|
||||
'apps/app.ts',
|
||||
'libs/standard'
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint('@project/features', 'apps/app.ts')
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/features',
|
||||
'apps/app.ts',
|
||||
'libs/features'
|
||||
)
|
||||
).toBe(false);
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint('@project/features/secondary', 'apps/app.ts')
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/features/secondary',
|
||||
'apps/app.ts',
|
||||
'libs/features'
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
isAngularSecondaryEntrypoint(
|
||||
'@project/buildable',
|
||||
'apps/app.ts',
|
||||
'libs/buildable'
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -447,7 +447,8 @@ export function groupImports(
|
||||
*/
|
||||
export function isAngularSecondaryEntrypoint(
|
||||
importExpr: string,
|
||||
filePath: string
|
||||
filePath: string,
|
||||
projectRoot: string
|
||||
): boolean {
|
||||
const resolvedModule = resolveModuleByImport(
|
||||
importExpr,
|
||||
@@ -455,12 +456,14 @@ export function isAngularSecondaryEntrypoint(
|
||||
join(workspaceRoot, getRootTsConfigFileName())
|
||||
);
|
||||
|
||||
return !!resolvedModule && fileIsSecondaryEntryPoint(resolvedModule);
|
||||
return (
|
||||
!!resolvedModule && fileIsSecondaryEntryPoint(resolvedModule, projectRoot)
|
||||
);
|
||||
}
|
||||
|
||||
function fileIsSecondaryEntryPoint(file: string): boolean {
|
||||
function fileIsSecondaryEntryPoint(file: string, projectRoot: string): boolean {
|
||||
let parent = joinPathFragments(file, '../');
|
||||
while (parent !== './') {
|
||||
while (parent !== `${projectRoot}/`) {
|
||||
// we need to find closest existing ng-package.json
|
||||
// in order to determine if the file matches the secondary entry point
|
||||
const ngPackageContent = readFileIfExisting(
|
||||
|
||||
@@ -205,6 +205,7 @@ function getPnpmResolver(extensions: string[]) {
|
||||
modules: [join(workspaceRoot, 'node_modules'), 'node_modules'],
|
||||
conditionNames: ['native', 'browser', 'require', 'default'],
|
||||
mainFields: ['react-native', 'browser', 'main'],
|
||||
aliasFields: ['browser'],
|
||||
});
|
||||
}
|
||||
return resolver;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user