adrian-goe/nx-aws-cdk-v2

cannot use imported stacks and constructs from libs/packages in nx monorepo

Closed this issue · 3 comments

There is an issue when importing stacks/constructs in an aws-cdk infra app created using @ago-dev/nx-aws-cdk-v2.

This was a surprise issue for me given that one of the key reasons to use an nx workspace is to be able to split up projects into different libs/packages and import them into project apps. This approach is recommended by nx's authors nrwl.io as well.

I am not sure however I wonder if this may be related to not using something like tsconfig-paths/register or another solution (tsc-alias etc?) to resolve paths in the definition of nodeCommandWithRelativePath in nx-aws-cdk-v2/packages/aws-cdk-v2/src/utils/executor.util.ts.

If the issue is indeed related to paths, an unrelated project that might be of interest to reference is nx-builders by Web & Söhne that registers TS paths in its solution for supporting ts-node in the monorepo: https://github.com/tailoredmedia/backend-nx-skeleton/tree/master/packages/nx-builders#ts-node-dev.

Also of potential interest: aws/jsii#865

Replication:

  • create a new nx node:lib or js:lib and add a stack or construct to it
  • import the stack and/or construct in the aws-cdk infra app created using the generator from @ago-dev/nx-aws-cdk-v2 and rig it up
  • try to deploy the stack
  • observe the error "Cannot find module @myorg/my-library" where "@myorg/my-library" is the importPath of the package/library within the nx workspace

Example error output from trying to deploy:

OptionsParsedExecutorInterface {"_":["TestBucketStack"],"parseArgs":{"_":["TestBucketStack"]},"sourceRoot":"apps/infra/src","root":"apps/infra"}
nodeCommandWithRelativePath node /home/username/projects/my-nx-project/node_modules/aws-cdk/bin/cdk.js deploy
Executing command: node /home/username/projects/my-nx-project/node_modules/aws-cdk/bin/cdk.js deploy --_ TestBucketStack
Error: Cannot find module '@myorg/cdk-stacks'
Require stack:
- /home/username/projects/my-nx-project/apps/infra/src/main.ts
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:995:15)
    at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (/home/username/projects/my-nx-project/node_modules/.pnpm/@cspotcode+source-map-support@0.8.1/node_modules/@cspotcode/source-map-support/source-map-support.js:811:30)
    at Function.Module._load (node:internal/modules/cjs/loader:841:27)
    at Module.require (node:internal/modules/cjs/loader:1061:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at Object.<anonymous> (/home/username/projects/my-nx-project/apps/infra/src/main.ts:2:1)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module.m._compile (/home/username/projects/my-nx-project/node_modules/.pnpm/ts-node@10.9.1_72wjdbglblrsksmmmhzpvueijy/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Object.require.extensions.<computed> [as .ts] (/home/username/projects/my-nx-project/node_modules/.pnpm/ts-node@10.9.1_72wjdbglblrsksmmmhzpvueijy/node_modules/ts-node/src/index.ts:1621:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/home/username/projects/my-nx-project/apps/infra/src/main.ts' ]
}

Versions:

  • running current versions of everything including nx 15.8.6 (latest) + @ago-dev/nx-aws-cdk-v2 1.3.1

If there's a way to successfully import constants / functions / constructs + stacks from other libraries within the nx workspace, please let me know! Thanks for your work on this package!

For anyone stuck on this, I believe I have found a workaround solution that works for now.

Running the following command from the root path of an nx workspace will successfully run cdk synth and any stacks/constructs/functions/etc imported in the code from other libs within the monorepo will work.

Ensure you have tsconfig-paths installed as at least a dev dependency in your project's package.json (e.g. via npm install tsconfig-paths --save-dev or pnpm add -D tsconfig-paths).

Be sure to edit all the paths in the following example and change the command (from "synth" to "deploy", "destroy", etc.) to suit your particular needs:

node --require ts-node/register node_modules/aws-cdk/bin/cdk.js -a "npx ts-node --require tsconfig-paths/register --project apps/infra/tsconfig.app.json apps/infra/src/main.ts" synth DemoBucketStack

If you use pnpm like me and want to go full hog with it, substitute npx with pnpm dlx:

node --require ts-node/register node_modules/aws-cdk/bin/cdk.js -a "pnpm dlx ts-node --require tsconfig-paths/register --project apps/infra/tsconfig.app.json apps/infra/src/main.ts" synth DemoBucketStack

What's going on:

  • the above command examples run the cdk command directly from node_modules/ as cdj.js.
  • cdk.js is provided the ts-node command to run the aws-cdk app entrypoint file main.ts via the -a argument
  • ts-node is run with tsconfig-paths/register and the --project argument is provided to specify the tsconfig to use: tsconfig.app.json
  • tsconfig.app.json is as generated by the generator and extends from tsconfig.json which in turn extends from tsconfig.base.json in the root path of the nx workspace. The file tsconfig.base.json is where nx tooling adds the paths for each library/package added to the nx workspace, under compilerOptions > paths.
  • at the very end of the example we pass the cdk command (synth in my example, you can change this to deploy, destroy, etc) and the name of the stack we're targeting (in my case: "DemoBucketStack", which is specified in main.ts)

In my project, I will add scripts targets to package.json so that I can run the required cdk commands easily.

I believe the above resolution may be of assistance in terms of fixing the commands implemented by the @ago-dev/nx-aws-cdk-v2 package to support imports from other libraries/packages within the nx workspace.

In case it helps anyone, to build on the above, here's what I added to my package.json in the scripts section:

    "cdk:command": "node --require ts-node/register node_modules/aws-cdk/bin/cdk.js -a \"pnpm dlx ts-node --require tsconfig-paths/register --project apps/infra/tsconfig.app.json apps/infra/src/main.ts\""

This enables me to run:

pnpm cdk:command <cdk_command_name> <StackToTarget> --profile <aws_profile_name>

For example: pnpm cdk:command synth DemoBucketStack --profile default

If you have ts-node installed as a project dependency / dev dependency in package.json you can omit running it via npx or pnpm dlx and instead use the version installed in your node_modules:

    "cdk:command": "node --require ts-node/register node_modules/aws-cdk/bin/cdk.js -a \"pnpm ts-node --require tsconfig-paths/register --project apps/infra/tsconfig.app.json apps/infra/src/main.ts\""

@firxworx I have a PR up to fix this.