@nestjs/schematics 12.0.0-alpha.2 → 12.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/collection.json +0 -5
  2. package/dist/lib/application/files/ts/eslint.config.mjs +2 -3
  3. package/dist/lib/application/files/ts/package.json +4 -5
  4. package/dist/lib/application/files/ts/tsconfig.json +2 -5
  5. package/dist/lib/application/files/ts-esm/eslint.config.mjs +2 -3
  6. package/dist/lib/application/files/ts-esm/package.json +4 -5
  7. package/dist/lib/application/files/ts-esm/src/main.ts +1 -1
  8. package/dist/lib/application/files/ts-esm/tsconfig.json +3 -5
  9. package/dist/lib/application/files/ts-esm/vitest.config.e2e.ts +1 -2
  10. package/dist/lib/application/files/ts-esm/vitest.config.ts +1 -2
  11. package/dist/lib/application/schema.json +2 -5
  12. package/dist/lib/controller/controller.factory.js +5 -2
  13. package/dist/lib/decorator/files/js/__name__.decorator.js +2 -2
  14. package/dist/lib/decorator/files/ts/__name__.decorator.ts +2 -2
  15. package/dist/lib/gateway/gateway.factory.js +5 -2
  16. package/dist/lib/library/files/ts/tsconfig.lib.json +1 -1
  17. package/dist/lib/library/library.factory.js +6 -0
  18. package/dist/lib/library/schema.json +5 -0
  19. package/dist/lib/module/module.factory.js +5 -2
  20. package/dist/lib/provider/provider.factory.js +5 -2
  21. package/dist/lib/resolver/resolver.factory.js +5 -2
  22. package/dist/lib/resource/resource.factory.js +2 -1
  23. package/dist/lib/service/service.factory.js +5 -2
  24. package/dist/utils/module-import.declarator.js +2 -1
  25. package/dist/utils/module.declarator.d.ts +1 -0
  26. package/dist/utils/source-root.helpers.d.ts +1 -0
  27. package/dist/utils/source-root.helpers.js +14 -0
  28. package/package.json +1 -1
  29. package/dist/lib/client-app/angular/angular.factory.d.ts +0 -3
  30. package/dist/lib/client-app/angular/angular.factory.js +0 -100
  31. package/dist/lib/client-app/angular/files/angular.constants.ts +0 -4
  32. package/dist/lib/client-app/angular/files/angular.module.ts +0 -41
  33. package/dist/lib/client-app/angular/files/angular.providers.ts +0 -27
  34. package/dist/lib/client-app/angular/files/angular.utils.ts +0 -19
  35. package/dist/lib/client-app/angular/files/interfaces/angular-options.interface.ts +0 -87
  36. package/dist/lib/client-app/angular/files/loaders/abstract.loader.ts +0 -16
  37. package/dist/lib/client-app/angular/files/loaders/express.loader.ts +0 -25
  38. package/dist/lib/client-app/angular/files/loaders/fastify.loader.ts +0 -34
  39. package/dist/lib/client-app/angular/files/loaders/noop.loader.ts +0 -12
  40. package/dist/lib/client-app/angular/schema.json +0 -24
  41. /package/dist/lib/library/files/ts/src/{__name__.service.spec.ts → __name__.service.__specFileSuffix__.ts} +0 -0
@@ -6,11 +6,6 @@
6
6
  "description": "Create a Nest application.",
7
7
  "schema": "./lib/application/schema.json"
8
8
  },
9
- "angular-app": {
10
- "factory": "./lib/client-app/angular/angular.factory#main",
11
- "description": "Create a new Angular application.",
12
- "schema": "./lib/client-app/angular/schema.json"
13
- },
14
9
  "class": {
15
10
  "factory": "./lib/class/class.factory#main",
16
11
  "description": "Create a new class.",
@@ -1,10 +1,9 @@
1
- // @ts-check
2
1
  import eslint from '@eslint/js';
3
2
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
4
3
  import globals from 'globals';
5
4
  import tseslint from 'typescript-eslint';
6
5
 
7
- export default tseslint.config(
6
+ export default [
8
7
  {
9
8
  ignores: ['eslint.config.mjs'],
10
9
  },
@@ -32,4 +31,4 @@ export default tseslint.config(
32
31
  "prettier/prettier": ["error", { endOfLine: "auto" }],
33
32
  },
34
33
  },
35
- );
34
+ ];
@@ -27,8 +27,7 @@
27
27
  "rxjs": "^7.8.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@eslint/eslintrc": "^3.2.0",
31
- "@eslint/js": "^9.18.0",
30
+ "@eslint/js": "^10.0.0",
32
31
  "@nestjs/cli": "^11.0.0",
33
32
  "@nestjs/schematics": "^11.0.0",
34
33
  "@nestjs/testing": "^11.0.1",
@@ -36,9 +35,9 @@
36
35
  "@types/jest": "^30.0.0",
37
36
  "@types/node": "^24.0.0",
38
37
  "@types/supertest": "^7.0.0",
39
- "eslint": "^9.18.0",
38
+ "eslint": "^10.0.0",
40
39
  "eslint-config-prettier": "^10.0.1",
41
- "eslint-plugin-prettier": "^5.2.2",
40
+ "eslint-plugin-prettier": "^5.5.0",
42
41
  "globals": "^17.0.0",
43
42
  "jest": "^30.0.0",
44
43
  "prettier": "^3.4.2",
@@ -49,7 +48,7 @@
49
48
  "ts-node": "^10.9.2",
50
49
  "tsconfig-paths": "^4.2.0",
51
50
  "typescript": "^5.7.3",
52
- "typescript-eslint": "^8.20.0"
51
+ "typescript-eslint": "^8.30.0"
53
52
  },
54
53
  "jest": {
55
54
  "moduleFileExtensions": [
@@ -15,10 +15,7 @@
15
15
  "outDir": "./dist",
16
16
  "incremental": true,
17
17
  "skipLibCheck": true,
18
- "strictNullChecks": true,
19
- "forceConsistentCasingInFileNames": true,
20
- "noImplicitAny": <%= strict %>,
21
- "strictBindCallApply": <%= strict %>,
22
- "noFallthroughCasesInSwitch": <%= strict %>
18
+ "strict": <%= strict %>,
19
+ "strictPropertyInitialization": false
23
20
  }
24
21
  }
@@ -1,10 +1,9 @@
1
- // @ts-check
2
1
  import eslint from '@eslint/js';
3
2
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
4
3
  import globals from 'globals';
5
4
  import tseslint from 'typescript-eslint';
6
5
 
7
- export default tseslint.config(
6
+ export default [
8
7
  {
9
8
  ignores: ['eslint.config.mjs'],
10
9
  },
@@ -31,4 +30,4 @@ export default tseslint.config(
31
30
  "prettier/prettier": ["error", { endOfLine: "auto" }],
32
31
  },
33
32
  },
34
- );
33
+ ];
@@ -28,23 +28,22 @@
28
28
  "rxjs": "^7.8.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@eslint/eslintrc": "^3.2.0",
32
- "@eslint/js": "^9.18.0",
31
+ "@eslint/js": "^10.0.0",
33
32
  "@nestjs/cli": "^11.0.0",
34
33
  "@nestjs/schematics": "^11.0.0",
35
34
  "@nestjs/testing": "^11.0.1",
36
35
  "@types/express": "^5.0.0",
37
36
  "@types/node": "^24.0.0",
38
37
  "@types/supertest": "^7.0.0",
39
- "eslint": "^9.18.0",
38
+ "eslint": "^10.0.0",
40
39
  "eslint-config-prettier": "^10.0.1",
41
- "eslint-plugin-prettier": "^5.2.2",
40
+ "eslint-plugin-prettier": "^5.5.0",
42
41
  "globals": "^17.0.0",
43
42
  "prettier": "^3.4.2",
44
43
  "source-map-support": "^0.5.21",
45
44
  "supertest": "^7.0.0",
46
45
  "typescript": "^5.7.3",
47
- "typescript-eslint": "^8.20.0",
46
+ "typescript-eslint": "^8.30.0",
48
47
  "vitest": "^4.0.0-beta.1"
49
48
  }
50
49
  }
@@ -5,4 +5,4 @@ async function bootstrap() {
5
5
  const app = await NestFactory.create(AppModule);
6
6
  await app.listen(process.env.PORT ?? 3000);
7
7
  }
8
- bootstrap();
8
+ await bootstrap();
@@ -15,10 +15,8 @@
15
15
  "outDir": "./dist",
16
16
  "incremental": true,
17
17
  "skipLibCheck": true,
18
- "strictNullChecks": true,
19
- "forceConsistentCasingInFileNames": true,
20
- "noImplicitAny": <%= strict %>,
21
- "strictBindCallApply": <%= strict %>,
22
- "noFallthroughCasesInSwitch": <%= strict %>
18
+ "strict": <%= strict %>,
19
+ "strictPropertyInitialization": false,
20
+ "types": ["vitest/globals", "node"]
23
21
  }
24
22
  }
@@ -3,8 +3,7 @@ import { defineConfig } from 'vitest/config';
3
3
  export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
- rootDir: '.',
7
- testRegex: '.e2e-spec.ts$',
8
6
  root: './',
7
+ include: ['**/*.e2e-spec.ts'],
9
8
  },
10
9
  });
@@ -3,8 +3,7 @@ import { defineConfig } from 'vitest/config';
3
3
  export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
- rootDir: '.',
7
- testRegex: '.*\.spec\.ts$',
8
6
  root: './',
7
+ include: ['**/*.spec.ts'],
9
8
  },
10
9
  });
@@ -5,10 +5,7 @@
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "name": {
8
- "oneOf": [
9
- { "type": "string" },
10
- { "type": "number" }
11
- ],
8
+ "oneOf": [{ "type": "string" }, { "type": "number" }],
12
9
  "description": "The name of the application.",
13
10
  "$default": {
14
11
  "$source": "argv",
@@ -33,7 +30,7 @@
33
30
  "strict": {
34
31
  "type": "boolean",
35
32
  "description": "With TypeScript strict mode.",
36
- "default": false
33
+ "default": true
37
34
  },
38
35
  "version": {
39
36
  "type": "string",
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  import { DEFAULT_LANGUAGE } from '../defaults.js';
9
9
  const ELEMENT_METADATA = 'controllers';
10
10
  const ELEMENT_TYPE = 'controller';
@@ -63,7 +63,10 @@ function addDeclarationToModule(options) {
63
63
  }
64
64
  const content = tree.read(options.module).toString();
65
65
  const declarator = new ModuleDeclarator();
66
- tree.overwrite(options.module, declarator.declare(content, options));
66
+ tree.overwrite(options.module, declarator.declare(content, {
67
+ ...options,
68
+ isEsm: isEsmProject(tree),
69
+ }));
67
70
  return tree;
68
71
  };
69
72
  }
@@ -1,3 +1,3 @@
1
- import { SetMetadata } from '@nestjs/common';
1
+ import { Reflector } from '@nestjs/core';
2
2
 
3
- export const <%= classify(name) %> = (...args) => SetMetadata('<%= name %>', args);
3
+ export const <%= classify(name) %> = Reflector.createDecorator();
@@ -1,3 +1,3 @@
1
- import { SetMetadata } from '@nestjs/common';
1
+ import { Reflector } from '@nestjs/core';
2
2
 
3
- export const <%= classify(name) %> = (...args: string[]) => SetMetadata('<%= name %>', args);
3
+ export const <%= classify(name) %> = Reflector.createDecorator<string[]>();
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  export function main(options) {
9
9
  options = transform(options);
10
10
  return (tree, context) => {
@@ -63,7 +63,10 @@ function addDeclarationToModule(options) {
63
63
  }
64
64
  const content = tree.read(options.module).toString();
65
65
  const declarator = new ModuleDeclarator();
66
- tree.overwrite(options.module, declarator.declare(content, options));
66
+ tree.overwrite(options.module, declarator.declare(content, {
67
+ ...options,
68
+ isEsm: isEsmProject(tree),
69
+ }));
67
70
  return tree;
68
71
  };
69
72
  }
@@ -6,5 +6,5 @@
6
6
  "outDir": "../../dist/libs/<%= name %>"
7
7
  },
8
8
  "include": ["src/**/*"],
9
- "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9
+ "exclude": ["node_modules", "dist", "test", "**/*<%= specFileSuffix %>.ts"]
10
10
  }
@@ -46,6 +46,7 @@ function transform(options) {
46
46
  ? join(normalize(defaultSourceRoot), target.path)
47
47
  : normalize(defaultSourceRoot);
48
48
  target.prefix = target.prefix || getDefaultLibraryPrefix();
49
+ target.specFileSuffix = normalizeToKebabOrSnakeCase(target.specFileSuffix ?? 'spec');
49
50
  return target;
50
51
  }
51
52
  function updatePackageJson(options) {
@@ -145,6 +146,11 @@ function updateTsConfig(packageName, _packagePrefix, root) {
145
146
  }
146
147
  delete tsconfig.compilerOptions.baseUrl;
147
148
  delete tsconfig.compilerOptions.paths;
149
+ if (!tsconfig.files) {
150
+ tsconfig.files = [];
151
+ }
152
+ delete tsconfig.include;
153
+ delete tsconfig.exclude;
148
154
  if (!tsconfig.references) {
149
155
  tsconfig.references = [];
150
156
  }
@@ -31,6 +31,11 @@
31
31
  "type": "string",
32
32
  "format": "path",
33
33
  "description": "The libraries root directory."
34
+ },
35
+ "specFileSuffix": {
36
+ "type": "string",
37
+ "default": "spec",
38
+ "description": "Specifies the file suffix of spec files."
34
39
  }
35
40
  },
36
41
  "required": ["name", "prefix"]
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  export function main(options) {
9
9
  options = transform(options);
10
10
  return (tree, context) => {
@@ -51,7 +51,10 @@ function addDeclarationToModule(options) {
51
51
  }
52
52
  const content = tree.read(options.module).toString();
53
53
  const declarator = new ModuleDeclarator();
54
- tree.overwrite(options.module, declarator.declare(content, options));
54
+ tree.overwrite(options.module, declarator.declare(content, {
55
+ ...options,
56
+ isEsm: isEsmProject(tree),
57
+ }));
55
58
  return tree;
56
59
  };
57
60
  }
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  export function main(options) {
9
9
  options = transform(options);
10
10
  return (tree, context) => {
@@ -67,7 +67,10 @@ function addDeclarationToModule(options) {
67
67
  }
68
68
  const content = tree.read(options.module).toString();
69
69
  const declarator = new ModuleDeclarator();
70
- tree.overwrite(options.module, declarator.declare(content, options));
70
+ tree.overwrite(options.module, declarator.declare(content, {
71
+ ...options,
72
+ isEsm: isEsmProject(tree),
73
+ }));
71
74
  return tree;
72
75
  };
73
76
  }
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  export function main(options) {
9
9
  options = transform(options);
10
10
  return (tree, context) => {
@@ -62,7 +62,10 @@ function addDeclarationToModule(options) {
62
62
  }
63
63
  const content = tree.read(options.module).toString();
64
64
  const declarator = new ModuleDeclarator();
65
- tree.overwrite(options.module, declarator.declare(content, options));
65
+ tree.overwrite(options.module, declarator.declare(content, {
66
+ ...options,
67
+ isEsm: isEsmProject(tree),
68
+ }));
66
69
  return tree;
67
70
  };
68
71
  }
@@ -7,7 +7,7 @@ import { ModuleDeclarator, ModuleFinder, } from '../../index.js';
7
7
  import { addPackageJsonDependency, getPackageJsonDependency, NodeDependencyType, } from '../../utils/dependencies.utils.js';
8
8
  import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
9
9
  import { NameParser } from '../../utils/name.parser.js';
10
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
10
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
11
11
  export function main(options) {
12
12
  options = transform(options);
13
13
  return (tree, context) => {
@@ -109,6 +109,7 @@ function addDeclarationToModule(options) {
109
109
  tree.overwrite(options.module, declarator.declare(content, {
110
110
  ...options,
111
111
  type: 'module',
112
+ isEsm: isEsmProject(tree),
112
113
  }));
113
114
  return tree;
114
115
  };
@@ -4,7 +4,7 @@ import { normalizeToKebabOrSnakeCase } from '../../utils/formatting.js';
4
4
  import { ModuleDeclarator, } from '../../utils/module.declarator.js';
5
5
  import { ModuleFinder } from '../../utils/module.finder.js';
6
6
  import { NameParser } from '../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../utils/source-root.helpers.js';
7
+ import { isEsmProject, mergeSourceRoot, } from '../../utils/source-root.helpers.js';
8
8
  function isNullOrUndefined(value) {
9
9
  return value === null || value === undefined;
10
10
  }
@@ -65,7 +65,10 @@ function addDeclarationToModule(options) {
65
65
  }
66
66
  const content = tree.read(options.module).toString();
67
67
  const declarator = new ModuleDeclarator();
68
- tree.overwrite(options.module, declarator.declare(content, options));
68
+ tree.overwrite(options.module, declarator.declare(content, {
69
+ ...options,
70
+ isEsm: isEsmProject(tree),
71
+ }));
69
72
  return tree;
70
73
  };
71
74
  }
@@ -31,6 +31,7 @@ export class ModuleImportDeclarator {
31
31
  else {
32
32
  importModulePath = normalize(`/${options.path}/${options.name}`);
33
33
  }
34
- return this.solver.relative(options.module, importModulePath);
34
+ const relativePath = this.solver.relative(options.module, importModulePath);
35
+ return options.isEsm ? `${relativePath}.js` : relativePath;
35
36
  }
36
37
  }
@@ -9,6 +9,7 @@ export interface DeclarationOptions {
9
9
  path: Path;
10
10
  module: Path;
11
11
  symbol?: string;
12
+ isEsm?: boolean;
12
13
  staticOptions?: {
13
14
  name: string;
14
15
  value: Record<string, any>;
@@ -1,4 +1,5 @@
1
1
  import { Rule, Tree } from '@angular-devkit/schematics';
2
+ export declare function isEsmProject(host: Tree): boolean;
2
3
  export declare function isInRootDirectory(host: Tree, extraFiles?: string[]): boolean;
3
4
  export declare function mergeSourceRoot<T extends {
4
5
  sourceRoot?: string;
@@ -1,5 +1,19 @@
1
1
  import { join, normalize } from '@angular-devkit/core';
2
2
  import { DEFAULT_PATH_NAME } from '../lib/defaults.js';
3
+ export function isEsmProject(host) {
4
+ const packageJsonPath = 'package.json';
5
+ const buffer = host.read(packageJsonPath);
6
+ if (!buffer) {
7
+ return false;
8
+ }
9
+ try {
10
+ const packageJson = JSON.parse(buffer.toString());
11
+ return packageJson.type === 'module';
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
3
17
  export function isInRootDirectory(host, extraFiles = []) {
4
18
  const files = ['nest-cli.json', 'nest.json'].concat(extraFiles || []);
5
19
  return files.map((file) => host.exists(file)).some((isPresent) => isPresent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/schematics",
3
- "version": "12.0.0-alpha.2",
3
+ "version": "12.0.0-alpha.4",
4
4
  "description": "Nest - modern, fast, powerful node.js web framework (@schematics)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,3 +0,0 @@
1
- import { Rule } from '@angular-devkit/schematics';
2
- import type { AngularOptions } from './angular.schema.js';
3
- export declare function main(options: AngularOptions): Rule;
@@ -1,100 +0,0 @@
1
- import { strings } from '@angular-devkit/core';
2
- import { apply, branchAndMerge, chain, externalSchematic, mergeWith, move, noop, template, url, } from '@angular-devkit/schematics';
3
- import { join } from 'path';
4
- import { ModuleDeclarator, } from '../../../utils/module.declarator.js';
5
- import { ModuleFinder } from '../../../utils/module.finder.js';
6
- import { NameParser } from '../../../utils/name.parser.js';
7
- import { mergeSourceRoot } from '../../../utils/source-root.helpers.js';
8
- export function main(options) {
9
- options = transform(options);
10
- return (tree, context) => {
11
- return branchAndMerge(chain([
12
- createAngularApplication(options),
13
- mergeSourceRoot(options),
14
- addDeclarationToModule(options),
15
- addGlobalPrefix(),
16
- mergeWith(generate(options)),
17
- ]))(tree, context);
18
- };
19
- }
20
- function transform(source) {
21
- const target = Object.assign({}, source);
22
- target.directory = target.name ? strings.dasherize(target.name) : 'client';
23
- target.name = 'Angular';
24
- target.metadata = 'imports';
25
- target.type = 'module';
26
- const location = new NameParser().parse(target);
27
- target.name = strings.dasherize(location.name);
28
- target.path = join(strings.dasherize(location.path), target.name);
29
- return target;
30
- }
31
- function generate(options) {
32
- return (context) => apply(url('./files'), [
33
- template({
34
- ...strings,
35
- ...options,
36
- }),
37
- move(options.path),
38
- ])(context);
39
- }
40
- function createAngularApplication(options) {
41
- if (!options.initApp) {
42
- return noop();
43
- }
44
- return externalSchematic('@schematics/angular', 'ng-new', {
45
- name: options.directory,
46
- version: '8.0.0',
47
- });
48
- }
49
- function addDeclarationToModule(options) {
50
- return (tree) => {
51
- options.module = new ModuleFinder(tree).find({
52
- name: options.name,
53
- path: options.path,
54
- });
55
- if (!options.module) {
56
- return tree;
57
- }
58
- const content = tree.read(options.module).toString();
59
- const declarator = new ModuleDeclarator();
60
- const rootPath = `${options.directory}/dist/${options.directory}`;
61
- const staticOptions = {
62
- name: 'forRoot',
63
- value: {
64
- rootPath,
65
- },
66
- };
67
- const declarationOptions = {
68
- ...options,
69
- staticOptions,
70
- };
71
- tree.overwrite(options.module, declarator.declare(content, declarationOptions));
72
- return tree;
73
- };
74
- }
75
- function addGlobalPrefix() {
76
- return (tree) => {
77
- const mainFilePath = 'src/main.ts';
78
- const fileRef = tree.get(mainFilePath);
79
- if (!fileRef) {
80
- return tree;
81
- }
82
- const ts = require('ts-morph');
83
- const tsProject = new ts.Project({
84
- manipulationSettings: {
85
- indentationText: ts.IndentationText.TwoSpaces,
86
- },
87
- });
88
- const tsFile = tsProject.addSourceFileAtPath(mainFilePath);
89
- const bootstrapFunction = tsFile.getFunction('bootstrap');
90
- const listenStatement = bootstrapFunction.getStatement((node) => node.getText().includes('listen'));
91
- const setPrefixStatement = bootstrapFunction.getStatement((node) => node.getText().includes('setGlobalPrefix'));
92
- if (!listenStatement || setPrefixStatement) {
93
- return tree;
94
- }
95
- const listenExprIndex = listenStatement.getChildIndex();
96
- bootstrapFunction.insertStatements(listenExprIndex, `app.setGlobalPrefix('api');`);
97
- tree.overwrite(mainFilePath, tsFile.getFullText());
98
- return tree;
99
- };
100
- }
@@ -1,4 +0,0 @@
1
- export const ANGULAR_MODULE_OPTIONS = 'ANGULAR_MODULE_OPTIONS';
2
-
3
- export const DEFAULT_ROOT_PATH = 'client/dist';
4
- export const DEFAULT_RENDER_PATH = '*';
@@ -1,41 +0,0 @@
1
- import { DynamicModule, Inject, Module, OnModuleInit } from '@nestjs/common';
2
- import { HttpAdapterHost } from '@nestjs/core';
3
- import {
4
- ANGULAR_MODULE_OPTIONS,
5
- DEFAULT_RENDER_PATH,
6
- DEFAULT_ROOT_PATH,
7
- } from './angular.constants';
8
- import { angularProviders } from './angular.providers';
9
- import { AngularModuleOptions } from './interfaces/angular-options.interface';
10
- import { AbstractLoader } from './loaders/abstract.loader';
11
-
12
- @Module({
13
- providers: [...angularProviders],
14
- })
15
- export class AngularModule implements OnModuleInit {
16
- constructor(
17
- @Inject(ANGULAR_MODULE_OPTIONS)
18
- private readonly ngOptions: AngularModuleOptions,
19
- private readonly loader: AbstractLoader,
20
- private readonly httpAdapterHost: HttpAdapterHost,
21
- ) {}
22
-
23
- public static forRoot(options: AngularModuleOptions = {}): DynamicModule {
24
- options.rootPath = options.rootPath || DEFAULT_ROOT_PATH;
25
- options.renderPath = options.renderPath || DEFAULT_RENDER_PATH;
26
- return {
27
- module: AngularModule,
28
- providers: [
29
- {
30
- provide: ANGULAR_MODULE_OPTIONS,
31
- useValue: options,
32
- },
33
- ],
34
- };
35
- }
36
-
37
- public async onModuleInit() {
38
- const httpAdapter = this.httpAdapterHost.httpAdapter;
39
- this.loader.register(httpAdapter, this.ngOptions);
40
- }
41
- }
@@ -1,27 +0,0 @@
1
- import { Provider } from '@nestjs/common';
2
- import { HttpAdapterHost } from '@nestjs/core';
3
- import { AbstractLoader } from './loaders/abstract.loader';
4
- import { ExpressLoader } from './loaders/express.loader';
5
- import { FastifyLoader } from './loaders/fastify.loader';
6
- import { NoopLoader } from './loaders/noop.loader';
7
-
8
- export const angularProviders: Provider[] = [
9
- {
10
- provide: AbstractLoader,
11
- useFactory: (httpAdapterHost: HttpAdapterHost) => {
12
- if (!httpAdapterHost) {
13
- return new NoopLoader();
14
- }
15
- const httpAdapter = httpAdapterHost.httpAdapter;
16
- if (
17
- httpAdapter &&
18
- httpAdapter.constructor &&
19
- httpAdapter.constructor.name === 'FastifyAdapter'
20
- ) {
21
- return new FastifyLoader();
22
- }
23
- return new ExpressLoader();
24
- },
25
- inject: [HttpAdapterHost],
26
- },
27
- ];
@@ -1,19 +0,0 @@
1
- import { Logger } from '@nestjs/common';
2
-
3
- const MISSING_REQUIRED_DEPENDENCY = (name: string, reason: string) =>
4
- `The "${name}" package is missing. Please, make sure to install this library ($ npm install ${name}) to take advantage of ${reason}.`;
5
-
6
- const logger = new Logger('PackageLoader');
7
-
8
- export function loadPackage<T = any>(
9
- packageName: string,
10
- context: string,
11
- loaderFn?: () => T,
12
- ): T {
13
- try {
14
- return loaderFn ? loaderFn() : require(packageName);
15
- } catch (e) {
16
- logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
17
- process.exit(1);
18
- }
19
- }
@@ -1,87 +0,0 @@
1
- export interface AngularModuleOptions {
2
- /**
3
- * Static files root directory.
4
- * Default: "client/dist"
5
- */
6
- rootPath?: string;
7
- /**
8
- * Path to render Angular app.
9
- * Default: * (wildcard - all paths)
10
- */
11
- renderPath?: string;
12
- /**
13
- * Serve static options (static files)
14
- * Passed down to the underlying either `express.static` or `fastify-static.send`
15
- */
16
- serveStaticOptions?: {
17
- /**
18
- * Enable or disable setting Cache-Control response header, defaults to true.
19
- * Disabling this will ignore the immutable and maxAge options.
20
- */
21
- cacheControl?: boolean;
22
-
23
- /**
24
- * Set how "dotfiles" are treated when encountered. A dotfile is a file or directory that begins with a dot (".").
25
- * Note this check is done on the path itself without checking if the path actually exists on the disk.
26
- * If root is specified, only the dotfiles above the root are checked
27
- * (i.e. the root itself can be within a dotfile when when set to "deny").
28
- * The default value is 'ignore'.
29
- * 'allow' No special treatment for dotfiles
30
- * 'deny' Send a 403 for any request for a dotfile
31
- * 'ignore' Pretend like the dotfile does not exist and call next()
32
- */
33
- dotfiles?: string;
34
-
35
- /**
36
- * Enable or disable etag generation, defaults to true.
37
- */
38
- etag?: boolean;
39
-
40
- /**
41
- * Set file extension fallbacks. When set, if a file is not found, the given extensions
42
- * will be added to the file name and search for.
43
- * The first that exists will be served. Example: ['html', 'htm'].
44
- * The default value is false.
45
- */
46
- extensions?: string[];
47
-
48
- /**
49
- * Enable or disable the immutable directive in the Cache-Control response header.
50
- * If enabled, the maxAge option should also be specified to enable caching.
51
- * The immutable directive will prevent supported clients from making conditional
52
- * requests during the life of the maxAge option to check if the file has changed.
53
- */
54
- immutable?: boolean;
55
-
56
- /**
57
- * By default this module will send "index.html" files in response to a request on a directory.
58
- * To disable this set false or to supply a new index pass a string or an array in preferred order.
59
- */
60
- index?: boolean | string | string[];
61
-
62
- /**
63
- * Enable or disable Last-Modified header, defaults to true. Uses the file system's last modified value.
64
- */
65
- lastModified?: boolean;
66
-
67
- /**
68
- * Provide a max-age in milliseconds for http caching, defaults to 0.
69
- * This can also be a string accepted by the ms module.
70
- */
71
- maxAge?: number | string;
72
-
73
- /**
74
- * Redirect to trailing "/" when the pathname is a dir. Defaults to true.
75
- */
76
- redirect?: boolean;
77
-
78
- /**
79
- * Function to set custom headers on response. Alterations to the headers need to occur synchronously.
80
- * The function is called as fn(res, path, stat), where the arguments are:
81
- * res the response object
82
- * path the file path that is being sent
83
- * stat the stat object of the file that is being sent
84
- */
85
- setHeaders?: (res: any, path: string, stat: any) => any;
86
- };
87
- }
@@ -1,16 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { AbstractHttpAdapter } from '@nestjs/core';
3
- import { join } from 'path';
4
- import { AngularModuleOptions } from '../interfaces/angular-options.interface';
5
-
6
- @Injectable()
7
- export abstract class AbstractLoader {
8
- public abstract register(
9
- httpAdapter: AbstractHttpAdapter,
10
- options: AngularModuleOptions,
11
- );
12
-
13
- public getIndexFilePath(clientPath: string): string {
14
- return join(clientPath, 'index.html');
15
- }
16
- }
@@ -1,25 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { AbstractHttpAdapter } from '@nestjs/core';
3
- import { loadPackage } from '../angular.utils';
4
- import { AngularModuleOptions } from '../interfaces/angular-options.interface';
5
- import { AbstractLoader } from './abstract.loader';
6
-
7
- @Injectable()
8
- export class ExpressLoader extends AbstractLoader {
9
- public register(
10
- httpAdapter: AbstractHttpAdapter,
11
- options: AngularModuleOptions,
12
- ) {
13
- const app = httpAdapter.getInstance();
14
- const express = loadPackage('express', 'AngularModule', () =>
15
- require('express'),
16
- );
17
- const clientPath = options.rootPath;
18
- const indexFilePath = this.getIndexFilePath(clientPath);
19
-
20
- app.use(express.static(clientPath, options.serveStaticOptions));
21
- app.get(options.renderPath, (req: any, res: any) =>
22
- res.sendFile(indexFilePath),
23
- );
24
- }
25
- }
@@ -1,34 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { AbstractHttpAdapter } from '@nestjs/core';
3
- import * as fs from 'fs';
4
- import { loadPackage } from '../angular.utils';
5
- import { AngularModuleOptions } from '../interfaces/angular-options.interface';
6
- import { AbstractLoader } from './abstract.loader';
7
-
8
- @Injectable()
9
- export class FastifyLoader extends AbstractLoader {
10
- public register(
11
- httpAdapter: AbstractHttpAdapter,
12
- options: AngularModuleOptions,
13
- ) {
14
- const app = httpAdapter.getInstance();
15
- const fastifyStatic = loadPackage('fastify-static', 'AngularModule', () =>
16
- require('fastify-static'),
17
- );
18
- const { setHeaders, redirect, ...send } =
19
- options.serveStaticOptions || ({} as any);
20
- const clientPath = options.rootPath;
21
- const indexFilePath = this.getIndexFilePath(clientPath);
22
-
23
- app.register(fastifyStatic, {
24
- root: clientPath,
25
- setHeaders,
26
- redirect,
27
- send,
28
- });
29
- app.get(options.renderPath, (req: any, res: any) => {
30
- const stream = fs.createReadStream(indexFilePath);
31
- res.type('text/html').send(stream);
32
- });
33
- }
34
- }
@@ -1,12 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { AbstractHttpAdapter } from '@nestjs/core';
3
- import { AngularModuleOptions } from '../interfaces/angular-options.interface';
4
- import { AbstractLoader } from './abstract.loader';
5
-
6
- @Injectable()
7
- export class NoopLoader extends AbstractLoader {
8
- public register(
9
- httpAdapter: AbstractHttpAdapter,
10
- options: AngularModuleOptions,
11
- ) {}
12
- }
@@ -1,24 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/schema",
3
- "$id": "SchematicsNestModule",
4
- "title": "Nest Module Options Schema",
5
- "type": "object",
6
- "properties": {
7
- "initApp": {
8
- "type": "boolean",
9
- "description": "Flag to skip the angular application generation.",
10
- "default": false,
11
- "x-prompt": "Would you like to initialize Angular application?"
12
- },
13
- "name": {
14
- "type": "string",
15
- "description": "The name of the application.",
16
- "$default": {
17
- "$source": "argv",
18
- "index": 0
19
- },
20
- "x-prompt": "What name would you like to (or do you) use for Angular application?"
21
- }
22
- },
23
- "required": ["name"]
24
- }