Ways to "Compile out" log commands

In my typescript project, there is a section of code dedicated to creating debug information. However, upon profiling the application, I discovered that this debug code is causing a significant performance impact.

Currently, my approach involves setting a global variable let debug=true, and

function log(statement: string):void {
  if(debug) console.log(statement);
}

// somewhere in the application
...
log(`hi mom! ${Factory.createDebugStatement()}`);
...

The function Factory.createDebugStatement() is resource-intensive. Furthermore, I am trying to find a way to avoid cluttering my code with if(debug) log(...). I would like typescript to eliminate the entire log statement.

Essentially, I am seeking a TypeScript 'equivalent' of the concept found in the following C structure:

// header file
#ifdef ENABLE_DEBUG
#define DEBUG(...) printf(__VA_ARGS__)
#else
#define DEBUG(...)                                                             \
  {}
#endif

//application

main(){
...
DEBUG("hi mom!");
...
}

This way, I could use cc -DENABLE_DEBUG=1 main.c to include debug messages. By omitting -DENABLE_DEBUG, the entire code related to debugging would be removed.

Answer №1

To achieve that, my go-to solution is using a bundler with advanced tree-shaking capabilities like or

Esbuild makes it simple: check out

You can define the debug variable and let the bundler eliminate code branches that are redundant.

*For safety, consider using a unique name like DEBUG_YOUR_PACKAGE_NAME to avoid conflicts with dependency code)

Answer №2

Credit: Big thanks to Simon Lax for guiding me towards focusing on the bundler aspects...

In my development setup, I utilize rollup.js, along with the plugin-strip. By toggling a single switch in my rollup.config.js, I can easily enable/disable the 'transpile away' feature.

// src/main.ts
import fs from "fs";
class Service {
  expensive(res = ""): string {
    for (let i = 0; i < 1000; i++) res += i;
    return res;
  }
  normal(): string {
    return "Hi mom!";
  }
}

class Logger {
  public static log<T>(statement: T): T | undefined {
    console.log(statement);
    return statement;
  }
}

const s = new Service();

Logger.log(`${s.expensive()}`);

console.log(s.normal());

// and I can even use it in different situations, which usually expect the output.
fs.writeFileSync(
  "/tmp/abc",
  Logger.log(
    "not printed to screen nor to file but I wont have runtime exceptions, because TS hints me to catch the undefined case with the null'ish concealing",
  ) ?? "",
);
// rollup.config.js
import typescript from "@rollup/plugin-typescript";
import strip from "@rollup/plugin-strip";

export default [
  {
    external: ["fs"],
    input: ["src/main.ts"],
    output: {
      dir: "dist",
      format: "es",
    },
    strictDeprecations: true,
    plugins: [
      strip({
        include: ["**/*.ts"],
        functions: process.env.DEBUG ? [] : ["logger.log"],
      }),
      typescript(),
    ],
  },
];
// package.json
 
 "type": "module",
  "scripts": {
    "bundle": "rollup --config rollup.config.js"
  },
  "devDependencies": {
    "@rollup/plugin-strip": "^3.0.2",
    "@rollup/plugin-typescript": "^11.0.0",
    "rollup": "^3.15.0",
    "typescript": "^4.9.5"
  },

Upon executing npm run bundle, the stripped version will be generated

// dist/main.js
import fs from 'fs';

class Service {
    expensive(res = "") {
        for (let i = 0; i < 1000; i++)
            res += i;
        return res;
    }
    normal() {
        return "Hi mom!";
    }
}
const s = new Service();
console.log(s.normal());
// and I can even use it in different situations, which usually expect the output.
fs.writeFileSync("/tmp/abc", "");

Running DEBUG=1 npm run bundle will create the full version

// dist/main.js
import fs from 'fs';

class Service {
    expensive(res = "") {
        for (let i = 0; i < 1000; i++)
            res += i;
        return res;
    }
    normal() {
        return "Hi mom!";
    }
}
class Logger {
    static log(statement) {
        console.log(statement);
        return statement;
    }
}
const s = new Service();
Logger.log(`${s.expensive()}`);
console.log(s.normal());
// and I can even use it in different situations, which usually expect the output.
fs.writeFileSync("/tmp/abc", Logger.log("not printed to screen nor to file but I wont have runtime exceptions, because TS hints me to catch the undefined case with the null'ish concealing") ?? "");

(In case you are interested, here is my tsconfig.json)

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist",
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "baseUrl": "."
  },
  "ts-node": {
    "transpileOnly": true
  }
}

Answer №3

If you're struggling with Rollup and trying to eliminate those errors, you can also prevent the invocation of Factory.createDebugStatement() by passing Factory.createDebugStatement as a reference.

For example:

Factory.createDebugStatement = (arg1, arg2) => {
  return `${arg1}-${arg2}`;
};

function log(statement: string, ...args: any[]): void {
  if (debug) {
    console.log(statement.replace('%s', args.map((arg) => (typeof arg === 'function' ? arg() : arg)).join(''));
  }
}

// somewhere in the application
log('hi %s mom!', Factory.createDebugStatement.bind(null, 'hello', 'world'));
// Output: hi hello-world mom!

If your Factory.createDebugStatement does not require any arguments, you can simply use...

log('hi %s mom!', Factory.createDebugStatement);

The above code snippet will not trigger Factory.createDebugStatement if debug=false. This approach aligns with the 'Build Once Deploy Many' strategy.

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

Using a Typescript typeguard to validate function parameters of type any[]

Is it logical to use this type of typeguard check in a function like the following: Foo(value: any[]) { if (value instanceof Array) { Console.log('having an array') } } Given that the parameter is defined as an array o ...

In Vue3, automatically infer a type for a slotProp based on the modelValue

In simplifying the component I aim to create, I have created the following code structure: // MyComp.vue <script setup lang="ts"> import { PropType, defineProps, defineEmits } from 'vue'; const props = defineProps({ modelVal ...

Is it possible to configure a unique Bearer Access Token in the "angular-oauth2-oidc" library?

For my Facebook login, I have set up a custom endpoint where the client sends the Facebook access token. In my Ionic App, I use the '@ionic-native/facebook/ngx' package to retrieve this token. Within a Laravel Json API controller, I utilize Soci ...

Unable to swap out string with text box in TypeScript

I am trying to swap __ with a text box in Angular 2/4. Take a look at the example provided in the link below. https://stackblitz.com/edit/angular-ajkvyq?file=app%2Fapp.component.ts ...

Learn how to trigger an HTTP exception after a failed command in a saga with NestJS CQRS

Currently utilizing the NestJS CQRS pattern to handle interactions between User and UserProfile entities within my system. The setup consists of an API Gateway NestJS server along with dedicated NestJS servers for each microservice (User, UserProfile, etc. ...

What are the steps to set up NextJS 12.2 with SWC, Jest, Eslint, and Typescript for optimal configuration?

Having trouble resolving an error with Next/Babel in Jest files while using VSCode. Any suggestions on how to fix this? I am currently working with NextJS and SWC, and I have "extends": "next" set in my .eslintrc file. Error message: Parsing error - Can ...

Tips for implementing <mat-progress-bar> in .ts file when making API service requests with Angular

I'm currently utilizing an API call to retrieve an image from a service, and I would like to display a progress bar while the image is being fetched. It seems that I need to incorporate the progress bar within the service as the image data is returned ...

Troubleshooting a Type Parameter Error in React Native TypeScript

I am working on a project in React Native using TypeScript, and I encountered this issue: I am getting the error Argument of type 'GestureResponderEvent' is not assignable to parameter of type 'SetStateAction<string>'.ts(2345) wit ...

Automatically update data in Angular without the need to refresh the page

One feature of my application involves displaying a table with rows retrieved from a database. The functionality responsible for fetching this data is an AJAX call, implemented as follows: getPosts(): Observable<Posts[]> { return this.http.post ...

Troubleshooting React using breakpoints: A step-by-step guide

Currently I am working with Chrome and facing an issue. For instance, I require Component.jsx and want to set a breakpoint at a specific location. When I press F12, navigate to the Sources tab, and use Ctrl+P to search, I only find 1.chunk.js, bundle.js, ...

What is the best way to rekindle the d3 force simulation within React's StrictMode?

Creating an interactive force directed graph in React using D3 has been successful except for the dragging functionality not working in React StrictMode. The issue seems to be related to mounting and remounting components in ReactStrict mode 18, but pinpoi ...

The image map library functions seamlessly with React but encounters issues when integrated with Next.js

I have been working on a client project that requires the use of an image map. I searched for a suitable library, but struggled to find one that is easy to maintain. However, I came across this particular library that seemed promising. https://github.com/ ...

Error message in Ionic 2: "Property is not found on type"

Currently, I am working on a project in Ionic 2 and have encountered a stumbling block with a seemingly simple task. My issue lies with a Textbox where I aim to input text that will then be displayed. I found some code on a website (http://www.tizag.com/j ...

Guide on navigating to a specific step within a wizard using Vue and TypeScript

In this wizard, there are 6 steps. The last step includes a button that redirects the user back to step 4 when clicked. The user must then complete steps 5 and 6 in order to finish the wizard. step6.ts <router-link to="/stepFour" ...

Issue encountered when importing a font in TypeScript due to an error in the link tag's crossorigin

How do I troubleshoot a TypeScript error when importing a custom font, such as a Google font? <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> Below is the specific error message: Type 'boolean' is ...

How can I remove a row from a mat table using Angular?

Having trouble implementing *ngFor in my angular mat table, seeking guidance from someone with more expertise? I am trying to delete a row within an array using a button and display it on my table, but encountering issues. I intend to utilize *ngFor to sh ...

What could be causing the "no exported member" errors to appear when trying to update Angular?

The dilemma I'm facing a challenge while attempting to upgrade from Angular V9 to V11. Here are the errors that I am encountering: Namespace node_module/@angular/core/core has no exported member ɵɵFactoryDeclaration Namespace node_module/@angular/ ...

Angular 2 error: "unknown element" issue persists despite exhausting all attempted solutions

Here's a typical scenario where I attempt to incorporate a component from another module : External component : import { Component, ViewEncapsulation, ElementRef, ViewChild, Input, Output, EventEmitter } from '@angular/core'; declare ...

What distinguishes between a public variable declared as var: any = []; versus a public variable declared as var: any[] =

I'm seeking clarification on the distinction between the following: public var: any = []; // versus public var: any[] = []; ...

What could be causing the issue with my customized sorting in the Material-UI Data Grid component?

After integrating Material-UI's Data Grid Component with my API's JSON array, I had to create a RenderCell function to handle text overflow and include a button that directs Users to a different page. Additionally, I utilized the ValueGetter for ...