Ts-loader: Build including files that are not in the directory and hence failing

Created on 29 Dec 2020  路  11Comments  路  Source: TypeStrong/ts-loader

Expected Behaviour

Build should not include files that are contained within the same directory.

Actual Behaviour

It tries to load files outside of the include path and fails.

Location of a Minimal Repository that Demonstrates the Issue.

Unfortunately, I cannot share the repo as it is private.

Here is my webpack.config.js

const { join, resolve } = require('path');

const slsw = require('serverless-webpack');
const webpackNodeExternals = require('webpack-node-externals');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

const app = Object.keys(slsw.lib.entries)[0].split('/')[2];

module.exports = {
    mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
    externals: [webpackNodeExternals(), 'aws-sdk'],
    target: 'node',
    entry: slsw.lib.entries,
    output: {
        libraryTarget: 'commonjs',
        filename: 'handler.js',
        path: join(__dirname, `../.lambda/${app}`),
    },
    resolve: {
        extensions: ['.ts', '.js'],
        plugins: [
            new TsconfigPathsPlugin({
                configFile: join(__dirname, '../tsconfig.json'),
            }),
        ],
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: 'ts-loader',
                include: resolve(__dirname, '../src'),
            },
        ],
    },
};

Where the included path is src/apps/auth (for example).

This is the errors I get:

ERROR in /Users/sam/Work/personal/test-api/test/__test-utils__.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test-utils__.ts(5,31)
      TS2307: Cannot find module './modules' or its corresponding type declarations.

ERROR in /Users/sam/Work/personal/test-api/test/__test-utils__.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test-utils__.ts(6,27)
      TS2307: Cannot find module './providers' or its corresponding type declarations.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(3,28)
      TS2307: Cannot find module '../auth.module' or its corresponding type declarations.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(20,30)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(41,30)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(51,30)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(52,35)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(62,30)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(63,35)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(75,30)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts
[tsl] ERROR in /Users/sam/Work/personal/test-api/test/__test__/auth-login.spec.ts(76,35)
      TS2339: Property 'toHaveProperty' does not exist on type 'FunctionMatchers<any>'.

As the error says, these files are included even though the are outside of the src folder which is the only folder included in the rule.

Most helpful comment

@samrith-s, That's great. Please consider sharing what you learned as we all benefit from reading discussions on issues, even if they are now closed.

All 11 comments

What does your tsconfog.json look like? Webpack will get ts-loader to process .ts files in src/ but then TypeScript may process additional files if they are referenced by the original typescript source or listed in the "files" or "include" section of your tsconfig.json.

If you can't find the problem try to create a minimal example which demonstrates the problem and which you can share.

Hello @appzuka

Yeah I figured that out. I added a new tsconfig.json which excludes test and .spec.ts files.

{
    "extends": "./tsconfig.json",
    "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}

Quick question: none of my paths are resolved. Is this something that ts-loader should do or something that is out of scope of this project? It is only bundling the entry file, and none of the dependents. So I still have:

import { createApp } from '@globals/create-app'

Which is throwing an error at runtime.

Webpack should bundle all files imported in your app. The resolution needs to be set in both tsconfig and webpack.config but as you are using tsconfig-paths-webpack-plugin then defining them in tsconfig.json should be enough. If TypeScript or webpack cannot find the modules you would get an error at build time, not run time.

Webpack won't bundle files specified in externals. As you are using webpack-node-externals it will not bundle any files from node_modules, in which case you will get a runtime error. Try omitting the externals from your webpack.config

Yep, got that. I am trying it with this config:

const { resolve } = require('path');

const slsw = require('serverless-webpack');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');

const {
    compilerOptions: { baseUrl, paths },
} = require('../tsconfig.json');

const app = Object.keys(slsw.lib.entries)[0].split('/')[2];

const alias = Object.entries(paths).reduce((acc, [key, value]) => {
    const newKey = key.replace(/\/\*/g, '');

    if (!acc[newKey]) {
        acc[newKey] = resolve(__dirname, '../', baseUrl, value[0].replace(/\/\*/g, '') + '/');
    }

    return acc;
}, {});

module.exports = {
    mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
    target: 'node',
    entry: slsw.lib.entries,
    output: {
        libraryTarget: 'commonjs2',
        path: resolve(__dirname, '../.webpack'),
        filename: '[name].js',
    },
    resolve: {
        extensions: ['.ts', '.js'],
        alias,
        plugins: [
            new TsconfigPathsPlugin({
                configFile: resolve(__dirname, '../tsconfig.sls.json'),
                extensions: ['.ts', '.js'],
            }),
        ],
    },
    module: {
        noParse: /test/gi,
        rules: [
            {
                test: /\.(ts|js)$/,
                loader: 'ts-loader',
                include: resolve(__dirname, '../src'),
                options: {
                    transpileOnly: true,
                },
            },
        ],
    },
    plugins: [
        new copyWebpackPlugin({
            patterns: [
                {
                    from: 'prisma/schema.prisma',
                    to: `src/apps/${app}/src`,
                },
            ],
        }),
    ],
};

And my output is very weird:

module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
/******/
/******/    // define __esModule on exports
/******/    __webpack_require__.r = function(exports) {
/******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/        }
/******/        Object.defineProperty(exports, '__esModule', { value: true });
/******/    };
/******/
/******/    // create a fake namespace object
/******/    // mode & 1: value is a module id, require it
/******/    // mode & 2: merge all properties of value into the ns
/******/    // mode & 4: return value when already ns object
/******/    // mode & 8|1: behave like require
/******/    __webpack_require__.t = function(value, mode) {
/******/        if(mode & 1) value = __webpack_require__(value);
/******/        if(mode & 8) return value;
/******/        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/        var ns = Object.create(null);
/******/        __webpack_require__.r(ns);
/******/        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/        return ns;
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = "./src/apps/auth/src/lambda.ts");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./src/apps/auth/src/lambda.ts":
/*!*************************************!*\
  !*** ./src/apps/auth/src/lambda.ts ***!
  \*************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.handler = void 0;\nconst create_lambda_1 = require(\"@globals/create-lambda\");\nconst auth_module_1 = require(\"./auth.module\");\nlet server;\n(async function () {\n    server = await create_lambda_1.bootstrapServer(auth_module_1.AuthModule);\n})();\nexports.handler = create_lambda_1.createHandler(server);\n\n\n//# sourceURL=webpack:///./src/apps/auth/src/lambda.ts?");

/***/ })

/******/ });

I have no idea why this is happening. Sorry if this is not relevant to ts-loader I'm just trying all possible avenues to figure out the problem.

The output looks like a webpack bundle output. What are you expecting to see?

If you can create a minimal repository that shows any problems you are having I am happy to take a look on my system.

I can add you to a private repository that has all of this, as extracting it to create a minimal reproducible repository will be extremely difficult. Will that do?

Sure. I cannot promise how much time I can spend on it but I'd be happy to clone the project, see what results I get and let you know if I can see any problems.

Please include exact instructions of what I need to do and how to reproduce the error, for example:

git clone
yarn install
yarn start(?)

Hey @appzuka ,

I figured out the reason for this. It works well, but just one thing. Now it doesn't bundle the imported node_modules into the package. I mean, it's weird, it kinda does, but I get an error saying createParamsDecorator (which is exported from @nestjs/common) is not found.

Figured it out. Took a long time, but everything is working in the end. If anyone is stuck with this, please feel free to reach out! 馃槃

@samrith-s, That's great. Please consider sharing what you learned as we all benefit from reading discussions on issues, even if they are now closed.

Yep, sure!

What I did is to use TS Loader in conjunction with tsconfig-paths-webpack-plugin.

Here is my paths and baseUrl of tsconfig.json

{
    "paths": {
        "@apps/*": ["apps/*"],
        "@config": ["libs/globals/src/config"],
        "@config/*": ["libs/globals/src/config/*"],
        "@decorators/*": ["libs/globals/src/decorators/*"],
        "@globals": ["libs/globals/src"],
        "@globals/*": ["libs/globals/src/*"],
        "@guards/*": ["libs/globals/src/guards/*"],
        "@interceptors/*": ["libs/globals/src/interceptors/*"],
        "@orm": ["libs/orm/src"],
        "@strategies/*": ["libs/globals/src/strategies/*"],
        "@test-utils": ["../test/__test-utils__"],
        "@utils": ["libs/globals/src/utils"],
        "@utils/*": ["libs/globals/src/utils/*"]
    },
    "baseUrl": "./src"
}

And the webpack.config.json

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

module.exports = {
    // other config
    resolve: {
        extensions: ['.ts', '.js'],
        plugins: [
            new TsconfigPathsPlugin({
                configFile: './tsconfig.json',
            }),
        ],
    }
}
Was this page helpful?
0 / 5 - 0 ratings