@aligent/nx-cdk 0.0.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.
Files changed (61) hide show
  1. package/README.md +130 -0
  2. package/generators.json +15 -0
  3. package/package.json +20 -0
  4. package/src/generators/helpers/configs/nxJson.d.ts +4 -0
  5. package/src/generators/helpers/configs/nxJson.js +52 -0
  6. package/src/generators/helpers/configs/packageJson.d.ts +61 -0
  7. package/src/generators/helpers/configs/packageJson.js +65 -0
  8. package/src/generators/helpers/configs/tsConfigs.d.ts +12 -0
  9. package/src/generators/helpers/configs/tsConfigs.js +40 -0
  10. package/src/generators/helpers/utilities.d.ts +82 -0
  11. package/src/generators/helpers/utilities.js +100 -0
  12. package/src/generators/preset/files/.editorconfig.template +18 -0
  13. package/src/generators/preset/files/.git-hooks/pre-push.template +68 -0
  14. package/src/generators/preset/files/.github/CODEOWNERS.template +3 -0
  15. package/src/generators/preset/files/.github/dependabot.yml.template +7 -0
  16. package/src/generators/preset/files/.github/workflows/ci-cd.yml.template +27 -0
  17. package/src/generators/preset/files/.gitignore.template +62 -0
  18. package/src/generators/preset/files/.nvmrc.template +1 -0
  19. package/src/generators/preset/files/.prettierignore.template +10 -0
  20. package/src/generators/preset/files/.yarnrc.yml.template +6 -0
  21. package/src/generators/preset/files/LICENSE.template +21 -0
  22. package/src/generators/preset/files/README.md.template +77 -0
  23. package/src/generators/preset/files/application/README.md.template +61 -0
  24. package/src/generators/preset/files/application/_internal/log-group-defaults-injector.ts.template +100 -0
  25. package/src/generators/preset/files/application/_internal/microservice-checks.ts.template +58 -0
  26. package/src/generators/preset/files/application/_internal/nodejs-function-defaults-injector.ts.template +110 -0
  27. package/src/generators/preset/files/application/_internal/step-function-defaults-injector.ts.template +126 -0
  28. package/src/generators/preset/files/application/_internal/version-functions-aspect.ts.template +74 -0
  29. package/src/generators/preset/files/application/bin/main.ts.template +72 -0
  30. package/src/generators/preset/files/application/cdk.context.json.template +4 -0
  31. package/src/generators/preset/files/application/cdk.json.template +92 -0
  32. package/src/generators/preset/files/application/eslint.config.mjs.template +3 -0
  33. package/src/generators/preset/files/application/lib/service-stacks.ts.template +21 -0
  34. package/src/generators/preset/files/application/package.json.template +27 -0
  35. package/src/generators/preset/files/cdk-config.yml.template +16 -0
  36. package/src/generators/preset/files/eslint.config.mjs.template +54 -0
  37. package/src/generators/preset/files/prettier.config.mjs.template +3 -0
  38. package/src/generators/preset/files/rolldown.config.base.mjs.template +46 -0
  39. package/src/generators/preset/files/tsconfig.json.template +6 -0
  40. package/src/generators/preset/files/vitest.config.base.mjs.template +39 -0
  41. package/src/generators/preset/files/vitest.global.setup.mjs.template +109 -0
  42. package/src/generators/preset/preset.d.ts +4 -0
  43. package/src/generators/preset/preset.js +35 -0
  44. package/src/generators/preset/schema.d.ts +7 -0
  45. package/src/generators/preset/schema.json +32 -0
  46. package/src/generators/service/files/README.md.template +29 -0
  47. package/src/generators/service/files/eslint.config.mjs.template +20 -0
  48. package/src/generators/service/files/package.json.template +32 -0
  49. package/src/generators/service/files/rolldown.config.mjs.template +3 -0
  50. package/src/generators/service/files/src/index.ts.template +75 -0
  51. package/src/generators/service/files/src/infra/.gitkeep.template +0 -0
  52. package/src/generators/service/files/src/runtime/handlers/.gitkeep.template +0 -0
  53. package/src/generators/service/files/src/runtime/lib/utils/.gitkeep.template +0 -0
  54. package/src/generators/service/files/tests/__data__/.gitkeep.template +0 -0
  55. package/src/generators/service/files/vitest.config.mjs.template +18 -0
  56. package/src/generators/service/generator.d.ts +4 -0
  57. package/src/generators/service/generator.js +43 -0
  58. package/src/generators/service/schema.d.ts +4 -0
  59. package/src/generators/service/schema.json +20 -0
  60. package/src/index.d.ts +2 -0
  61. package/src/index.js +19 -0
@@ -0,0 +1,109 @@
1
+ import { beforeAll } from 'vitest'
2
+
3
+ beforeAll(() => {
4
+ expect.addSnapshotSerializer(
5
+ replaceProperties({ property: [
6
+ // Replace asset storage locations in Lambda function snapshots
7
+ 'Code.S3Bucket',
8
+ 'Code.S3Key',
9
+ // Replace asset storage locations in Step Function snapshots
10
+ 'DefinitionS3Location.Bucket',
11
+ 'DefinitionS3Location.Key'
12
+ ] })
13
+ );
14
+ })
15
+
16
+ const PLACEHOLDER = '[SNAPSHOT_PLACEHOLDER]';
17
+ const isObject = (val) => !!val && typeof val === 'object';
18
+
19
+ // Helper function to traverse object and find properties to replace
20
+ const findPropertiesToReplace = (
21
+ obj,
22
+ property,
23
+ path = []
24
+ ) => {
25
+ const results = [];
26
+
27
+ for (const [key, value] of Object.entries(obj)) {
28
+ const currentPath = [...path, key];
29
+ const fullPath = currentPath.join('.');
30
+
31
+ let shouldReplace = false;
32
+
33
+ if (property instanceof RegExp) {
34
+ shouldReplace = property.test(fullPath);
35
+ } else if (Array.isArray(property)) {
36
+ shouldReplace = property.includes(fullPath);
37
+ } else {
38
+ shouldReplace = fullPath === property;
39
+ }
40
+
41
+ if (shouldReplace && value !== PLACEHOLDER) {
42
+ results.push({ path: currentPath, value });
43
+ }
44
+
45
+ if (isObject(value)) {
46
+ results.push(...findPropertiesToReplace(value, property, currentPath));
47
+ }
48
+ }
49
+
50
+ return results;
51
+ };
52
+
53
+ // Helper function to set value at nested path
54
+ const setValueAtPath = (obj, path, value) => {
55
+ let current = obj;
56
+
57
+ for (let i = 0; i < path.length - 1; i++) {
58
+ const key = path[i];
59
+ if (!isObject(current[key])) {
60
+ current[key] = {};
61
+ }
62
+ current = current[key];
63
+ }
64
+
65
+ current[path[path.length - 1]] = value;
66
+ };
67
+
68
+ /**
69
+ * Custom serializer for vitest snapshot tests
70
+ * Allows replacing properties in a snapshot with placeholder values.
71
+ *
72
+ * Properties to replace can be specified as a string, array of strings, or a single regular expression
73
+ * Nested properties can be specified using dot notation.
74
+ *
75
+ * @example
76
+ * ```
77
+ * beforeAll(() => {
78
+ * expect.addSnapshotSerializer(
79
+ * // Will replace the value of Code: { S3Bucket: '...', S3Key: '...' } anywhere in the object structure
80
+ * replaceProperties({ property: ['Code.S3Bucket', 'Code.S3Key'] })
81
+ * );
82
+ * })
83
+ * ```
84
+ *
85
+ * @param {{ property: string | string[] | RegExp, placeholder?: string }} input
86
+ * @returns
87
+ */
88
+ export const replaceProperties = ({
89
+ property,
90
+ placeholder = PLACEHOLDER,
91
+ }) => {
92
+ return {
93
+ test(val) {
94
+ if (!isObject(val)) return false;
95
+ const propertiesToReplace = findPropertiesToReplace(val, property);
96
+ return propertiesToReplace.length > 0;
97
+ },
98
+ serialize(val, config, indentation, depth, refs, printer) {
99
+ const clone = { ...val };
100
+ const propertiesToReplace = findPropertiesToReplace(clone, property);
101
+
102
+ for (const { path } of propertiesToReplace) {
103
+ setValueAtPath(clone, path, placeholder);
104
+ }
105
+
106
+ return printer(clone, config, indentation, depth, refs);
107
+ },
108
+ }
109
+ };
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { PresetGeneratorSchema } from './schema';
3
+ export declare function presetGenerator(tree: Tree, options: PresetGeneratorSchema): Promise<void>;
4
+ export default presetGenerator;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.presetGenerator = presetGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const path_1 = require("path");
6
+ const nxJson_1 = require("../helpers/configs/nxJson");
7
+ const utilities_1 = require("../helpers/utilities");
8
+ async function presetGenerator(tree, options) {
9
+ const { name, destination, nodeVersion } = options;
10
+ const [nodeVersionMajor] = nodeVersion.split('.');
11
+ const nameParts = (0, utilities_1.splitInputName)(name);
12
+ const projectName = nameParts.join(' ');
13
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), '.', {
14
+ ...options,
15
+ projectName,
16
+ folderName: destination || name,
17
+ nodeRuntime: `Runtime.NODEJS_${nodeVersionMajor}_X`,
18
+ template: '',
19
+ });
20
+ (0, devkit_1.updateNxJson)(tree, { ...nxJson_1.NX_JSON });
21
+ const packageJson = (0, utilities_1.constructPackageJsonFile)({
22
+ name: options.name,
23
+ projectName,
24
+ version: (0, utilities_1.getGeneratorVersion)(),
25
+ nodeVersion,
26
+ });
27
+ (0, devkit_1.writeJson)(tree, 'package.json', packageJson);
28
+ // Generate application's tsconfigs
29
+ const { tsConfig, tsConfigLib, tsConfigSpec } = (0, utilities_1.constructProjectTsConfigFiles)('application');
30
+ (0, devkit_1.writeJson)(tree, 'application/tsconfig.json', tsConfig);
31
+ (0, devkit_1.writeJson)(tree, 'application/tsconfig.lib.json', tsConfigLib);
32
+ (0, devkit_1.writeJson)(tree, 'application/tsconfig.spec.json', tsConfigSpec);
33
+ await (0, devkit_1.formatFiles)(tree);
34
+ }
35
+ exports.default = presetGenerator;
@@ -0,0 +1,7 @@
1
+ /* v8 ignore start */
2
+ export interface PresetGeneratorSchema {
3
+ name: string;
4
+ nodeVersion: string;
5
+ // FIXME: [MI-281] Add this into schema.json once we move to our own cli tool
6
+ destination?: string;
7
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "Preset",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the project/application",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What is the name of the project?",
15
+ "pattern": "^[a-z0-9-]+$",
16
+ "patternErrorMessage": "Project name must contain only lowercase alphanumeric characters and dashes"
17
+ },
18
+ "nodeVersion": {
19
+ "type": "string",
20
+ "description": "The target Node.js version",
21
+ "$default": {
22
+ "$source": "argv",
23
+ "index": 1
24
+ },
25
+ "default": "24.11.0",
26
+ "x-prompt": "What is the target Node.js version the project?",
27
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
28
+ "patternErrorMessage": "Node.js version must be a valid semantic version (semver), e.g. 22.10.0 or 24.10.0"
29
+ }
30
+ },
31
+ "required": ["name"]
32
+ }
@@ -0,0 +1,29 @@
1
+ # <%= serviceName %>
2
+
3
+ This is a CDK service generated using `@aligent/nx-cdk` for Nx.
4
+
5
+ ## Architecture
6
+
7
+ <!-- Add your architecture documentation here -->
8
+
9
+ # Technical Decisions
10
+
11
+ <!-- Add your notes and technical decisions here -->
12
+
13
+ ## Development
14
+
15
+ ### Type Checking
16
+
17
+ ```bash
18
+ npx nx check-types <%= name %>
19
+ ```
20
+
21
+ ### Testing
22
+
23
+ ```bash
24
+ npx nx test <%= name %>
25
+ ```
26
+
27
+ ## Deployment
28
+
29
+ This service is deployed as part of the CDK application. See the main README for deployment instructions.
@@ -0,0 +1,20 @@
1
+ import eslintPluginImport from 'eslint-plugin-import';
2
+ import baseConfig from '../../eslint.config.mjs';
3
+
4
+ export default [
5
+ ...baseConfig,
6
+ {
7
+ files: ['**/*.ts'],
8
+ plugins: { import: eslintPluginImport },
9
+ rules: {
10
+ 'import/no-extraneous-dependencies': [
11
+ 'warn',
12
+ {
13
+ optionalDependencies: false,
14
+ peerDependencies: false,
15
+ packageDir: ['.', '..', '../..'],
16
+ },
17
+ ],
18
+ },
19
+ },
20
+ ];
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "type": "module",
4
+ "main": "./src/index.ts",
5
+ "types": "./src/index.ts",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "import": "./src/index.ts",
10
+ "default": "./src/index.ts"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "bundleDependencies": [
15
+ "clients"
16
+ ],
17
+ "nx": {
18
+ "targets": {
19
+ "build": {
20
+ "executor": "nx:run-commands",
21
+ "options": {
22
+ "command": "rolldown -c rolldown.config.mjs",
23
+ "color": true,
24
+ "cwd": "{projectRoot}"
25
+ }
26
+ }
27
+ },
28
+ "tags": [
29
+ "scope:services"
30
+ ]
31
+ }
32
+ }
@@ -0,0 +1,3 @@
1
+ import { defineLambdaConfig } from '../../rolldown.config.base.mjs';
2
+
3
+ export default defineLambdaConfig('src/runtime/handlers');
@@ -0,0 +1,75 @@
1
+ import { Stack, StackProps, Stage, Tags } from 'aws-cdk-lib';
2
+ import { Code } from 'aws-cdk-lib/aws-lambda';
3
+ import { Construct } from 'constructs';
4
+ import path from 'node:path';
5
+
6
+ /**
7
+ * @important
8
+ * Due to the immutable nature of CloudFormation,
9
+ * CDK will generate new CloudFormation template and deploy if you change stack name
10
+ */
11
+ export const <%= constant %> = {
12
+ NAME: '<%= name %>',
13
+ DESCRIPTION: '<%= name %> service description'
14
+ } as const;
15
+
16
+ type Id = typeof <%= constant %>.NAME | (string & {});
17
+ interface Props extends StackProps {
18
+ description: string;
19
+ }
20
+
21
+ /**
22
+ * Entrypoint for the <%= name %> service
23
+ *
24
+ * Instantiate in a CDK Application Stage to deploy to AWS.
25
+ *
26
+ * Use 'resolve' helpers below when referencing lambda handlers, step function files etc.
27
+ */
28
+ export class <%= stack %> extends Stack {
29
+ constructor(scope: Construct, id: Id, props?: Props) {
30
+ super(scope, id, props);
31
+
32
+ const STAGE = Stage.of(this)?.stageName;
33
+ if (!STAGE) {
34
+ throw new Error('This construct must be used within a CDK Stage');
35
+ }
36
+
37
+ Tags.of(this).add('SERVICE', id);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Resolves a path to infra assets relative to this stack
43
+ *
44
+ * @param assetPath - The path to the asset.
45
+ * @returns The resolved path.
46
+ */
47
+ export function resolveAssetPath(assetPath: `${'infra/'}${string}`) {
48
+ return path.resolve(import.meta.dirname, assetPath);
49
+ }
50
+
51
+ /**
52
+ * Return an object with the default bundled code asset and
53
+ * handler property for use with the NodejsFunction construct.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * new NodejsFunction(this, 'FetchData', {
58
+ * ...resolveLambdaHandler('runtime/handlers/fetch-data.ts'),
59
+ * });
60
+ * ```
61
+ *
62
+ * @param assetPath - The path to the typescript handler file.
63
+ * @returns The resolved bundled code path and handler name.
64
+ */
65
+ export function resolveLambdaHandler(assetPath: `${'runtime/handlers/'}${string}${'.ts'}`) {
66
+ // Replace 'runtime/handlers/' with '..dist/' and remove the file extension
67
+ const bundledPath = assetPath.replace(
68
+ /^runtime\/handlers\/(?<path>.*)\.ts$/,
69
+ '../dist/$<path>'
70
+ );
71
+ return {
72
+ code: Code.fromAsset(path.resolve(import.meta.dirname, bundledPath)),
73
+ handler: 'index.handler',
74
+ };
75
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig, mergeConfig } from 'vitest/config';
2
+ import { vitestBaseConfig } from '../../vitest.config.base.mjs';
3
+
4
+ export default defineConfig(configEnv => {
5
+ return mergeConfig(
6
+ vitestBaseConfig(configEnv),
7
+ defineConfig({
8
+ cacheDir: '../../node_modules/.vite/<%= name %>',
9
+ test: {
10
+ env: {
11
+ NODE_ENV: 'test',
12
+ YOUR_ENV_VAR: 'environment-variable',
13
+ },
14
+ unstubEnvs: true,
15
+ },
16
+ })
17
+ );
18
+ });
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { ServiceGeneratorSchema } from './schema';
3
+ export declare function serviceGenerator(tree: Tree, options: ServiceGeneratorSchema): Promise<void>;
4
+ export default serviceGenerator;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serviceGenerator = serviceGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const path_1 = require("path");
6
+ const utilities_1 = require("../helpers/utilities");
7
+ const SERVICES_FOLDER = 'services';
8
+ function addTsConfigReference(tree, referencePath) {
9
+ (0, devkit_1.updateJson)(tree, 'tsconfig.json', json => {
10
+ json.references ??= [];
11
+ if (json.references.some((r) => r.path === referencePath)) {
12
+ throw new Error(`You already have a library using the import path "${referencePath}". Make sure to specify a unique one.`);
13
+ }
14
+ json.references.push({ path: referencePath });
15
+ return json;
16
+ });
17
+ }
18
+ async function serviceGenerator(tree, options) {
19
+ const projectRoot = `${SERVICES_FOLDER}/${options.name}`;
20
+ const nameParts = (0, utilities_1.splitInputName)(options.name);
21
+ const constant = nameParts.map(name => name.toUpperCase()).join('_');
22
+ const stack = `${nameParts.join('')}Stack`;
23
+ if (!tree.exists(SERVICES_FOLDER)) {
24
+ tree.write(`${SERVICES_FOLDER}/.gitkeep`, '');
25
+ }
26
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, {
27
+ ...options,
28
+ serviceName: nameParts.join(' '),
29
+ constant,
30
+ stack,
31
+ template: '',
32
+ });
33
+ // Generate service's tsconfigs
34
+ const { tsConfig, tsConfigLib, tsConfigSpec } = (0, utilities_1.constructProjectTsConfigFiles)('service');
35
+ (0, devkit_1.writeJson)(tree, `${projectRoot}/tsconfig.json`, tsConfig);
36
+ (0, devkit_1.writeJson)(tree, `${projectRoot}/tsconfig.lib.json`, tsConfigLib);
37
+ (0, devkit_1.writeJson)(tree, `${projectRoot}/tsconfig.spec.json`, tsConfigSpec);
38
+ // Integrate the new service with the root application
39
+ addTsConfigReference(tree, `./${projectRoot}`);
40
+ (0, utilities_1.addServiceStackToMainApplication)(tree, { name: options.name, constant, stack }, 'application');
41
+ await (0, devkit_1.formatFiles)(tree);
42
+ }
43
+ exports.default = serviceGenerator;
@@ -0,0 +1,4 @@
1
+ /* v8 ignore start */
2
+ export interface ServiceGeneratorSchema {
3
+ name: string;
4
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "ServiceGenerator",
4
+ "title": "Nx Generator for bootstrapping Serverless Framework services",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the service",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What is the name of the service?",
15
+ "pattern": "^(?!.*[Ss]tack|.*[Ss]ervice).*$",
16
+ "patternErrorMessage": "Service name cannot contain '[Ss]tack' or '[Ss]ervice'"
17
+ }
18
+ },
19
+ "required": ["name"]
20
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './generators/preset/preset';
2
+ export * from './generators/service/generator';
package/src/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /* v8 ignore next 2 */
18
+ __exportStar(require("./generators/preset/preset"), exports);
19
+ __exportStar(require("./generators/service/generator"), exports);