• Jobs
  • About Us
  • professionals
    • Home
    • Jobs
    • Courses and challenges
  • business
    • Home
    • Post vacancy
    • Our process
    • Pricing
    • Assessments
    • Payroll
    • Blog
    • Sales
    • Salary Calculator

0

239
Views
Are Native Node Absolute (ie. Root-Relative) Paths Possible With ES Module Named Imports?

Normally in Node one can useNODE_PATH=./src" to make it so that instead of:

import { foo } from '../../../bar'

You can just do:

import { foo } from 'src/bar'

However, that only works if you use the esm package (ie. node -r esm): NODE_PATH doesn't work with native ES Modules (ie. adding "type": "module" to package.json) ... so what is the modern replacement?

I've tried all of the following, and none of them seem to work (though I may not be using them correctly, and would welcome any clarification):

  • local files (ie. `"dependencies": { "src": "file:./src",) - couldn't get this to even work
  • symlinks (ie. adding a symlink from node_modules/src to project-root/src) - that imports the file as a CommonJS package, not an ES one, which means that named imports don't work
  • workspaces (ie. "workspaces": ["src"], in package.json) - same issue: no named imports
  • imports (ie. "imports": {"#src": "./src"}) - ignores the --experimental-specifier-resolution=node flag (so it only works IF I want to go through and manually add .js to every import in my project)
  • custom loaders (ie. making a loader.js file and using node --loader loader.js) - I couldn't figure out how to make this work, as there is almost no documentation on custom loaders

Ideally, I'd prefer not to have to implement all of Babel/Webpack/Typescript/etc. on my project, just to replace NODE_PATH=./src, but it seems like adding some such tool is the only way now?

almost 3 years ago · Santiago Trujillo
1 answers
Answer question

0

It looks like the only viable option ... if you want root-relative imports AND you don't want to specify the .js extension ... is to use a custom loader.

For reference, the one I made to achieve this was:

import path from 'path';
import fs from 'fs';

export function resolve(originalSpecifier, context, defaultResolver) {
  let specifier = originalSpecifier;

  try {
    // All my root-relative imports start with "src/"; if you 
    // have other folders you'll need to account for them here
    if (specifier.startsWith('src')) {
      specifier = specifier.replace(/^src/, path.resolve('.') + '/src');
      // If the import is for a directory, get its index.js file
      const itExists = fs.existsSync(specifier);
      let isDirectory = false;
      try {
        isDirectory = fs.lstatSync(specifier).isDirectory();
      } catch (err) {}
      specifier = itExists && isDirectory ? `${specifier}/index` : specifier;

      // Add the ".js" extension if not specified
      specifier += specifier.endsWith('.js') ? '' : '.js';

      return {
        format: 'module',
        url: new URL(specifier, context.parentURL).href,
      };
    }
  } catch (err) {
    console.error(err);
  }
  // If we're not handling our special cases, just use the
  // default handler
  return defaultResolver(specifier, context);
}

Then you can use that loader with the --loader option, eg.

node --loader loader.js index.js

However, it's worth noting that the loader stuff is still under development, and could change (making the above loader invalid) in the future. Probably sometime in 2030, given how slow the Node org is with their development ;)

almost 3 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Our process Sales
Legal
Terms and conditions Privacy policy
© 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recommend me some offers
I have an error