4

I want to split my application into different node modules and have a main module which builds all other modules as well and I want to use typescript with es6 modules.

Here is my planned project structure:

  • main
    • node_modules
      • dep-a
      • dep-b
  • framework
    • interfaces
      • IComponent.ts
  • dep-a
    • components
      • test.ts
    • node_modules
      • framework
    • index.ts
  • dep-b
    • node_modules
      • framework

I want to be able to define interfaces in framework which can be consumed in dep-a, dep-b and main.

How do I set up this correctly? Can I compile everything from my main-module? Do I need to create different bundles for framework, dep-a, ... and another typing file? What is the best approach for this?

I already set up some test files and folders and used npm link to link the dependencies and webpack to bundle the files and I am always running into issues with files not being found:

error TS2307: Cannot find module 'framework/interfaces/IComponent'

and

Module not found: Error: Cannot resolve 'file' or 'directory' ./components/test

3 Answers 3

5

TL;DR generate declarations for the modules using declaration: true in tsconfig.json and specify the file for your generated typings in the typings entry of the package.json file

framework

Use a tsconfig file similar to this:

{
       "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "declaration": true,
        "noImplicitAny": true,
        "removeComments": true,
        "outDir": "dist",
        ...
    },
    "files": [
         ...
    ]
}

The important bit is declaration: true which will generate internal declarations in the dist directory

Assuming there is an index.ts file which (re)exports all the interesting parts of framework, create a package.json file with a main and typings entry pointing to, respectively, the generated js and the generated declaration, i.e.

{
   "name": "framework",
   "main": "dist/index.js",
   "typings": "dist/index.d.ts",
   ...
 }

Commit this module to a git repo, say bitbucket at : "https://[email protected]/myUser/framework.git"

dep-a

in package.json create a dependency to framework

{
    "dependencies": {
        "framework":     "https://[email protected]/myUser/framework.git"
    },
}

That is it.

import * from 'framework'

will pull the dependency with the typings, automatically

Obviously, it is possible to do with dep-a what was done with framework i.e. generate the declarations, update package.json and use dep-a as a module with embedded typings in main

note: a file URL will do in package.json/dependencies if you do not want go to via an external git repo

Sign up to request clarification or add additional context in comments.

3 Comments

thx for the quick response. This is more or less working. Only problem I am now facing with webpack is, that sourcemaps for dep-a are not working ... Do you know of any possibility to compile dep-a in main as well to get proper sourcemaps?
compile dep-a in main: I do not believe this is possible unless you move the dep-a sources to main (and it is not a dependency anymore). soucemaps: we do not ship sourcemaps with our own modules , so I have not tested. What i know is fine with webpack is accessing the js from the ~ source in the dubugger. Having the map files along with the js in dist/ for dep-a does not work ?
hm, ok, thx. The sourcemaps in deps are there but seem not to be loaded from webpack. I created an issue in the webpack repo: github.com/webpack/webpack/issues/1695
2

What arrived in TypeScript 1.6 is typings property in package.json module. You can check the relevant issue on GitHub.

So assuming you want to create separate modules ( dep-a, framework ). You can do the following :

main.ts                 // (1)
package.json            // (2)
node_modules/
  dep_a/                
    index.js            // (3)
    index.d.ts          // (4)
    package.json        // (5)
    node_modules/
      framework/ 
        index.js        // (6)
        index.d.ts      // (7)
        package.json    // (8)

So let's see what you have in your files :

//(1) main.ts
import * as depA from "depA";

console.log(depA({ a : true, b : 2 }) === true) // true;

//(2) package.json
{
  name: "main",
  dependencies: {
    "dep_a" : "0.0.1"
  }
  ...
}

For depA

//(3) dep_a/index.js
module.exports = function a(options) { return true; };

//(4) dep_a/index.d.ts;

import * as framework from "framework";

export interface IDepA extends framework.IFramework {
   a : boolean
}

export default function a(options: IDepA) : boolean; 

//(5) dep_a/package.json
{
  name: "dep_a",
  dependencies: {
    "framework" : "0.0.1"
  },
  ...
  typings : "index.d.ts" // < Magic happens here
}

For framework

//(6) dep_a/node_modules/framework/index.js
module.exports = true // we need index.js here, but we will only use definition file

//(7) dep_a/node_modules/framework/index.d.ts;

export interface IFramework {
   b : number;
}

//(8) dep_a/node_modules/framework/package.json
{
  name: "framework"
  ...
  typings : "index.d.ts"
}

What I don't include in this answer ( for clarity ) is another compilation phase, so you could actually write the modules ( dep_a, framework ) with typescript and then compile them to index.js before you use them.

Comments

1

For a detailed explanation and some background also see: https://medium.com/@mweststrate/how-to-create-strongly-typed-npm-modules-1e1bda23a7f4

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.