Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native ESM support, possibly requiring integration with ts-node #122

Closed
cspotcode opened this issue May 3, 2020 · 8 comments
Closed

Native ESM support, possibly requiring integration with ts-node #122

cspotcode opened this issue May 3, 2020 · 8 comments

Comments

@cspotcode
Copy link
Collaborator

In ts-node we released experimental support for native ECMAScript modules. To do this we needed to implement a custom resolve() loader hook. It is a copy-paste of node's built-in resolver, tweaked for our needs.

TypeStrong/ts-node#1007

Is native ESM support on your radar? Do you have thoughts on how best to implement it for tsconfig-paths? I expect we will need to coordinate, since node only supports a single loader hook.

I see that the compiler itself can perform these resolutions via ts.resolveModuleName. Do you use that internally, or do you do something else?

@desmap
Copy link

desmap commented Jul 17, 2020

just posted a similar issue but I am not sure if this repo is still maintained...

@cspotcode
Copy link
Collaborator Author

@desmap you can be the one to do it!

You can also stop using path mappings in your project. Probably wouldn't be much work since it's some find-and-replace of the import statements.

What benefits do you hope to get from switching to native ESM? You'll be forcing your users to install a non-standard path mapping hook.

@desmap
Copy link

desmap commented Jul 26, 2020

What benefits do you hope to get from switching to native ESM?

It'd def debatable but I'd like it, check yarnpkg/berry#638 (comment) and yarnpkg/berry#638 (comment) for examples

@jacobq
Copy link

jacobq commented Sep 10, 2021

I had trouble getting module path mappings to work with ESM (using tsconfig-paths' register function -- pre-ESM I did it this way: #157 (comment)). What finally worked for me was using a custom loader (thanks for the tip/example @geigerzaehler TypeStrong/ts-node#1450 (comment))

To use the loader code below, pass it to node via --loader option, e.g. node --loader loader.js main.js.

Custom loader code
// (loader.ts)
// Uses tsconfig.json and createMatchPath (from tsconfig-paths lib) to implement a custom loader
// (resolve function) that applies path mappings like `@src/foo` --> `/path/to/app/build/src/foo.js`
// See also https://github.com/TypeStrong/ts-node/discussions/1450#discussion-3563207
import { existsSync } from 'fs';
import { readFile } from 'fs/promises';
import { basename, dirname, resolve as pathResolve } from 'path';
import { fileURLToPath } from 'url';

import { createMatchPath } from 'tsconfig-paths';

const __dirname = dirname(fileURLToPath(import.meta.url));

async function getTSConfig() {
  const maxDepth = 32; // arbitrary
  let depth = 0;
  let tsConfigFile = pathResolve(__dirname, 'tsconfig.json');
  while(!existsSync(tsConfigFile)) {
    tsConfigFile = pathResolve(dirname(tsConfigFile), '..', basename(tsConfigFile));
    depth++;
    if (depth > maxDepth) {
      throw Error(`maxDepth (${maxDepth}) exceeded while searching for tsconfig.json`);
    }
  }
  return JSON.parse(await readFile(tsConfigFile, 'utf-8'));
}

const getMatchPathPromise = (async () => {
  const tsConfig = await getTSConfig();
  const baseUrl = tsConfig.compilerOptions.baseUrl || '.';
  const outDir = tsConfig.compilerOptions.outDir || '.';
  const absoluteBaseUrl = pathResolve(baseUrl, outDir);
  const paths = tsConfig.compilerOptions.paths;
  return createMatchPath(absoluteBaseUrl, paths);
})();

export async function resolve(specifier, context, defaultResolve) {
  const matchPath = await getMatchPathPromise;
  const mappedSpecifier = matchPath(specifier)
  if (mappedSpecifier) {
    specifier = `${mappedSpecifier}.js`
  }
  return defaultResolve(specifier, context, defaultResolve);
}

@dobromyslov
Copy link

@jacobq Also have a look at this solution TypeStrong/ts-node#1450
It's rather simple because it extends native ts-node/esm loader.

@jacobq
Copy link

jacobq commented Dec 17, 2021

@jacobq Also have a look at this solution TypeStrong/ts-node#1450 It's rather simple because it extends native ts-node/esm loader.

Yes, I saw it (hence why I mentioned it in my comment 😉). I probably should've mentioned that I am not actually using ts-node though.

@dobromyslov
Copy link

I wanted to mention new solution created by @charles-allen 4 days ago (TypeStrong/ts-node#1450 (comment))

D-Pow added a commit to D-Pow/react-app-boilerplate that referenced this issue Jan 12, 2022
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules.

See:

Setup
* https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2

Issues with `ts-node`, ESM, and aliases
* TypeStrong/ts-node#1007
    - TypeStrong/ts-node#476
    - dividab/tsconfig-paths#122 (comment)
    - TypeStrong/ts-node#1450 (comment)
* TypeStrong/ts-node#1414
* TypeStrong/ts-node#995
    - TypeStrong/ts-node#639

Node issues with ESM
* https://nodejs.org/api/packages.html#determining-module-system
* nodejs/node#37468
D-Pow added a commit to D-Pow/react-app-boilerplate that referenced this issue Jan 12, 2022
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules.

See:

Setup
* https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2

Issues with `ts-node`, ESM, and aliases
* TypeStrong/ts-node#1007
    - TypeStrong/ts-node#476
    - dividab/tsconfig-paths#122 (comment)
    - TypeStrong/ts-node#1450 (comment)
* TypeStrong/ts-node#1414
* TypeStrong/ts-node#995
    - TypeStrong/ts-node#639

Node issues with ESM
* https://nodejs.org/api/packages.html#determining-module-system
* nodejs/node#37468
D-Pow added a commit to D-Pow/react-app-boilerplate that referenced this issue Jan 12, 2022
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules.

See:

Setup
* https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2

Issues with `ts-node`, ESM, and aliases
* TypeStrong/ts-node#1007
    - TypeStrong/ts-node#476
    - dividab/tsconfig-paths#122 (comment)
    - TypeStrong/ts-node#1450 (comment)
* TypeStrong/ts-node#1414
* TypeStrong/ts-node#995
    - TypeStrong/ts-node#639

Node issues with ESM
* https://nodejs.org/api/packages.html#determining-module-system
* nodejs/node#37468
@cspotcode
Copy link
Collaborator Author

Closing in favor of TypeStrong/ts-node#1585 which will add native path mapping support to ts-node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants