Ts-loader: Error When Including Module From Mapped Path

Created on 13 Dec 2016  路  9Comments  路  Source: TypeStrong/ts-loader

Hi there, I'm getting an error when trying to load a module from a mapped path. The error is as follows:

ERROR in ./src/actions/auth/index.ts
Module not found: Error: Cannot resolve module '@app/constants/actionTypes' in /var/www/front-end/src/actions/auth
 @ ./src/actions/auth/index.ts 3:20-56

And I'm trying to do the following:

import * as actionTypes from "@app/constants/actionTypes";

The file definitely exists and worked with relative paths.

Here's my tsconfig.json:

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "module": "commonjs",
        "target": "es5",
        "jsx": "react",
        "baseUrl": ".",
        "paths": {
            "@app/*": [ "./src/*" ]
        }
    },
    "filesGlob": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "src/*.ts",
        "src/*.tsx"
    ],
    "exclude": [
        "node_modules"
    ]
}

What's weirder still, is that I can still reference other modules through that path, and the following runs without error:

import { RequestLogin, ResponseError, ResponseAuth } from "@app/models/api";

My actionTypes.ts file, if you need to see it, is as follows:

const LOGIN_REQUEST: string           = "App/LOGIN_REQUEST";
const LOGIN_SUCCESS: string           = "App/LOGIN_SUCCESS";
const LOGIN_FAILURE: string           = "App/LOGIN_FAILURE";
const UNAUTH_USER: string             = "App/UNAUTH_USER";
const AUTH_ERROR: string              = "App/AUTH_ERROR";
const FORGOT_PASSWORD_REQUEST: string = "App/FORGOT_PASSWORD_REQUEST";
const RESET_PASSWORD_REQUEST: string  = "App/RESET_PASSWORD_REQUEST";

export {
    LOGIN_REQUEST,
    LOGIN_SUCCESS,
    LOGIN_FAILURE,
    UNAUTH_USER,
    AUTH_ERROR,
    FORGOT_PASSWORD_REQUEST,
    RESET_PASSWORD_REQUEST,
};

I'm also using webpack for the build and jest for the testing and the error shows in both. It's baffling me as I can't work out why it would affect one module, but not the other.

Thanks for your help, and apologies if this isn't a ts-loader issue.

Most helpful comment

Ok, I didn't really understand how to implement this and I did it wrong! The reference in alias needs to be an absolute path and it works perfectly. I expected it to use the root property along with alias to resolve the module. So the following worked for me:

resolve: {
    root: [ __dirname ],

    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],

    alias: {
        "@app": __dirname + "/src"
    }
},

With corresponding baseUrl and paths in tsconfig.json:

"baseUrl": ".",
"paths": {
    "@app/*": [ "./src/*" ]
}

Thanks for the help.

All 9 comments

I've no experience using paths and I couldn't really say:

"paths": {
            "@app/*": [ "./src/*" ]
        }

Out of curiosity why are you using @ as a prefix?

Total aside but you could write less code if you did this:

export const LOGIN_REQUEST = "App/LOGIN_REQUEST";
export const LOGIN_SUCCESS = "App/LOGIN_SUCCESS";
export const LOGIN_FAILURE = "App/LOGIN_FAILURE";
export const UNAUTH_USER  = "App/UNAUTH_USER";
export const AUTH_ERROR = "App/AUTH_ERROR";
export const FORGOT_PASSWORD_REQUEST = "App/FORGOT_PASSWORD_REQUEST";
export const RESET_PASSWORD_REQUEST = "App/RESET_PASSWORD_REQUEST";

Type inference is your friend!

It's just a pattern I've seen elsewhere and I used it myself. I get the same issue without it.

Again, with the exports, I'm following a code style and know I have the option to export on the line.

Running directly with tsc and with the traceResolution flag it seems to resolve without issue:

======== Resolving module '@app/constants/actionTypes' from '/var/www/front-end/src/actions/auth/index.ts'. ========
'baseUrl' option is set to '/var/www/front-end', using this value to resolve non-relative module name '@app/constants/actionTypes'
'paths' option is specified, looking for a pattern to match module name '@app/constants/actionTypes'.
Module name '@app/constants/actionTypes', matched pattern '@app/*'.
Trying substitution './src/*', candidate module location: './src/constants/actionTypes'.
Loading module as file / folder, candidate module location '/var/www/front-end/src/constants/actionTypes'.
File '/var/www/front-end/src/constants/actionTypes.ts' exist - use it as a name resolution result.
Resolving real path for '/var/www/front-end/src/constants/actionTypes.ts', result '/var/www/front-end/src/constants/actionTypes.ts'
======== Module name '@app/constants/actionTypes' was successfully resolved to '/var/www/front-end/src/constants/actionTypes.ts'. ========

Which version of ts-loader are you using? I'm not sure I have any ideas other than are you using >= 0.9.3. that's when most of the TypeScript 2.0 support was added (which is I think what your approach requires)

I had 0.9.5 but just updated to 1.3.2 to see if it would work and no luck unfortunately.

In case there's something obvious, here's my webpack.config.js file:

module.exports = {
    entry: [
        "webpack-dev-server/client?http://192.168.34.10:8080",
        "webpack/hot/only-dev-server",
        "./src/index.tsx"
    ],
    output: {
        path: __dirname + "/dist",
        publicPath: "/",
        filename: "bundle.js",
    },

    devServer: {
        contentBase: './dist',
        hot: true,
        historyApiFallback: true,
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],

        modulesDirectories: [
            ".",
            "src/",
            "node_modules/"
        ]
    },

    module: {
        loaders: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'.
            { test: /\.tsx?$/, loader: "ts-loader", exclude: /node_modules/ }
        ],

        preLoaders: [
            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            { test: /\.js$/, loader: "source-map-loader" }
        ]
    },

    // When importing a module whose path matches one of the following, just
    // assume a corresponding global variable exists and use that instead.
    // This is important because it allows us to avoid bundling all of our
    // dependencies, which allows browsers to cache those libraries between builds.
    // externals: {
    //     "react": "React",
    //     "react-dom": "ReactDOM"
    // },
    watchOptions: {
        poll: true
    },
    tslint: {
        emitErrors: true,
        failOnHint: true
    }
};

Looks fine. Here's the docs on paths: http://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping

I've not used them myself so can't say much more. I do wonder if having a paths entry requires a corresponding alias entry in the webpack.config.js.... You could try it

Looks like including the alias config changed the error:

ERROR in ./src/actions/auth/index.ts
Module not found: Error: Cannot resolve 'file' or 'directory' ./src/constants/actionTypes in /var/www/front-end/src/actions/auth
 @ ./src/actions/auth/index.ts 3:18-55

But seems like it's searching for the file itself in that location, rather than a module. Even replacing with the following didn't help:

import * as actionTypes from "@app/constants/actionTypes.ts";

Ok, I didn't really understand how to implement this and I did it wrong! The reference in alias needs to be an absolute path and it works perfectly. I expected it to use the root property along with alias to resolve the module. So the following worked for me:

resolve: {
    root: [ __dirname ],

    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],

    alias: {
        "@app": __dirname + "/src"
    }
},

With corresponding baseUrl and paths in tsconfig.json:

"baseUrl": ".",
"paths": {
    "@app/*": [ "./src/*" ]
}

Thanks for the help.

Oh and to add to it, the reason it worked for one module and not the other in my case was because the one that worked was only exporting interfaces, which are ignored at runtime. Which is also why it worked in the compiler and not in webpack.

Was this page helpful?
0 / 5 - 0 ratings