Modules in Bundlers

Do I need to specify file extensions when importing modules?

The need to specify file extensions in import statements depends on your runtime and compilation environment, and also on whether you are using ES modules (the import syntax) or CommonJS modules (the require syntax)

  • Webpack (used by Create React App and others tools): If the path has a file extension, then the file is bundled straightaway. Otherwise, the file extension is resolved using the resolve.extensions option, which tells the resolver which extensions are acceptable for resolution, e.g., .js.jsx.
  • ES modules with Node.js or in the browser without any compilation step: A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. This behavior matches how import behaves in browser environments.
  • CommonJS modules with Node.js: If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js.json, and finally .node.
  • TypeScript: When using '--moduleResolution' with the option 'nodenext', it is necessary to add explicit file extensions to relative import paths in EcmaScript imports. However, TypeScript was implemented based on the import syntax proposal before ES6 was finalized, so they followed node module (commonjs) behavior which guesses file extensions.

How does Webpack module resolution play together with TypeScript module resolution?

Webpack and TypeScript use different module resolution strategies, but they can work together seamlessly to build and bundle projects. Understanding how each tool handles module resolution is crucial for efficient development.

  1. TypeScript Module Resolution:
    • TypeScript has its own module resolution logic, which includes resolving module paths based on the baseUrl, paths, and rootDirs settings in the tsconfig.json file.
    • TypeScript can resolve both relative and non-relative module paths using these configurations.
    • TypeScript’s resolution is mainly used during the TypeScript compilation process to generate valid JavaScript code.
    • TypeScript supports different module systems, including CommonJS and ECMAScript Modules (ESM).
  2. Webpack Module Resolution:
    • Webpack also has its module resolution logic, which is responsible for locating and bundling modules during the build process.
    • Webpack can handle a variety of module formats, including CommonJS, AMD, and ES6 modules (ESM).
    • It uses the resolve configuration to determine how to locate and load modules, including specifying file extensions, aliasing paths, and defining module directories.
    • Webpack’s module resolution is applied at runtime when the bundled application is executed in the browser or another runtime environment.
  3. Working Together:
    • During development, TypeScript and Webpack can complement each other. TypeScript handles type checking and transpilation, and Webpack bundles the compiled JavaScript files along with their dependencies for deployment.
    • Webpack extends the module resolution capabilities of TypeScript by allowing for additional configurations and optimizations during the bundling process.
    • Ensure that TypeScript and Webpack configurations are aligned to avoid conflicts. For example, file extensions and paths configured in Webpack’s resolve should match TypeScript’s settings to prevent unexpected resolution issues.
  4. Common Configurations:
    • It’s a good practice to have common configurations for paths, extensions, and aliases in both TypeScript and Webpack to maintain consistency and prevent potential issues.
  5. Webpack ts-loader:
    • When using TypeScript with Webpack, the ts-loader is commonly employed to integrate TypeScript compilation into the Webpack build process. This loader utilizes the TypeScript compiler to transpile TypeScript code and works in harmony with Webpack’s module resolution.
  6. Using Webpack Aliases in TypeScript:
    • If you have defined aliases in Webpack’s resolve.alias, you might want to mirror them in TypeScript using paths in tsconfig.json to avoid discrepancies in module resolution.

How do I reference modules that have a custom TypeScript path?

To reference modules with a custom TypeScript path, you typically use the paths configuration in the tsconfig.json file. This allows you to define custom paths that map to specific directories, making it easier to reference and import modules in your TypeScript code.

Update tsconfig.json:

  • Open your tsconfig.json file and add or update the paths property to include your custom paths. The paths property is an object where keys represent the module names, and values represent the corresponding paths.
  • The baseUrl configuration in tsconfig.json specifies the base directory for resolving non-relative module names. Make sure it is set appropriately for your project.
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@myModule/*": ["app/*"],
      "@utils/*": ["utils/*"]
      // Add more custom paths as needed
    },
    // Other compiler options...
  },
  // Other settings...
}

Organize your project structure according to the defined paths. For example, if you have the following directory structure:

src/
  app/
    moduleA.ts
    moduleB.ts
  utils/
    utilityModule.ts

In your TypeScript files, you can now use the custom paths to reference and import modules.

// In a file in the app directory
import { something } from '@myModule/moduleA';

// In a file in the utils directory
import { utilityFunction } from '@utils/utilityModule';

Webpack or Other Build Tools:

  • If you’re using a bundler like Webpack, ensure that the bundler’s configuration also recognizes and respects the custom paths. Webpack has its own configuration for module resolution (resolve.alias), and it should be aligned with the TypeScript configuration.

About Author

Mathias Bothe To my job profile

I am Mathias from Heidelberg, Germany. I am a passionate IT freelancer with 15+ years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I create Bosycom and initiated several software projects.