@aligent/nx-cdk 0.7.0 → 0.8.1

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.
package/README.md CHANGED
@@ -59,13 +59,17 @@ The service generator creates a new CDK service within the `services/` folder of
59
59
 
60
60
  ```bash
61
61
  yarn nx g @aligent/nx-cdk:service <service-name>
62
+
63
+ # Or simply (since the generator name is unique):
64
+ yarn nx g service <service-name>
62
65
  ```
63
66
 
64
67
  #### Options
65
68
 
66
- | Option | Type | Required | Description |
67
- | ------ | ------ | -------- | ------------------------------------------------------------------------- |
68
- | `name` | string | Yes | The name of the service (cannot contain 'Stack' or 'Service' in the name) |
69
+ | Option | Type | Required | Default | Description |
70
+ | --------- | ------- | -------- | ------- | ------------------------------------------------------------------------- |
71
+ | `name` | string | Yes | - | The name of the service (cannot contain 'Stack' or 'Service' in the name) |
72
+ | `example` | boolean | No | `false` | Generate example code with sample resources |
69
73
 
70
74
  #### What it creates
71
75
 
@@ -89,11 +93,56 @@ The service generator creates a new service in `services/<service-name>/` with:
89
93
  #### Example
90
94
 
91
95
  ```bash
92
- # Create a new service named "user-management"
96
+ # Create a new service named "user-management" (short form)
97
+ yarn nx g service user-management
98
+
99
+ # Or with the full plugin prefix
93
100
  yarn nx g @aligent/nx-cdk:service user-management
101
+ ```
102
+
103
+ ### Remove Service Generator
104
+
105
+ The remove-service generator cleanly removes a service and all of its references from the project. It reverses the changes made by the service generator, ensuring no dangling imports or references are left behind.
106
+
107
+ #### Usage
108
+
109
+ ```bash
110
+ yarn nx g @aligent/nx-cdk:remove-service <service-name>
111
+
112
+ # Or simply (since the generator name is unique):
113
+ yarn nx g remove-service <service-name>
114
+ ```
115
+
116
+ #### Options
117
+
118
+ | Option | Type | Required | Default | Description |
119
+ | ------------- | ------- | -------- | ------- | ----------------------------------------------- |
120
+ | `name` | string | Yes | - | The name of the service to remove |
121
+ | `forceRemove` | boolean | No | `false` | Skip dependency check when removing the project |
122
+
123
+ #### What it does
124
+
125
+ The remove generator performs the following cleanup:
126
+
127
+ - **Application updates**:
128
+ - Removes the service's import declaration from `application/lib/service-stacks.ts`
129
+ - Removes the stack instantiation from the `ApplicationStage` constructor
130
+
131
+ - **Root updates**:
132
+ - Removes the service from the root `tsconfig.json` references
133
+ - Removes the service from the root `package.json` workspaces (if present)
134
+
135
+ - **Service files**:
136
+ - Deletes the entire `services/<service-name>/` directory
137
+
138
+ #### Example
139
+
140
+ ```bash
141
+ # Remove the "user-management" service (short form)
142
+ yarn nx g remove-service user-management
94
143
 
95
- # Create a new service named "payment-processing"
96
- yarn nx g @aligent/nx-cdk:service payment-processing
144
+ # Or with the full plugin prefix
145
+ yarn nx g @aligent/nx-cdk:remove-service user-management
97
146
  ```
98
147
 
99
148
  ## Project Structure
package/generators.json CHANGED
@@ -10,6 +10,11 @@
10
10
  "factory": "./src/generators/service/generator",
11
11
  "schema": "./src/generators/service/schema.json",
12
12
  "description": "Generate a new service"
13
+ },
14
+ "remove-service": {
15
+ "factory": "./src/generators/remove-service/generator",
16
+ "schema": "./src/generators/remove-service/schema.json",
17
+ "description": "Remove a service and clean up its references"
13
18
  }
14
19
  }
15
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aligent/nx-cdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "commonjs",
5
5
  "main": "./src/index.js",
6
6
  "typings": "./src/index.d.ts",
@@ -0,0 +1,5 @@
1
+ export type ProjectType = 'application' | 'service';
2
+ export declare const MAIN_APPLICATION_FOLDER = "application";
3
+ export declare const MAIN_APPLICATION_NAME = "application";
4
+ export declare const SERVICES_FOLDER = "services";
5
+ export declare const SERVICES_SCOPE = "@services";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SERVICES_SCOPE = exports.SERVICES_FOLDER = exports.MAIN_APPLICATION_NAME = exports.MAIN_APPLICATION_FOLDER = void 0;
4
+ exports.MAIN_APPLICATION_FOLDER = 'application';
5
+ exports.MAIN_APPLICATION_NAME = 'application';
6
+ exports.SERVICES_FOLDER = 'services';
7
+ exports.SERVICES_SCOPE = '@services';
@@ -24,7 +24,7 @@
24
24
  "@aligent/cdk-aspects": "^0.5.5",
25
25
  "@aligent/cdk-nodejs-function-from-entry": "^0.2.1",
26
26
  "@aligent/cdk-step-function-from-file": "^0.5.1",
27
- "@aligent/nx-openapi": "^2.2.1",
27
+ "@aligent/nx-openapi": "^2.1.1",
28
28
  "@aligent/ts-code-standards": "^4.2.1",
29
29
  "@nx/eslint": "22.1.3",
30
30
  "@nx/eslint-plugin": "22.1.3",
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NX_JSON = void 0;
4
+ const constants_1 = require("../../constants");
4
5
  exports.NX_JSON = {
5
6
  $schema: './node_modules/nx/schemas/nx-schema.json',
6
7
  defaultBase: 'origin/staging',
@@ -37,7 +38,11 @@ exports.NX_JSON = {
37
38
  configurations: { coverage: { coverage: true } },
38
39
  },
39
40
  typecheck: { cache: true, inputs: ['default', '^production'] },
40
- cdk: { dependsOn: [{ target: 'build', params: 'forward', projects: '@services/*' }] },
41
- pg: { dependsOn: [{ target: 'build', params: 'forward', projects: '@services/*' }] },
41
+ cdk: {
42
+ dependsOn: [{ target: 'build', params: 'forward', projects: `${constants_1.SERVICES_SCOPE}/*` }],
43
+ },
44
+ pg: {
45
+ dependsOn: [{ target: 'build', params: 'forward', projects: `${constants_1.SERVICES_SCOPE}/*` }],
46
+ },
42
47
  },
43
48
  };
@@ -1,4 +1,5 @@
1
1
  import { Tree } from '@nx/devkit';
2
+ import { ProjectType } from '../constants';
2
3
  interface PackageJsonInput {
3
4
  name: string;
4
5
  projectName: string;
@@ -20,7 +21,7 @@ export declare function constructPackageJsonFile(input: PackageJsonInput): {
20
21
  node?: never;
21
22
  };
22
23
  };
23
- export declare function constructProjectTsConfigFiles(type: 'application' | 'service'): {
24
+ export declare function constructProjectTsConfigFiles(type: ProjectType): {
24
25
  tsConfig: {
25
26
  extends: string;
26
27
  compilerOptions?: Record<string, unknown>;
@@ -61,6 +62,25 @@ export declare function getGeneratorVersion(): string;
61
62
  * @throws {Error} If the ApplicationStage constructor cannot be found in service-stacks.ts
62
63
  */
63
64
  export declare function addServiceStackToMainApplication(tree: Tree, service: Service, projectName: string): void;
65
+ /**
66
+ * Removes a service stack registration from the main CDK application's ApplicationStage.
67
+ *
68
+ * This function modifies the service-stacks.ts file by:
69
+ * 1. Removing import statements that reference the service's module specifier
70
+ * 2. Removing statements in the ApplicationStage constructor that reference the service's stack class
71
+ *
72
+ * @param tree - The Nx virtual file system tree
73
+ * @param serviceName - The name of the service (e.g., "companies")
74
+ * @param projectName - The name of the main application project
75
+ */
76
+ export declare function removeServiceFromMainApplication(tree: Tree, serviceName: string, projectName: string): void;
77
+ /**
78
+ * Removes a project reference from the root tsconfig.json.
79
+ *
80
+ * @param tree - The Nx virtual file system tree
81
+ * @param referencePath - The path to remove from the references array
82
+ */
83
+ export declare function removeTsConfigReference(tree: Tree, referencePath: string): void;
64
84
  /**
65
85
  * Splits a kebab-case name into an array of capitalized parts.
66
86
  *
@@ -4,11 +4,14 @@ exports.constructPackageJsonFile = constructPackageJsonFile;
4
4
  exports.constructProjectTsConfigFiles = constructProjectTsConfigFiles;
5
5
  exports.getGeneratorVersion = getGeneratorVersion;
6
6
  exports.addServiceStackToMainApplication = addServiceStackToMainApplication;
7
+ exports.removeServiceFromMainApplication = removeServiceFromMainApplication;
8
+ exports.removeTsConfigReference = removeTsConfigReference;
7
9
  exports.splitInputName = splitInputName;
8
10
  /* v8 ignore start */
9
11
  const devkit_1 = require("@nx/devkit");
10
12
  const path_1 = require("path");
11
13
  const ts_morph_1 = require("ts-morph");
14
+ const constants_1 = require("../constants");
12
15
  const tsConfigs_1 = require("./configs/tsConfigs");
13
16
  /**
14
17
  * Reads the base package.json configuration from a JSON file.
@@ -101,7 +104,7 @@ function addServiceStackToMainApplication(tree, service, projectName) {
101
104
  throw new Error('Unable to find main application stage constructor');
102
105
  }
103
106
  stackSource.addImportDeclaration({
104
- moduleSpecifier: `@services/${service.name}`,
107
+ moduleSpecifier: `${constants_1.SERVICES_SCOPE}/${service.name}`,
105
108
  namedImports: [service.constant, service.stack],
106
109
  });
107
110
  const sharedInfra = stageConstructor.getVariableStatement('sharedInfra');
@@ -109,6 +112,69 @@ function addServiceStackToMainApplication(tree, service, projectName) {
109
112
  stageConstructor.addStatements(`new ${service.stack}(this, ${service.constant}.NAME, { ...props, ${sharedPropsStatement} description: ${service.constant}.DESCRIPTION });`);
110
113
  tree.write(stacksRelativePath, stackSource.getFullText());
111
114
  }
115
+ /**
116
+ * Removes a service stack registration from the main CDK application's ApplicationStage.
117
+ *
118
+ * This function modifies the service-stacks.ts file by:
119
+ * 1. Removing import statements that reference the service's module specifier
120
+ * 2. Removing statements in the ApplicationStage constructor that reference the service's stack class
121
+ *
122
+ * @param tree - The Nx virtual file system tree
123
+ * @param serviceName - The name of the service (e.g., "companies")
124
+ * @param projectName - The name of the main application project
125
+ */
126
+ function removeServiceFromMainApplication(tree, serviceName, projectName) {
127
+ const application = (0, devkit_1.readProjectConfiguration)(tree, projectName);
128
+ if (application.root.includes('..')) {
129
+ throw new Error('Invalid application root path');
130
+ }
131
+ const stacksRelativePath = (0, path_1.join)(application.root, 'lib/service-stacks.ts');
132
+ if (!tree.exists(stacksRelativePath)) {
133
+ console.log('Service Stacks does not exist, skipping service stacks cleanup.');
134
+ return;
135
+ }
136
+ const content = tree.read(stacksRelativePath, 'utf-8');
137
+ if (content === null) {
138
+ throw new Error(`Failed to read file: ${stacksRelativePath}`);
139
+ }
140
+ const fs = new ts_morph_1.InMemoryFileSystemHost();
141
+ fs.writeFileSync(stacksRelativePath, content);
142
+ const project = new ts_morph_1.Project({ fileSystem: fs });
143
+ const stackSource = project.addSourceFileAtPath(stacksRelativePath);
144
+ const imports = stackSource.getImportDeclarations();
145
+ for (const importDecl of imports) {
146
+ if (importDecl.getModuleSpecifierValue() === `${constants_1.SERVICES_SCOPE}/${serviceName}`) {
147
+ importDecl.remove();
148
+ }
149
+ }
150
+ const nameParts = splitInputName(serviceName);
151
+ const stackClassName = `${nameParts.join('')}Stack`;
152
+ const applicationStage = stackSource.getClass('ApplicationStage');
153
+ if (applicationStage) {
154
+ const stageConstructor = applicationStage.getConstructors()[0];
155
+ if (stageConstructor) {
156
+ const statements = stageConstructor.getStatements();
157
+ for (const statement of statements) {
158
+ if (statement.getText().includes(`new ${stackClassName}(`)) {
159
+ statement.remove();
160
+ }
161
+ }
162
+ }
163
+ }
164
+ tree.write(stacksRelativePath, stackSource.getFullText());
165
+ }
166
+ /**
167
+ * Removes a project reference from the root tsconfig.json.
168
+ *
169
+ * @param tree - The Nx virtual file system tree
170
+ * @param referencePath - The path to remove from the references array
171
+ */
172
+ function removeTsConfigReference(tree, referencePath) {
173
+ (0, devkit_1.updateJson)(tree, 'tsconfig.json', json => {
174
+ json.references = (json.references ?? []).filter((r) => r.path !== referencePath);
175
+ return json;
176
+ });
177
+ }
112
178
  /**
113
179
  * Splits a kebab-case name into an array of capitalized parts.
114
180
  *
@@ -12,6 +12,7 @@ jobs:
12
12
  with:
13
13
  deploy: true
14
14
  deploy-command: yarn nx run application:cdk deploy
15
+ stack-name: prd/*
15
16
  environment-target: prd
16
17
  github-environment: production
17
18
  secrets: inherit
@@ -12,6 +12,7 @@ jobs:
12
12
  with:
13
13
  deploy: true
14
14
  deploy-command: yarn nx run application:cdk deploy
15
+ stack-name: stg/*
15
16
  environment-target: stg
16
17
  github-environment: staging
17
18
  secrets: inherit
@@ -14,7 +14,13 @@ import type { Construct } from 'constructs';
14
14
  */
15
15
  export class ApplicationStage extends Stage {
16
16
  constructor(scope: Construct, id: string, props?: StageProps) {
17
- super(scope, id, props);
17
+ super(scope, id, {
18
+ ...props,
19
+ env: {
20
+ account: process.env.CDK_DEFAULT_ACCOUNT,
21
+ region: process.env.CDK_DEFAULT_REGION || 'ap-southeast-2',
22
+ },
23
+ });
18
24
 
19
25
  const sharedInfra = new SharedInfraStack(this, 'shared-infra', props);
20
26
 
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.presetGenerator = presetGenerator;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const path_1 = require("path");
6
+ const constants_1 = require("../constants");
6
7
  const nxJson_1 = require("../helpers/configs/nxJson");
7
8
  const utilities_1 = require("../helpers/utilities");
8
9
  async function presetGenerator(tree, options) {
@@ -30,8 +31,8 @@ async function presetGenerator(tree, options) {
30
31
  });
31
32
  (0, devkit_1.writeJson)(tree, 'package.json', packageJson);
32
33
  // Generate application's tsconfigs
33
- const { tsConfig } = (0, utilities_1.constructProjectTsConfigFiles)('application');
34
- (0, devkit_1.writeJson)(tree, 'application/tsconfig.json', tsConfig);
34
+ const { tsConfig } = (0, utilities_1.constructProjectTsConfigFiles)(constants_1.MAIN_APPLICATION_NAME);
35
+ (0, devkit_1.writeJson)(tree, `${constants_1.MAIN_APPLICATION_FOLDER}/tsconfig.json`, tsConfig);
35
36
  await (0, devkit_1.formatFiles)(tree);
36
37
  }
37
38
  exports.default = presetGenerator;
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { RemoveGeneratorSchema } from './schema';
3
+ export declare function removeGenerator(tree: Tree, options: RemoveGeneratorSchema): Promise<void>;
4
+ export default removeGenerator;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeGenerator = removeGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const constants_1 = require("../constants");
6
+ const utilities_1 = require("../helpers/utilities");
7
+ async function removeGenerator(tree, options) {
8
+ const { name, forceRemove } = options;
9
+ const projectRoot = `${constants_1.SERVICES_FOLDER}/${name}`;
10
+ const projectName = `${constants_1.SERVICES_SCOPE}/${name}`;
11
+ if (!tree.exists(projectRoot)) {
12
+ throw new Error(`Service "${name}" does not exist at "${projectRoot}".`);
13
+ }
14
+ if (!forceRemove) {
15
+ const graph = await (0, devkit_1.createProjectGraphAsync)();
16
+ const dependents = Object.entries(graph.dependencies)
17
+ .filter(([source]) => source !== projectName)
18
+ .filter(([, deps]) => deps.some(d => d.target === projectName))
19
+ .map(([source]) => source);
20
+ if (dependents.length > 0) {
21
+ throw new Error(`Cannot remove "${name}": it is depended on by ${dependents.join(', ')}. ` +
22
+ 'Use --forceRemove to skip this check.');
23
+ }
24
+ }
25
+ (0, utilities_1.removeServiceFromMainApplication)(tree, name, constants_1.MAIN_APPLICATION_NAME);
26
+ (0, utilities_1.removeTsConfigReference)(tree, `./${projectRoot}`);
27
+ tree.delete(projectRoot);
28
+ if (tree.exists('package.json')) {
29
+ (0, devkit_1.updateJson)(tree, 'package.json', json => {
30
+ if (Array.isArray(json.workspaces)) {
31
+ json.workspaces = json.workspaces.filter((w) => w !== projectRoot);
32
+ }
33
+ return json;
34
+ });
35
+ }
36
+ await (0, devkit_1.formatFiles)(tree);
37
+ }
38
+ exports.default = removeGenerator;
@@ -0,0 +1,4 @@
1
+ export interface RemoveGeneratorSchema {
2
+ name: string;
3
+ forceRemove?: boolean;
4
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "RemoveServiceGenerator",
4
+ "title": "Remove a service and clean up its references",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the service to remove",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What is the name of the service to remove?"
15
+ },
16
+ "forceRemove": {
17
+ "type": "boolean",
18
+ "description": "Skip dependency check when removing the project",
19
+ "default": false
20
+ }
21
+ },
22
+ "required": ["name"]
23
+ }
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serviceGenerator = serviceGenerator;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const path_1 = require("path");
6
+ const constants_1 = require("../constants");
6
7
  const utilities_1 = require("../helpers/utilities");
7
- const SERVICES_FOLDER = 'services';
8
8
  function addTsConfigReference(tree, referencePath) {
9
9
  (0, devkit_1.updateJson)(tree, 'tsconfig.json', json => {
10
10
  json.references ??= [];
@@ -16,12 +16,12 @@ function addTsConfigReference(tree, referencePath) {
16
16
  });
17
17
  }
18
18
  async function serviceGenerator(tree, options) {
19
- const projectRoot = `${SERVICES_FOLDER}/${options.name}`;
19
+ const projectRoot = `${constants_1.SERVICES_FOLDER}/${options.name}`;
20
20
  const nameParts = (0, utilities_1.splitInputName)(options.name);
21
21
  const constant = nameParts.map(name => name.toUpperCase()).join('_');
22
22
  const stack = `${nameParts.join('')}Stack`;
23
- if (!tree.exists(SERVICES_FOLDER)) {
24
- tree.write(`${SERVICES_FOLDER}/.gitkeep`, '');
23
+ if (!tree.exists(constants_1.SERVICES_FOLDER)) {
24
+ tree.write(`${constants_1.SERVICES_FOLDER}/.gitkeep`, '');
25
25
  }
26
26
  const templateVars = {
27
27
  ...options,
@@ -41,7 +41,7 @@ async function serviceGenerator(tree, options) {
41
41
  (0, devkit_1.writeJson)(tree, `${projectRoot}/tsconfig.spec.json`, tsConfigSpec);
42
42
  // Integrate the new service with the root application
43
43
  addTsConfigReference(tree, `./${projectRoot}`);
44
- (0, utilities_1.addServiceStackToMainApplication)(tree, { name: options.name, constant, stack }, 'application');
44
+ (0, utilities_1.addServiceStackToMainApplication)(tree, { name: options.name, constant, stack }, constants_1.MAIN_APPLICATION_NAME);
45
45
  await (0, devkit_1.formatFiles)(tree);
46
46
  }
47
47
  exports.default = serviceGenerator;