Search code examples
angulartypescriptmonorepo

Mono Repo: Accessing typescript file outside of project gives 'pos' undefined error


I am trying to build an Angular mono repo. However, when I try to run lerna run start --scope=@demo/core-app I get the following error:

[error] TypeError: Cannot read property 'pos' of undefined
    at createFileDiagnosticAtReference (E:\Temp\node_modules\typescript\lib\typescript.js:107529:68)
    at addProgramDiagnosticAtRefPath (E:\Temp\node_modules\typescript\lib\typescript.js:107547:93)
    at checkSourceFilesBelongToPath (E:\Temp\node_modules\typescript\lib\typescript.js:107208:25)
    at Object.getCommonSourceDirectory (E:\Temp\node_modules\typescript\lib\typescript.js:105619:21)
    at Object.getDeclarationEmitOutputFilePath (E:\Temp\node_modules\typescript\lib\typescript.js:17179:125)
    at getOutputPathsFor (E:\Temp\node_modules\typescript\lib\typescript.js:99488:112)
    at forEachEmittedFile (E:\Temp\node_modules\typescript\lib\typescript.js:99425:41)
    at Object.emitFiles (E:\Temp\node_modules\typescript\lib\typescript.js:99654:9)
    at emitWorker (E:\Temp\node_modules\typescript\lib\typescript.js:106125:33)
    at E:\Temp\node_modules\typescript\lib\typescript.js:106102:72
    at runWithCancellationToken (E:\Temp\node_modules\typescript\lib\typescript.js:106205:24)
    at Object.emit (E:\Temp\node_modules\typescript\lib\typescript.js:106102:26)
    at getFileEmitOutput (E:\Temp\node_modules\typescript\lib\typescript.js:108003:26)
    at updateShapeSignature (E:\Temp\node_modules\typescript\lib\typescript.js:108239:36)
    at getFilesAffectedByUpdatedShapeWhenModuleEmit (E:\Temp\node_modules\typescript\lib\typescript.js:108442:46)
    at Object.getFilesAffectedBy (E:\Temp\node_modules\typescript\lib\typescript.js:108194:144)

Here's how the folder is structured with Lerna: (Example github repo - Link)

-- packages
   -- core-app (Angular project)
   -- shared (Shared project from where the angular project would utilize some types)

Root Level tsconfig.json

{
    "compilerOptions": {     
      "baseUrl": "./",      /* Enable incremental compilation */
      "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
      "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015',
      "strict": false,                           /* Enable all strict type-checking options. */
      // "esModuleInterop": true,                  
      "skipLibCheck": true,                     /* Skip type checking of declaration files. */
      "forceConsistentCasingInFileNames": true,  /* Disallow inconsistently-cased references to the same file. */
    },
    "references": [
      { "path": "packages/shared" },
      { "path": "packages/core-app" }
    ]
  }

Shared project tsconfig.json

{
    "extends": "../../tsconfig.json",
    "compileOnSave": false,
    "compilerOptions": {
      "baseUrl": "./",
      "outDir": "lib",
      "sourceMap": true,
      "downlevelIteration": true,
      "allowSyntheticDefaultImports": true,
      "experimentalDecorators": true,
      "module": "es2020",
      "moduleResolution": "node",
      "importHelpers": true,
      "declaration": true,
      "declarationMap": true,
      "lib": [
        "es2018",
        "dom"
      ],
      "composite": true,
      "paths": {
        "@demo/core-app/*": ["../core-app/src/*"],
        "@demo/shared/*": ["./src/*"]
      }
    },
    "references": [
      { "path": "../core-app" } 
    ]  
  }

Core-app (Angular) tsconfig.json

{
  "extends": "../../tsconfig.json",
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": true,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ],
    "composite": true,
    "paths": {
      "@demo/core-app/*": ["src/*"],
      "@demo/shared/*": ["../shared/src/*"]
    }
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  },
  "references": [
    { "path": "../shared" } 
  ]  
}

Solution

  • I think you don't need to add references and paths in tsconfig.json on both side, lerna should be able to do it for you after bootstrapping.

    1. remove references, paths and composite in tsconfig.json

    After you remove references and paths, you should be able to see a different error indicating the missing @demo/shared/models/ExampleModel module.

    2. move the models folder in shared module

    To have the exact path of @demo/shared/models/ExampleModel, move the models folder to the root level of shared module and remove src folder.

    3. use interface or type for type definition. Otherwise, you will hit "Strict Class Initialization" error

    shared/models/ExampleModel.ts

    export interface ExampleModel{
        Id: number;
        Name: string;
    }
    

    4. add index.ts to export type for future expandability

    shared/models/index.ts export * from './ExampleModel'

    5. expose a entry point in shared module

    In package.json, add main entry point

    shared/package.json

    { "name": "@demo/shared", ... "main": "models/index.ts" }

    6. add dev and peer dependencies of @demo/shared packages in core-app

    core-app/package.json

    "peerDependencies": {
        "@demo/shared": "^0.0.1"
    },
    "devDependencies": {
        ...
        @demo/shared": "^0.0.1",
        ...
        "typescript": "~4.1.5"
    }
    

    typescript should be below 4.2.0 for angular 11

    7. fixing typo in app.component.html

    should be examples instead of exmaples*

    <div *ngFor="let example of examples"> 
      {{example.Name}}
    </div>
    

    8. boostraping

    npx lerna bootstrap --hoist
    

    9. run the app

    npx lerna run start --scope=@demo/core-app
    

    Here is the working repo for your reference. You can read the last commmit for details, it is concluded in the above steps.