5
votes

So I have a project that looks something like this:

app/
  bin/
  lib/
  src/
    main/
      submodule.ts
    utilities/
      common.ts
    main.ts
    tsconfig.json
  gulpfile.js

and app/src/main/submodule.ts needs to import app/src/utilities/common.ts. I am trying to use the ES6 syntax for this. Thus I expect something like this in submodule.ts:

import {common} from '/utilities/common';

Where the root / is app/src/ since that is where tsconfig is found. Yes, app/src/utilities/common.ts does export a module named common.

The problem is that I get "cannot find module '/utilities/common'" errors. I have tried a variety of things:

  • utilities/common
  • /src/utilities/common
  • /app/src/utilities/common

None of these work. A relative path of ../utilities/common does work, but relative paths for common modules is a maintenance nightmare.

It may be worth noting that I just updated from TS 1.5 to 1.6: using utilities/common had worked in 1.5. I cannot find any mention of a breaking change along these lines in the 1.6 notes, though.

I mention the gulpfile.ts and other folders because ultimately I want Gulp to get the TS files from src and put the compiled JS files in bin. I am reasonably confident that I have correctly configured Gulp for this, using gulp-typescript, but for completion's sake, here are my tsconfig.json and gulpfile.js.

  • tsconfig.json

    {
        "compilerOptions": {
            "module": "commonjs",
            "target": "es5",
            "experimentalDecorators": true,
            "emitDecoratorMetadata": true,
            "noEmitOnError": true
        },
        "filesGlob": [
            "./**/*.ts",
            "!./typings/**/*.ts"
        ]
    }
  • gulpfile.js

    var gulp = require('gulp');
    var ts   = require('gulp-typescript');
    var less = require('gulp-less');
    var sourcemaps = require('gulp-sourcemaps');
    
    var merge = require('merge2');
    var path  = require('path');
    
    var tsProject = ts.createProject('src/tsconfig.json', { noExternalResolve: true });
    
    gulp.task('html', function () {
        gulp.src([
                'src/**/*.html',
            ])
            .pipe(gulp.dest('bin'));
    });
    
    gulp.task('typescript', function () {
        tsProject.src()
            .pipe(sourcemaps.init())
            .pipe(ts(tsProject))
            .js
            .pipe(sourcemaps.write())
            .pipe(gulp.dest('bin'));
    });
    
    gulp.task('less', function () {
        gulp.src([
                'src/**/*.less',
            ])
            .pipe(sourcemaps.init())
            .pipe(less())
            .pipe(sourcemaps.write())
            .pipe(gulp.dest('bin'))
    });
    
    gulp.task('default', ['html', 'typescript', 'less']);
    
2

2 Answers

3
votes

Finally solved this. Per What's New, 1.6 changed module resolution to behave like Node's. I have not yet investigated Node's module resolution to determine if a fix is possible using that behavior, but I have found a workaround:

The old behavior can be triggered by specifying "moduleResolution": "classic" in tsconfig.json.

1
votes

Module resolution is performed relative to the current file if the path starts with ./ or ../.

Here is a quick example using:

/
/src/
/src/thing.ts
/main/
/main/submodule.ts
/utilities/
/utilities/common.ts

So the correct statement to import common.ts into submodule.ts would be:

import {common} from '../utilities/common';

You can also use the following root-path (note that there is no leading / or any .s here):

import {common} from 'src/utilities/common';

This works for me in Visual Studio code, with the parent folder of src opened as the working folder. In my case I am targeting ES5 with UMD modules.

It Works!

You can also resolve a module if it can be found by traversing up from the current location (this is a feature of NodeJS). So you can import thing.ts into submodule.ts using:

import {something} from 'thing';

The resolver will check the current folder, then the parent, then the parent's parent... and so on.

Absolute vs Relative Paths

When it comes to links on web pages, I'm in agreement with you that absolute paths are easier to maintain, especially where resources are shared at multiple levels.

When it comes to modules, I'm not sure I see the same maintenance problem as the paths here are "relative to the file that the import statement appears in" not relative to the web page. I wonder if this may be the application of a very sensible rule in the wrong place.