CDKTF tsconfig paths not working for local imports

Problem

For a cleaner approach I am trying to not use relative path in my Typescript project like this.
import { MyModel } from '../../../../../models';
However, when trying to use paths for local file resolution, cdktf throws the Error: Cannot find module '@models'.

Here is my tfconfig.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "alwaysStrict": true,
    "charset": "utf8",
    "declaration": true,
    "experimentalDecorators": true,
    "inlineSourceMap": true,
    "inlineSources": true,
    "lib": [
      "es2018"
    ],
    "module": "CommonJS",
    "noEmitOnError": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "strict": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "stripInternal": true,
    "target": "ES2018",
    "incremental": true,
    "paths": {
      "@models": [
        "./lib/models/index.ts"
      ],
      "@helpers": [
        "./lib/helpers/index.ts"
      ],
    },
  },
  "include": ["**/*.ts", "./src/**/*.ts"],
  "exclude": [
    "node_modules",
    "cdktf.out"
  ]
}

inside my code:

import { MyModel } from "@models"

  • The TS server doesn’t complain and compiles. But when running ‘cdktf synth’ I get that error.

I also tried this workaround from a github comment.

Please let me know how I can solve this issue.

Hi @sabinaya.kc :wave:

What command do you have in your cdktf.json > app config?

edit: You might need to adjust that one similar to in the example you linked: cdk-ts-path-mapping/cdk.json at master · moltar/cdk-ts-path-mapping · GitHub

{
  "language": "typescript",
  "app": "npx ts-node -r tsconfig-paths/register --prefer-ts-exts src/main.ts",
  "projectId": "XX",
  "sendCrashReports": "true",
  "terraformProviders": ["aws@~> 4.0"],
  "terraformModules": [
    {
      "name": "vpc",
      "source": "terraform-aws-modules/vpc/aws",
      "version": "~> 3.0"
    }
  ],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
    "allowSepCharsInLogicalIds": "true"
  }
}

I tried this approach but then I get this error.

In my stack code tgw-stack.ts, I have

Do you get the same error when running the app command from your cdktf.json file directly?

Yes, I do get the same error. If my local imports are plain interfaces, it works fine such as @models, but as soon as I add a complex type or class (which has cdktf imports such as Construct or IAspect.ts) it throws the above error. I dont know what I am doing wrong. I am using the latest version of typescript, cdktf etc. Some kinda cyclic dependency might be causing this which I am struggling to find.

This is my folder structure,

libs 
- helpers
 - tag-aspect.ts 
- models
  - my-model.ts
- constructs
- my-vpc.ts 
src
- environments
  - core 
   - tgw-stack.ts  --> imports { TagAddingAspect } from "@helpers" --> Throws above error.
  main.ts --> calls the tgw-stack.ts

TagAspect code inside lib\helpers

import { IConstruct } from "constructs";
import { IAspect } from "cdktf";

// Not all constructs are taggable, so we need to filter it
type TaggableConstruct = IConstruct & {
  tags?: { [key: string]: string };
  tagsInput?: { [key: string]: string };
};

function isTaggableConstruct(x: IConstruct): x is TaggableConstruct {
  return "tags" in x && "tagsInput" in x;
}

export class TagsAddingAspect implements IAspect {
  constructor(private tagsToAdd: Record<string, string>) {}

  // This method is called on every Construct within the specified scope (resources, data sources, etc.).
  visit(node: IConstruct) {
    if (isTaggableConstruct(node)) {
      // We need to take the input value to not create a circular reference
      const currentTags = node.tagsInput || {};
      this.tagsToAdd["Name"] = node.node.id;
      node.tags = { ...this.tagsToAdd, ...currentTags };
    }
  }
}

I got this to work by removing baseUrl from my tsconfig, and instead adding only paths, ex:

{
  "compilerOptions": {
    "paths": {
      "src/*": ["./src/*"],
      "gen/*": ["./.gen/*"]
    },
    "alwaysStrict": true,
    "charset": "utf8",
    "declaration": true,
    "experimentalDecorators": true,
    "inlineSourceMap": true,
    "inlineSources": true,
    "lib": ["es2018"],
    "module": "CommonJS",
    "noEmitOnError": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "strict": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "stripInternal": true,
    "target": "ES2018",
    "incremental": true,
    "skipLibCheck": true
  },
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  },
  "include": ["**/*.ts"],
  "exclude": ["node_modules", "cdktf.out"]
}

This still requires the fix from this commit.

Basically, I think the circular dependency is coming from having the base URL at the project root, which includes the node_modules folder. By not having this set, and instead only using paths, you can avoid the circular dependency.

I get this error.

Missing baseUrl in compilerOptions. tsconfig-paths will be skipped
Error: Cannot find module ‘gen/modules/eks-blueprints-addons’
Require stack:

Is this is a generated module? Normally the path is ./.gen/modules/... (with a . before the gen) and you need to run cdktf get to generate these bindings?

No its not a generated module. Its more like a lib folder in the root. that can be used accross the other stacks in a mono repo.

Also, its the same problem with the .gen folder.

If I need to access the .gen folder from a deeply nested stack or class, i have to use ../../../../gen instead of doing .gen/ or @gen.