tsconfig.json configuration file for a project containing both `src` and `tests` directories

Looking to achieve a specific project structure:

- tsconfig.json
- src
    - app.ts
- tests
    - appTest.ts
    - appTest.js
- dist
    - app.js

If the tests folder did not exist, this tsconfig.json configuration would suffice:

{
    "compilerOptions": {
        "outDir":"dist"
    },
    "include" :[
        "src/**/*.ts"
    ]
}

However, when adding tests/**/*.ts to the include section, it will compile the test files into dist as well, altering the folder structure unintentionally.

Is there a way to instruct the TypeScript compiler to include test files for project support (like refactoring) but avoid including them in the dist output? Ideally, I want the .js files to be compiled in the tests directory, as shown in the desired project structure above.

Answer №1

If you want to set multiple root directories in your tsconfig.json file, you can do so using the rootDirs option like this:


{
  "compilerOptions": {
    "rootDirs": [
      "app",
      "test"
    ]
  }
}

You can find more information about this feature in the Typescript documentation under the section titled Virtual Directories with rootDirs: Module Resolution

Answer №2

To streamline your testing process, eliminate tests from your tsconfig file. Utilize mocha for testing instead, as it does not rely on tsconfig to locate the tests.

In your package configuration file, consider using the following setup:

"mocha": "mocha test/ --compilers ts:ts-node/register --recursive"

Within tsconfig.json, include the following:

"include": [
    "src/**/*.ts"
],

For more guidance on setting up tsconfig with a spec/test folder, check out this resource.

Answer №3

What do you think about completely separating the tests? How about organizing it like this:

- scripts
    - tsconfig.json
    - src
        - app.ts
    - dist
        - app.js
- tests
    - tsconfig.json
    - src
        - appTest.ts
    - bin
        - appTest.js

For instance, the scripts/tsconfig.json will have:

"compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    ...
}

And the tests/tsconfig.json will look like this:

"compilerOptions": {
    "outDir": "bin",
    "rootDir": "src",
    ...
}

Update

I tested this solution in webstorm and found two options:

(1) Tests files can import from scripts/dist:

scripts/tsconfig.json:

"compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true
}

tests/tsconfig.json:

"compilerOptions": {
    "outDir": "bin",
    "rootDir": "src"
}

tests/src/apptest.ts:

import * as app from "../../scripts/dist/app";

...

The resulting structure in tests/bin will be:

- tests
    - bin
        - apptest.js

Although refactoring in scripts won't automatically update

tests/src/apptest.ts</code, it will prompt you to make the necessary changes to ensure successful compilation.</p>

<p>(2) Tests files can import from <code>scripts/src
:

The scripts/tsconfig.json file doesn't require the declaration option since the source is used directly.

tests/src/apptest.ts:

import * as app from "../../scripts/dist/src";

...

Refactoring the source will automatically update the test files, but the output in tests/bin will appear like this:

- tests
    - bin
        - scripts
            - src
                - app.js
        - tests
            - src
                - apptest.js

If the structure in tests/bin is acceptable, this approach eliminates the need for additional tools.

Answer №4

When faced with a similar challenge, I utilized gulp to break down the compilation process into two distinct steps:

var gulp = require('gulp');
var ts = require('gulp-typescript');
var sourcemap = require('gulp-sourcemaps');
var replace = require('gulp-replace');

var path = require('path');
var merge = require('merge2');

var paths = {
  source: "source/",
  output: "dist/",
  spec: "spec/"
}

gulp.task('compile:typescript', function () {
  var project = ts.createProject('tsconfig.json', {
    typescript: require('typescript')
  });

  var tsResult =  gulp.src(paths.source + '**/*.ts')
    .pipe(sourcemap.init())
    .pipe(project());

  return merge([
    tsResult.dts.pipe(gulp.dest(paths.output)),
    tsResult.js //fix issue with relative source path
      .pipe(sourcemap.write('.', { 
        sourceRoot: function (file) {              
          var relative = path.relative(file.path, path.join(__dirname, "source"));
          var relativeSource = path.join(relative, 'source')
          return relativeSource;
        }
      }))
      .pipe(gulp.dest(paths.output))
  ]);
});

gulp.task('compile:tests', function () {
  var project = ts.createProject('tsconfig.json', {
    typescript: require('typescript')
  });

  var tsResult = gulp.src(paths.spec + '**/*spec.ts')
    .pipe(sourcemap.init())
    .pipe(project());

  return tsResult.js //fix issue with relative source path
    .pipe(sourcemap.write('.', {
      sourceRoot: function (file) {
        var relative = path.relative(file.path, path.join(__dirname, "spec"));
        var relativeSource = path.join(relative, 'spec')
        return relativeSource;
      }
    }))
    .pipe(replace(/(require\('\..\/source\/)/g, 'require(\'..\/dist\/'))
    .pipe(gulp.dest(paths.spec));
});

By following this method, your test source-code will import from the actual source files, ensuring accurate refactoring.

The source code is compiled into the dist folder, while the test files are compiled from the spec folder and saved in the same location. During the compilation of the spec files, the import paths are corrected to link to the compiled output using gulp-replace.

There were some additional considerations; in the past, the outDir in vscode could only point to one folder. To enable breakpoints in the tests, the spec js files had to be placed next to the ts files. This limitation may no longer apply after recent updates, although I have not tested it.

For a demonstration of the refactoring process, refer to the following link:

https://i.sstatic.net/EBgTm.gif

Answer №5

I stumbled upon a straightforward solution by setting up an npm script that both compiles and eliminates unnecessary files/folders in the lib directory.

To get rid of the test folder in your lib directory post TypeScript compilation, add the below script to your package.json file:

"scripts": {
    "build": "./node_modules/.bin/tsc && rm -r ./lib/test",
},

Answer №6

Quick note: This answer is tailored for those using Mocha as their test tool.

Important information is scattered across multiple pages.

Initially, you can find a key detail on the official Mocha homepage:

--require , -r Make sure to load a module before accessing the user interface or test files.

This comes in handy when dealing with: Compilers like ... TypeScript via ts-node (using --require ts-node/register)

Consequently, install ts-node using npm install ts-node --save-dev.


Furthermore, insights can be found on the Mocha wiki.

Although not directly quoted, the command you need to execute is:

$ mocha --require ts-node/register "test/**/*.ts"

Your package.json might have a configuration like this:

  "scripts": {
    "pretest": "npx tsc",
    "test": "mocha './test/**/*.ts' --require ts-node/register --recursive"
  },

As for your tsconfig.json file, it could resemble the following:

  "include": [
    "./src"
  ]

It's unnecessary to incorporate your /tests folder in the transpilation process. Also, since you're directly running .ts test and source files, there's no need for a sourcemap; your line numbers and callstack will still be functional.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Testing the receiveMessage function in SQS using Jest unit tests

Struggling to find the right approach for unit testing this function. I almost have it, but can't quite nail it down. Take a look at the function below: receiveMessage(callback: Function): any { this.sqs.receiveMessage( this.params, ...

I am encountering an issue in Vue3 where the parent event handler arguments are not being typed with the child's defineEmits definition. Can anyone explain this to me

I am struggling to correctly type the parent event handler based on the child definition, but no matter what I try, I always end up with `any` as the event type. Here is a code example: <script setup lang="ts"> // Child component type Even ...

"Efficient ways to calculate the total sum of an array of objects based on a specific property

I currently have a straightforward method that calculates the total sum of an object array based on one of the properties. const calculateSum = <T extends object, K extends keyof T>(array: T[], property : K) : number =>{ let total = 0; if ( ...

Type guards do not work properly on a union of enum types in TypeScript

Recently delved into the concept of Type Guards Chapter within the realm of Typescript However, I encountered an issue where my basic type guards failed to differentiate a union of enums. Why is this happening? enum A { COMMA = ',', PLUS = & ...

What is the method for generating a data type from an array of strings using TypeScript?

Is there a more efficient way to create a TypeScript type based on an array of strings without duplicating values in an Enum declaration? I am using version 2.6.2 and have a long array of colors that I want to convert into a type. Here is what I envision: ...

Updating Previous and Next links in an Angular Table following row deletions: A step-by-step guide

I need to implement a feature where row elements can be deleted by enabling checkboxes on the rows and clicking the Delete button. Although I am able to successfully delete items from the table upon clicking the Delete button, I am facing challenges in upd ...

Upon initialization, navigate to the specified location in the route by scrolling

My page has various components stacked one after the other, such as: <about></about> <contact></contact> I am utilizing the ng2-page-scroll to smoothly scroll to a particular section when a navigation link is clicked. However, I a ...

Create a dynamic styled component with tags based on props

Looking to craft a dynamic tag using styled components, where the tag is passed in via props. Here's an example of the code: import * as React from 'react'; import styled from 'styled-components'; type ContainerProps = { chi ...

What are the repercussions of labeling a function, TypeScript interface, or TypeScript type with export but never actually importing it? Is this considered poor practice or is there a potential consequence?

I find myself grappling with a seemingly straightforward question that surprisingly has not been asked before by others. I am currently immersed in a TypeScript project involving Vue, and one of the developers has taken to labeling numerous interfaces and ...

Transmit information using the buttonRenderer feature in Ag-Grid for Angular applications

Struggling to transfer data between two unrelated components by clicking on a cell in ag-Grid and passing the data to a form component. I utilized the buttonRenderer function to extract row data from ag-Grid, but I'm unsure how to pass it to the secon ...

"Error: Variable becomes undefined due to the implementation of async-await in TypeScript

There has been a persistent issue I've been dealing with for some time now. The problem lies in the fact that vm_res is undefined within the async update_vm_raw_device function despite the function running smoothly. As a result, the value being update ...

Running a TypeScript program that has been compiled with module resolution is not working as expected

I am currently in the process of compiling a TypeScript file with the following code: import { magic } from 'lib/magic'; magic(); The file structure looks like this: ./src/ main.ts lib/ a/magic.ts b/magic.ts Within ...

Implementing Routes in Express Using Typescript Classes

Seeking assistance in converting a Node.js project that utilizes Express.js. The objective is to achieve something similar to the setup in the App.ts file. In pure Javascript, the solution remains unchanged, except that instead of a class, it involves a mo ...

Assign the chosen option in the Angular 2 dropdown menu to a specific value

Currently, I am utilizing FormBuilder in order to input values into a database. this.formUser = this._form.group({ "firstName": new FormControl('', [Validators.required]), "lastName": new FormControl('', [Validators.required]), ...

Discover the location of the class definition in VSCode with this helpful clue

Here is the code snippet I am working with: const Gatherer = require('../gatherer'); class MetaRobots extends Gatherer { /** * @param {{driver: !Driver}} options Run options … } module.exports = MetaRobots; When using VSCode, Driver ...

The Angular Material date picker unpredictably updates when a date is manually changed and the tab key is pressed

My component involves the use of the Angular material date picker. However, I have encountered a strange issue with it. When I select a date using the calendar control, everything works fine. But if I manually change the date and then press the tab button, ...

What are the steps to integrate a database into my Next.js application?

While I was experimenting with integrating postgresql into a nextjs project, I encountered an error 405 when trying to create an account. Below is the error message in the browser console: page.tsx:14 POST http://localhost:3000/api/auth/ ...

Utilizing Angular and TypeScript: The best approach for managing this situation

I need some guidance on handling asynchronous calls in Angular. Currently, I am invoking two methods from a service in a controller to fetch an object called "categoryInfo." How can I ensure that these methods return the categoryInfo correctly and displa ...

Navigating the diverse types of React HTML DOM events within a function concatenation

Question: Is it possible to merge different HTML DOM event types in Typescript? (I am aware of the option to split my function into two separate functions to avoid this error, but I would like to find another solution) Here is an example of my component ...

Unable to retrieve this information within an anonymous function

I am currently working on converting the JSON data received from an API interface into separate arrays containing specific objects. The object type is specified as a variable in the interface. Here is the interface structure: export interface Interface{ ...