Create a module containing all TypeScript exports

What i am trying to achieve

I am trying to create a module in TypeScript that will contain every export of the required classes. In fact, I actually need to have instructions import

in my classes that are relative, which is not really the best way to further maintain it. I would like to use absolute paths only.

After translation, classes (in prepared JavaScript) with absolute paths have incorrect paths (since they are not translated to relative paths and even worse when used in conjunction with the tsconfig.json

> option outDir

).

Example usage

Start with the following project hierarchy as an example

root
|- helpers
   |- MyHelper.ts
   |- AnotherHelper.ts
|- calculators
   |- MyCalc.ts
|- parsers
   |- XmlParser.ts
   |- JsonParser.ts
|- index.ts // currently holding export statements

      

What i want to do

I would like to have a file index.ts

like this:

// a top-level module, resolved like the `node` strategy of NodeJS module resolution
export module IndexModule {
    export { MyHelper } from "./helpers/MyHelper";
    export { AnotherHelper } from "./helpers/AnotherHelper";
    // do the same for every class in my project
}

      

So, I could do the following:

import { JsonParser } from "IndexModule"; // no relative nor absolute path

export class MyHelper {
    public constructor(input: any){
       var result: any = new JsonParser().parse(input);
       // work with result ...
    }
}

      

I've seen in many places (here on SO, the TypeScript github repo , ...) that I'm not the only one struggling with relative / absolute paths for imports.

My actual question is: Is it possible to create such a module (or mechanic) so that I can define each export in the top container and import that container as if I were using a NodeJS module (for example import * as moment from "moment"

)?

What i tried

Try first

This is the file index.ts

I have:

// index.ts is in the root directory of the project, it accesses classes with relative paths (but here, management is quite easy)
export { Constants } from "./helpers/Constants";
export { ConstraintCalculatorHelper } from "./helpers/ConstraintCalculatorHelper";
export { MomentHelper } from "./helpers/MomentHelper";
export { Formatter, OutputFormatterHelper } from "./helpers/OutputFormatterHelper";
// ... doing this for each class I have in my project

      

Second attempt

declare module IndexModule {
    export { Constants } from "./helpers/Constants";
    export { ConstraintCalculatorHelper } from "./helpers/ConstraintCalculatorHelper";
    export { MomentHelper } from "./helpers/MomentHelper";
    export { Formatter, OutputFormatterHelper } from "./helpers/OutputFormatterHelper";
    // error : Export declarations are not permitted in a namespace
}

      

Third, fourth, Nth attempts

Tried it declare namespace

, export module

etc. etc. no luck.

Tried also declare module "IndexModule

, results in error: import or export declaration in environment module declaration cannot refer to module via module relative name

Side note

I am not a NodeJS / Modules / Namespaces expert, so I may not have understood anything good. If so, I would appreciate it if someone could point out what I misunderstood.

Relevant link here that shows the actual problem.

Configuration:

  • NodeJS v7.2
  • TypeScript v2.2

tsconfig.json:

{
  "compileOnSave": true,
  "compilerOptions": {
    "removeComments": false,
    "sourceMap": true,
    "module": "commonjs",
    "target": "es6",
    "noImplicitAny": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "./bin/LongiCalc"
  }
}

      

+3


source to share


1 answer


Finally I managed to get what I wanted. Here are the steps and explanations.

1. An example of architecture

To illustrate the problem / solution, take this tree structure as an example

root
  \- A
     \- a1.ts
     \- a2.ts
  \- B
     \- D
        \- bd1.ts
        \- bd2.ts
     \- b1.ts
  \- C
     \- c1.ts
     \- c2.ts

      

There is a root folder containing 3 other folders (A, B and C):

  • A file contains two ( a1.ts

    and a2.ts

    )
  • B contains a folder with two files (D, c bd1.ts

    and bd2.ts

    ) and a file ( b1.ts

    )
  • C contains two files ( c1.ts

    and c2.ts

    )

So, if bd1.ts

classes declared in are needed a1.ts

, we will currently do.

import { ClassFromA1 } from "../../A/a1"

      

.. which uses relative paths and is difficult to maintain (IMO). We'll get rid of this in the next steps.

2. Build index.ts

with all imports

The purpose of the file index.ts

(I named it like this, you can choose any name you want) is to keep all the needed classes in one file.

So what we're going to do is create this file at the top of our arborescence in the directory node_modules

and export every single class we need in our project. Thus, we only need a file index.ts

to extract the required class.

Here is the content:

// current position : root/node_modules/index.ts
export { ClassFromA1 }  from "./A/a1.ts"
export { ClassFromA2 }  from "./A/a2.ts"
export { ClassFromBD1 } from "./B/D/bd1.ts"
export { ClassFromBD2 } from "./B/D/bd2.ts"
export { ClassFromB1 }  from "./B/b1.ts"
export { ClassFromC1 }  from "./C/c1.ts"
export { ClassFromC2 }  from "./C/c2.ts"

      

Now that we've got this, the previous import can be done this way.

import { ClassFromA1 } from "../../index"

      

.. but that still means using relative paths, not optimal paths.



Setting in project a rootDir

to folder root

can fix this problem.

import { ClassFromA1 } from "index"

      

.. and it works! But the problem arises with overflowed classes where paths are not resolved as relative after compilation.

This means that your a1.js

(compiled a1.ts

file) will still have the import set as it is, but will probably be wrong because it doesn't know rootDir

.

// a1.js
const index_1 = require("index") // supposed to be in the same package than the current file

      

3. Choose a strategy node resolution

Fortunately, NodeJS has a strategy node resolution

that can be set to allow import of the node module. Here is a really quick overview of the node module resolution strategy that can be installed in our project:

  • Check the import path
  • If the import path starts with .

    , ..

    or /

    =>, this is relative
  • If the import path starts with name => it is a node module ( this is important )

The resolution of modules in NodeJS is as follows (starting always with bs1.ts

):

  • Check if it exists root/B/D/node_modules/index.ts

    > No
  • Check if it exists root/B/node_modules/index.ts

    > No
  • Check if it exists root/node_modules/index.ts

    > Yes!

What we have done here is that we tricked Typescript into thinking that it index.ts

is a NodeJS module and that it should be resolved as one.

It will use the template described above (and in the link) to extract index.ts

, and from that we can then import the needed classes without relative / absolute paths.

4. Import as needed

Now the import can be done as follows.

// no matter in which file we are
import { ClassFromA1 } from "index";
import { ClassFromBD1 } from "index";
// and so on ..

      

.. and it works great, no errors in VisualStudio and no class overloads. Any comments on this solution are appreciated.

Greetings!

0


source







All Articles