Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Packaging multiple Typescript projects depending on the same common local module

I'm working on a set of VSTS extensions. Each extension is its own little Node project with its own package.json and its own node_modules folder. The folder structure is as follows:

 - MyExtension
   - package.json // containing all dev-dependencies
   - tslint.json
   - Tasks
     - tsconfig.json
     - Common
       - common.ts    // containing functioanlity shared across tasks
       - package.json // containing all runtime dependencies for all projects
     - My1stTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation
     - ...
     - ...
     - My6thTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation

The way VSTS build tasks work, is that they should be fully self-contained. I've fixed this so far by copying the contents of the Common project into each task and then run tsc to convert them all to JavaScript.

This isn't bad but requires constant copying of the contents of Common to get anything tested.

I tried using local file references, added a dependency in each task's package.json to file:../common, which works at development time, but this doesn't result in the common module being part of the task after generating the extension.

My background isn't in Node develpment, but in C#. I've searched all over and haven't found a solution that works well with vsts-extensions.

  • npm pack doesn't seem to work, as the extension expects all files to be there.
  • package.json/bundleDependencies looks promising but doesn't bundle the local file reference.
  • ///<reference path="../common/common.ts"/> works lovely for editing but still can't run after building the extension.
  • project reference with prepend doesn't work, build tasks require the commonjs module resolver. System and AMD aren't able to load the modules. Prepend only works with the latter.

Is there a way I can make this work "seamlessly" without having to take on bower or grunt and simply get each MyXthTask to have a copy of the local common module in their node_modules folder?

like image 721
jessehouwing Avatar asked Aug 20 '18 21:08

jessehouwing


People also ask

What are @types packages?

Packages on under the @types organization are published automatically from DefinitelyTyped using the types-publisher tool as per the docs. In addition, to there is another way to add types to your packages: In your package.json.

What does TSC -- build do?

Build Mode for TypeScript Running tsc --build ( tsc -b for short) will do the following: Find all referenced projects. Detect if they are up-to-date. Build out-of-date projects in the correct order.


Video Answer


1 Answers

I tried @matt-mccutchen's approach, but unfortunately, I couldn't get that to work with the VSTS build tasks due to the fact that these tasks require commonjs:

"compilerOptions": {
  "module": "commonjs",
  "target": "es6", 

But I did find a solution that works for me.

In the Tasks folder I've added a tsconfig.json which defines my default settings and includes the files from the Common library:

{
  "compileOnSave": true,
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "strict": false,
    "strictNullChecks": false,
    "removeComments": true
  },
  "files": [
    "./Common/uuidv5.d.ts",
    "./Common/Common.ts"
  ]
}

Then in each task I created a tsconfig.json which sets the output folder to the current folder for that project and which inherits from the tsconfig.json in the Tasks folder:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
      "outDir": "./", 
      "sourceRoot": "./"
  },
  "files": [
      "InstallExtension.ts"
  ]
}

This results in:

 - MyExtension
   - package.json // containing all dev-dependencies
   - tslint.json
   - Tasks
     - tsconfig.json   // Including Common by default
     - Common
       - common.ts     // containing functionality shared across tasks
       - package.json  // containing all runtime dependencies for Common
       - tsconfig.json // containing build configuration for just the common files, inherits from ..\Task\tsconfig.json
     - My1stTask
       - package.json  // containing all prod-dependencies for the task
       - task.ts       // containing task implementation
       - tsconfig.json // containing build configuration for the task, inherits from ..\Task\tsconfig.json
     - ...
     - ...
     - My6thTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation
       - tsconfig.json // containing build configuration for the task, inherits from ..\Task\tsconfig.json

When compiling a task the following

     - My6thTask
       - Common
         - Common.js   // Compiled common
       - My6thTask
         - task.js     // Compiled task
       - package.json  // containing all prod-dependencies
       - task.ts       // containing task implementation
       - task.json     // defining the task UI
       - tsconfig.json // containing build configuration for the task

The only thing I had to add to task.ts is the following:

///<reference path="../Common/Common.ts"/>
import * as common from "../Common/Common";

And to change the execution handler in the task.json to point to the new location:

  "execution": {
    "Node": {
      "target": "InstallExtension/InstallExtension.js", // was: InstallExtension.js
      "argumentFormat": ""
    }
  }

And all seems fine :D. Combined with using glob-exec I've been able to reduce the build time to less than a minute when building clean:

"initdev:npm": "npm install & glob-exec --parallel --foreach \"Tasks/*/tsconfig.json\" -- \"cd {{file.dir}} && npm install\"",
"compile:tasks": "glob-exec \"Tasks/*/tsconfig.json\" -- \"tsc -b {{files.join(' ')}}\"",
"lint:tasks": "glob-exec --parallel --foreach \"Tasks/*/tsconfig.json\" -- \"tslint -p {{file}}\"",
like image 151
jessehouwing Avatar answered Jan 03 '23 14:01

jessehouwing