feat(angular): add scam generator for pipes and directives (#8144)
ISSUES CLOSED: #8122
This commit is contained in:
parent
bc8dda6c44
commit
4bb109e175
86
docs/angular/api-angular/generators/scam-directive.md
Normal file
86
docs/angular/api-angular/generators/scam-directive.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-directive generator'
|
||||||
|
description: 'Generate a directive with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-directive
|
||||||
|
|
||||||
|
Generate a directive with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-directive` in the default collection provisioned in `angular.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-directive ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the directive.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Directive.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.
|
||||||
|
|
||||||
|
### prefix
|
||||||
|
|
||||||
|
Alias(es): p
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The prefix to apply to the generated directive selector.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### selector
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The HTML selector to use for this directive.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new directive.
|
||||||
72
docs/angular/api-angular/generators/scam-pipe.md
Normal file
72
docs/angular/api-angular/generators/scam-pipe.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-pipe generator'
|
||||||
|
description: 'Generate a pipe with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-pipe
|
||||||
|
|
||||||
|
Generate a pipe with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-pipe` in the default collection provisioned in `angular.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-pipe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the pipe.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Pipe.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new pipe.
|
||||||
@ -473,6 +473,16 @@
|
|||||||
"id": "scam",
|
"id": "scam",
|
||||||
"file": "angular/api-angular/generators/scam"
|
"file": "angular/api-angular/generators/scam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-directive generator",
|
||||||
|
"id": "scam-directive",
|
||||||
|
"file": "angular/api-angular/generators/scam-directive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-pipe generator",
|
||||||
|
"id": "scam-pipe",
|
||||||
|
"file": "angular/api-angular/generators/scam-pipe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setup-mfe generator",
|
"name": "setup-mfe generator",
|
||||||
"id": "setup-mfe",
|
"id": "setup-mfe",
|
||||||
@ -1812,6 +1822,16 @@
|
|||||||
"id": "scam",
|
"id": "scam",
|
||||||
"file": "react/api-angular/generators/scam"
|
"file": "react/api-angular/generators/scam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-directive generator",
|
||||||
|
"id": "scam-directive",
|
||||||
|
"file": "react/api-angular/generators/scam-directive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-pipe generator",
|
||||||
|
"id": "scam-pipe",
|
||||||
|
"file": "react/api-angular/generators/scam-pipe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setup-mfe generator",
|
"name": "setup-mfe generator",
|
||||||
"id": "setup-mfe",
|
"id": "setup-mfe",
|
||||||
@ -3115,6 +3135,16 @@
|
|||||||
"id": "scam",
|
"id": "scam",
|
||||||
"file": "node/api-angular/generators/scam"
|
"file": "node/api-angular/generators/scam"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-directive generator",
|
||||||
|
"id": "scam-directive",
|
||||||
|
"file": "node/api-angular/generators/scam-directive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scam-pipe generator",
|
||||||
|
"id": "scam-pipe",
|
||||||
|
"file": "node/api-angular/generators/scam-pipe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setup-mfe generator",
|
"name": "setup-mfe generator",
|
||||||
"id": "setup-mfe",
|
"id": "setup-mfe",
|
||||||
|
|||||||
86
docs/node/api-angular/generators/scam-directive.md
Normal file
86
docs/node/api-angular/generators/scam-directive.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-directive generator'
|
||||||
|
description: 'Generate a directive with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-directive
|
||||||
|
|
||||||
|
Generate a directive with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-directive` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-directive ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the directive.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Directive.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.
|
||||||
|
|
||||||
|
### prefix
|
||||||
|
|
||||||
|
Alias(es): p
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The prefix to apply to the generated directive selector.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### selector
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The HTML selector to use for this directive.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new directive.
|
||||||
72
docs/node/api-angular/generators/scam-pipe.md
Normal file
72
docs/node/api-angular/generators/scam-pipe.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-pipe generator'
|
||||||
|
description: 'Generate a pipe with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-pipe
|
||||||
|
|
||||||
|
Generate a pipe with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-pipe` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-pipe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the pipe.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Pipe.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new pipe.
|
||||||
86
docs/react/api-angular/generators/scam-directive.md
Normal file
86
docs/react/api-angular/generators/scam-directive.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-directive generator'
|
||||||
|
description: 'Generate a directive with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-directive
|
||||||
|
|
||||||
|
Generate a directive with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-directive` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-directive ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-directive ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the directive.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Directive.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.
|
||||||
|
|
||||||
|
### prefix
|
||||||
|
|
||||||
|
Alias(es): p
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The prefix to apply to the generated directive selector.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### selector
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The HTML selector to use for this directive.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new directive.
|
||||||
72
docs/react/api-angular/generators/scam-pipe.md
Normal file
72
docs/react/api-angular/generators/scam-pipe.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
title: '@nrwl/angular:scam-pipe generator'
|
||||||
|
description: 'Generate a pipe with an accompanying Single Component Angular Module (SCAM).'
|
||||||
|
---
|
||||||
|
|
||||||
|
# @nrwl/angular:scam-pipe
|
||||||
|
|
||||||
|
Generate a pipe with an accompanying Single Component Angular Module (SCAM).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `scam-pipe` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:scam-pipe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g scam-pipe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### name (_**required**_)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the pipe.
|
||||||
|
|
||||||
|
### flat
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the new files at the top level of the current project.
|
||||||
|
|
||||||
|
### inlineScam
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Create the NgModule in the same file as the Pipe.
|
||||||
|
|
||||||
|
### path (**hidden**)
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.
|
||||||
|
|
||||||
|
### project
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the project.
|
||||||
|
|
||||||
|
### skipTests
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Do not create "spec.ts" test files for the new pipe.
|
||||||
@ -115,6 +115,16 @@
|
|||||||
"schema": "./src/generators/scam/schema.json",
|
"schema": "./src/generators/scam/schema.json",
|
||||||
"description": "Generate a component with an accompanying Single Component Angular Module (SCAM)."
|
"description": "Generate a component with an accompanying Single Component Angular Module (SCAM)."
|
||||||
},
|
},
|
||||||
|
"scam-directive": {
|
||||||
|
"factory": "./src/generators/scam-directive/scam-directive.compat",
|
||||||
|
"schema": "./src/generators/scam-directive/schema.json",
|
||||||
|
"description": "Generate a directive with an accompanying Single Component Angular Module (SCAM)."
|
||||||
|
},
|
||||||
|
"scam-pipe": {
|
||||||
|
"factory": "./src/generators/scam-pipe/scam-pipe.compat",
|
||||||
|
"schema": "./src/generators/scam-pipe/schema.json",
|
||||||
|
"description": "Generate a pipe with an accompanying Single Component Angular Module (SCAM)."
|
||||||
|
},
|
||||||
|
|
||||||
"web-worker": {
|
"web-worker": {
|
||||||
"factory": "./src/generators/web-worker/compat",
|
"factory": "./src/generators/web-worker/compat",
|
||||||
@ -209,6 +219,16 @@
|
|||||||
"schema": "./src/generators/scam/schema.json",
|
"schema": "./src/generators/scam/schema.json",
|
||||||
"description": "Generate a component with an accompanying Single Component Angular Module (SCAM)."
|
"description": "Generate a component with an accompanying Single Component Angular Module (SCAM)."
|
||||||
},
|
},
|
||||||
|
"scam-directive": {
|
||||||
|
"factory": "./src/generators/scam-directive/scam-directive",
|
||||||
|
"schema": "./src/generators/scam-directive/schema.json",
|
||||||
|
"description": "Generate a directive with an accompanying Single Component Angular Module (SCAM)."
|
||||||
|
},
|
||||||
|
"scam-pipe": {
|
||||||
|
"factory": "./src/generators/scam-pipe/scam-pipe",
|
||||||
|
"schema": "./src/generators/scam-pipe/schema.json",
|
||||||
|
"description": "Generate a pipe with an accompanying Single Component Angular Module (SCAM)."
|
||||||
|
},
|
||||||
"stories": {
|
"stories": {
|
||||||
"factory": "./src/generators/stories/stories",
|
"factory": "./src/generators/stories/stories",
|
||||||
"schema": "./src/generators/stories/schema.json",
|
"schema": "./src/generators/stories/schema.json",
|
||||||
|
|||||||
@ -15,3 +15,5 @@ export * from './src/generators/storybook-migrate-stories-to-6-2/migrate-stories
|
|||||||
export * from './src/generators/upgrade-module/upgrade-module';
|
export * from './src/generators/upgrade-module/upgrade-module';
|
||||||
export * from './src/generators/setup-mfe/setup-mfe';
|
export * from './src/generators/setup-mfe/setup-mfe';
|
||||||
export * from './src/generators/scam/scam';
|
export * from './src/generators/scam/scam';
|
||||||
|
export * from './src/generators/scam-directive/scam-directive';
|
||||||
|
export * from './src/generators/scam-pipe/scam-pipe';
|
||||||
|
|||||||
@ -0,0 +1,331 @@
|
|||||||
|
import { addProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import { createScamDirective } from './create-module';
|
||||||
|
|
||||||
|
describe('Create module in the tree', () => {
|
||||||
|
it('should create the scam directive inline correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(directiveSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam directive separately correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExampleDirective } from './example.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam directive inline correctly when --flat', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveSource = tree.read(
|
||||||
|
'apps/app1/src/app/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam directive separately correctly when --flat', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExampleDirective } from './example.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place the directive and scam in the correct folder when --path is used', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
flat: false,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place the directive and scam in the correct folder when --path and --flat is used', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamDirective(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
flat: true,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
names,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
normalizePath,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
|
||||||
|
import { createSourceFile, ScriptTarget } from 'typescript';
|
||||||
|
|
||||||
|
export function createScamDirective(tree: Tree, schema: Schema) {
|
||||||
|
const project =
|
||||||
|
schema.project ?? readWorkspaceConfiguration(tree).defaultProject;
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project);
|
||||||
|
|
||||||
|
const directiveNames = names(schema.name);
|
||||||
|
const typeNames = names('directive');
|
||||||
|
|
||||||
|
const directiveFileName = `${directiveNames.fileName}.directive`;
|
||||||
|
|
||||||
|
let directiveDirectory = schema.flat
|
||||||
|
? joinPathFragments(
|
||||||
|
projectConfig.sourceRoot,
|
||||||
|
projectConfig.projectType === 'application' ? 'app' : 'lib'
|
||||||
|
)
|
||||||
|
: joinPathFragments(
|
||||||
|
projectConfig.sourceRoot,
|
||||||
|
projectConfig.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
directiveNames.fileName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (schema.path) {
|
||||||
|
directiveDirectory = schema.flat
|
||||||
|
? normalizePath(schema.path)
|
||||||
|
: joinPathFragments(schema.path, directiveNames.fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const directiveFilePath = joinPathFragments(
|
||||||
|
directiveDirectory,
|
||||||
|
`${directiveFileName}.ts`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tree.exists(directiveFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`Couldn't find directive at path ${directiveFilePath} to add SCAM setup.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.inlineScam) {
|
||||||
|
const currentDirectiveContents = tree.read(directiveFilePath, 'utf-8');
|
||||||
|
let source = createSourceFile(
|
||||||
|
directiveFilePath,
|
||||||
|
currentDirectiveContents,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
source = insertImport(
|
||||||
|
tree,
|
||||||
|
source,
|
||||||
|
directiveFilePath,
|
||||||
|
'NgModule',
|
||||||
|
'@angular/core'
|
||||||
|
);
|
||||||
|
|
||||||
|
source = insertImport(
|
||||||
|
tree,
|
||||||
|
source,
|
||||||
|
directiveFilePath,
|
||||||
|
'CommonModule',
|
||||||
|
'@angular/common'
|
||||||
|
);
|
||||||
|
|
||||||
|
let updatedDirectiveSource = source.getText();
|
||||||
|
|
||||||
|
updatedDirectiveSource = `${updatedDirectiveSource}${createAngularDirectiveModule(
|
||||||
|
`${directiveNames.className}${typeNames.className}`
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
tree.write(directiveFilePath, updatedDirectiveSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
joinPathFragments(
|
||||||
|
directiveDirectory,
|
||||||
|
`${directiveNames.fileName}.module.ts`
|
||||||
|
),
|
||||||
|
createSeparateAngularDirectiveModuleFile(
|
||||||
|
`${directiveNames.className}${typeNames.className}`,
|
||||||
|
directiveFileName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAngularDirectiveModule(name: string) {
|
||||||
|
return `
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [${name}],
|
||||||
|
exports: [${name}],
|
||||||
|
})
|
||||||
|
export class ${name}Module {}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSeparateAngularDirectiveModuleFile(
|
||||||
|
name: string,
|
||||||
|
directiveFileName: string
|
||||||
|
) {
|
||||||
|
return `import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ${name} } from './${directiveFileName}';
|
||||||
|
${createAngularDirectiveModule(name)}`;
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import scamGenerator from './scam-directive';
|
||||||
|
import { convertNxGenerator } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export default convertNxGenerator(scamGenerator);
|
||||||
@ -0,0 +1,203 @@
|
|||||||
|
import { addProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import scamDirectiveGenerator from './scam-directive';
|
||||||
|
|
||||||
|
describe('SCAM Directive Generator', () => {
|
||||||
|
it('should create the inline scam directive correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamDirectiveGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveSource = tree.read(
|
||||||
|
'apps/app1/src/app/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the separate scam directive correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamDirectiveGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExampleDirective } from './example.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('--path', () => {
|
||||||
|
it('should not throw when the path does not exist under project', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamDirectiveGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not matter if the path starts with a slash', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamDirectiveGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: '/apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const directiveSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.directive.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(directiveSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[example]'
|
||||||
|
})
|
||||||
|
export class ExampleDirective {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExampleDirective],
|
||||||
|
exports: [ExampleDirective],
|
||||||
|
})
|
||||||
|
export class ExampleDirectiveModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when the path does not exist under project', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
try {
|
||||||
|
await scamDirectiveGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: 'libs/proj/src/lib/random',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// ASSERT
|
||||||
|
expect(error).toMatchInlineSnapshot(
|
||||||
|
`[Error: The path provided for the SCAM (libs/proj/src/lib/random) does not exist under the project root (apps/app1).]`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
|
normalizePath,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { createScamDirective } from './lib/create-module';
|
||||||
|
import { normalize } from 'path';
|
||||||
|
|
||||||
|
export async function scamDirectiveGenerator(tree: Tree, schema: Schema) {
|
||||||
|
const { inlineScam, ...options } = schema;
|
||||||
|
|
||||||
|
checkPathUnderProjectRoot(tree, options);
|
||||||
|
|
||||||
|
const angularDirectiveSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'directive'
|
||||||
|
);
|
||||||
|
await angularDirectiveSchematic(tree, {
|
||||||
|
...options,
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
createScamDirective(tree, schema);
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPathUnderProjectRoot(tree: Tree, options: Partial<Schema>) {
|
||||||
|
if (!options.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const project =
|
||||||
|
options.project ?? readWorkspaceConfiguration(tree).defaultProject;
|
||||||
|
const { root } = readProjectConfiguration(tree, project);
|
||||||
|
|
||||||
|
let pathToDirective = normalizePath(options.path);
|
||||||
|
pathToDirective = pathToDirective.startsWith('/')
|
||||||
|
? pathToDirective.slice(1)
|
||||||
|
: pathToDirective;
|
||||||
|
|
||||||
|
if (!pathToDirective.startsWith(normalize(root))) {
|
||||||
|
throw new Error(
|
||||||
|
`The path provided for the SCAM (${options.path}) does not exist under the project root (${root}).`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default scamDirectiveGenerator;
|
||||||
10
packages/angular/src/generators/scam-directive/schema.d.ts
vendored
Normal file
10
packages/angular/src/generators/scam-directive/schema.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface Schema {
|
||||||
|
name: string;
|
||||||
|
path?: string;
|
||||||
|
project?: string;
|
||||||
|
skipTests?: boolean;
|
||||||
|
inlineScam?: boolean;
|
||||||
|
flat?: boolean;
|
||||||
|
prefix?: string;
|
||||||
|
selector?: string;
|
||||||
|
}
|
||||||
68
packages/angular/src/generators/scam-directive/schema.json
Normal file
68
packages/angular/src/generators/scam-directive/schema.json
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "SCAMDirectiveGenerator",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "SCAM Directive Generator Options Schema",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Creates a new, generic directive definition in the given or default project.",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "path",
|
||||||
|
"description": "The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.",
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the project.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "projectName"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the directive.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-prompt": "What name would you like to use for the directive?"
|
||||||
|
},
|
||||||
|
"skipTests": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Do not create \"spec.ts\" test files for the new directive.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"inlineScam": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Create the NgModule in the same file as the Directive.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"flat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Create the new files at the top level of the current project.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "html-selector",
|
||||||
|
"description": "The HTML selector to use for this directive."
|
||||||
|
},
|
||||||
|
"prefix": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The prefix to apply to the generated directive selector.",
|
||||||
|
"alias": "p",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"maxLength": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minLength": 1,
|
||||||
|
"format": "html-selector"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
@ -0,0 +1,335 @@
|
|||||||
|
import { addProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import { createScamPipe } from './create-module';
|
||||||
|
|
||||||
|
describe('Create module in the tree', () => {
|
||||||
|
it('should create the scam pipe inline correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam pipe separately correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExamplePipe } from './example.pipe';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam pipe inline correctly when --flat', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeSource = tree.read('apps/app1/src/app/example.pipe.ts', 'utf-8');
|
||||||
|
expect(pipeSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the scam pipe separately correctly when --flat', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExamplePipe } from './example.pipe';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place the pipe and scam pipe in the correct folder when --path is used', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: false,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
flat: false,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should place the pipe and scam pipe in the correct folder when --path and --flat is used', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const angularComponentSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularComponentSchematic(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
flat: true,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
createScamPipe(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
flat: true,
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
111
packages/angular/src/generators/scam-pipe/lib/create-module.ts
Normal file
111
packages/angular/src/generators/scam-pipe/lib/create-module.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
names,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
normalizePath,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils';
|
||||||
|
import { createSourceFile, ScriptTarget } from 'typescript';
|
||||||
|
|
||||||
|
export function createScamPipe(tree: Tree, schema: Schema) {
|
||||||
|
const project =
|
||||||
|
schema.project ?? readWorkspaceConfiguration(tree).defaultProject;
|
||||||
|
const projectConfig = readProjectConfiguration(tree, project);
|
||||||
|
|
||||||
|
const pipeNames = names(schema.name);
|
||||||
|
const typeNames = names('pipe');
|
||||||
|
|
||||||
|
const pipeFileName = `${pipeNames.fileName}.pipe`;
|
||||||
|
|
||||||
|
let pipeDirectory = schema.flat
|
||||||
|
? joinPathFragments(
|
||||||
|
projectConfig.sourceRoot,
|
||||||
|
projectConfig.projectType === 'application' ? 'app' : 'lib'
|
||||||
|
)
|
||||||
|
: joinPathFragments(
|
||||||
|
projectConfig.sourceRoot,
|
||||||
|
projectConfig.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
pipeNames.fileName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (schema.path) {
|
||||||
|
pipeDirectory = schema.flat
|
||||||
|
? normalizePath(schema.path)
|
||||||
|
: joinPathFragments(schema.path, pipeNames.fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pipeFilePath = joinPathFragments(pipeDirectory, `${pipeFileName}.ts`);
|
||||||
|
|
||||||
|
if (!tree.exists(pipeFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`Couldn't find pipe at path ${pipeFilePath} to add SCAM setup.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.inlineScam) {
|
||||||
|
const currentPipeContents = tree.read(pipeFilePath, 'utf-8');
|
||||||
|
let source = createSourceFile(
|
||||||
|
pipeFilePath,
|
||||||
|
currentPipeContents,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
source = insertImport(
|
||||||
|
tree,
|
||||||
|
source,
|
||||||
|
pipeFilePath,
|
||||||
|
'NgModule',
|
||||||
|
'@angular/core'
|
||||||
|
);
|
||||||
|
|
||||||
|
source = insertImport(
|
||||||
|
tree,
|
||||||
|
source,
|
||||||
|
pipeFilePath,
|
||||||
|
'CommonModule',
|
||||||
|
'@angular/common'
|
||||||
|
);
|
||||||
|
|
||||||
|
let updatedPipeSource = source.getText();
|
||||||
|
|
||||||
|
updatedPipeSource = `${updatedPipeSource}${createAngularPipeModule(
|
||||||
|
`${pipeNames.className}${typeNames.className}`
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
tree.write(pipeFilePath, updatedPipeSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
joinPathFragments(pipeDirectory, `${pipeNames.fileName}.module.ts`),
|
||||||
|
createSeparateAngularPipeModuleFile(
|
||||||
|
`${pipeNames.className}${typeNames.className}`,
|
||||||
|
pipeFileName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAngularPipeModule(name: string) {
|
||||||
|
return `
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [${name}],
|
||||||
|
exports: [${name}],
|
||||||
|
})
|
||||||
|
export class ${name}Module {}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSeparateAngularPipeModuleFile(
|
||||||
|
name: string,
|
||||||
|
pipeFileName: string
|
||||||
|
) {
|
||||||
|
return `import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ${name} } from './${pipeFileName}';
|
||||||
|
${createAngularPipeModule(name)}`;
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import scamPipeGenerator from './scam-pipe';
|
||||||
|
import { convertNxGenerator } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export default convertNxGenerator(scamPipeGenerator);
|
||||||
208
packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts
Normal file
208
packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import { addProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import scamPipeGenerator from './scam-pipe';
|
||||||
|
|
||||||
|
describe('SCAM Pipe Generator', () => {
|
||||||
|
it('should create the inline scam pipe correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamPipeGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the separate scam pipe correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamPipeGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
inlineScam: false,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeModuleSource = tree.read(
|
||||||
|
'apps/app1/src/app/example/example.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeModuleSource).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ExamplePipe } from './example.pipe';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('--path', () => {
|
||||||
|
it('should not throw when the path does not exist under project', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamPipeGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: 'apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not matter if the path starts with a slash', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await scamPipeGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: '/apps/app1/src/app/random',
|
||||||
|
inlineScam: true,
|
||||||
|
flat: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const pipeSource = tree.read(
|
||||||
|
'apps/app1/src/app/random/example/example.pipe.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(pipeSource).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe, PipeTransform, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'example'
|
||||||
|
})
|
||||||
|
export class ExamplePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: unknown, ...args: unknown[]): unknown {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [ExamplePipe],
|
||||||
|
exports: [ExamplePipe],
|
||||||
|
})
|
||||||
|
export class ExamplePipeModule {}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when the path does not exist under project', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
projectType: 'application',
|
||||||
|
sourceRoot: 'apps/app1/src',
|
||||||
|
root: 'apps/app1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
try {
|
||||||
|
await scamPipeGenerator(tree, {
|
||||||
|
name: 'example',
|
||||||
|
project: 'app1',
|
||||||
|
path: 'libs/proj/src/lib/random',
|
||||||
|
inlineScam: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// ASSERT
|
||||||
|
expect(error).toMatchInlineSnapshot(
|
||||||
|
`[Error: The path provided for the SCAM (libs/proj/src/lib/random) does not exist under the project root (apps/app1).]`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
52
packages/angular/src/generators/scam-pipe/scam-pipe.ts
Normal file
52
packages/angular/src/generators/scam-pipe/scam-pipe.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
|
normalizePath,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { createScamPipe } from './lib/create-module';
|
||||||
|
import { normalize } from 'path';
|
||||||
|
|
||||||
|
export async function scamPipeGenerator(tree: Tree, schema: Schema) {
|
||||||
|
const { inlineScam, ...options } = schema;
|
||||||
|
|
||||||
|
checkPathUnderProjectRoot(tree, options);
|
||||||
|
|
||||||
|
const angularPipeSchematic = wrapAngularDevkitSchematic(
|
||||||
|
'@schematics/angular',
|
||||||
|
'pipe'
|
||||||
|
);
|
||||||
|
await angularPipeSchematic(tree, {
|
||||||
|
...options,
|
||||||
|
skipImport: true,
|
||||||
|
export: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
createScamPipe(tree, schema);
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPathUnderProjectRoot(tree: Tree, options: Partial<Schema>) {
|
||||||
|
if (!options.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const project =
|
||||||
|
options.project ?? readWorkspaceConfiguration(tree).defaultProject;
|
||||||
|
const { root } = readProjectConfiguration(tree, project);
|
||||||
|
|
||||||
|
let pathToPipe = normalizePath(options.path);
|
||||||
|
pathToPipe = pathToPipe.startsWith('/') ? pathToPipe.slice(1) : pathToPipe;
|
||||||
|
|
||||||
|
if (!pathToPipe.startsWith(normalize(root))) {
|
||||||
|
throw new Error(
|
||||||
|
`The path provided for the SCAM (${options.path}) does not exist under the project root (${root}).`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default scamPipeGenerator;
|
||||||
8
packages/angular/src/generators/scam-pipe/schema.d.ts
vendored
Normal file
8
packages/angular/src/generators/scam-pipe/schema.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface Schema {
|
||||||
|
name: string;
|
||||||
|
path?: string;
|
||||||
|
project?: string;
|
||||||
|
skipTests?: boolean;
|
||||||
|
inlineScam?: boolean;
|
||||||
|
flat?: boolean;
|
||||||
|
}
|
||||||
49
packages/angular/src/generators/scam-pipe/schema.json
Normal file
49
packages/angular/src/generators/scam-pipe/schema.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "SCAMPipeGenerator",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "SCAM Pipe Generator Options Schema",
|
||||||
|
"type": "object",
|
||||||
|
"description": "Creates a new, generic pipe definition in the given or default project.",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "path",
|
||||||
|
"description": "The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.",
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the project.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "projectName"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the pipe.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-prompt": "What name would you like to use for the pipe?"
|
||||||
|
},
|
||||||
|
"skipTests": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Do not create \"spec.ts\" test files for the new pipe.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"inlineScam": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Create the NgModule in the same file as the Pipe.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"flat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Create the new files at the top level of the current project.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user