Coder Social home page Coder Social logo

esbuild-node-loader's Introduction

Deprecated. This package has been moved to https://github.com/esbuild-kit/esm-loader


esbuild-node-loader

NPM version

Transpile TypeScript to ESM with Node.js loader hooks on the fly.

Inspired by esbuild-register but for Node.js ESM instead of CJS.

Features

  • Supports .ts, .tsx and .json
  • Support extension also resolving (can import modules without extension)

Usage

npm i esbuild-node-loader -D
node --experimental-loader esbuild-node-loader file.ts

Or use with esmo:

npx esmo file.ts

Sponsors

License

MIT License ยฉ 2021 Anthony Fu

esbuild-node-loader's People

Contributors

antfu avatar dominikg avatar elianiva avatar hanayashiki avatar kamiazya avatar loynoir avatar nettybun avatar userquin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

esbuild-node-loader's Issues

Question about importing ts files with ESM .js imports

I have a project written in TypeScript that tries to compile to ESM. It's the future, we're on-board, idk, let's go.

entry.ts

import hi from "./dep.js"
hi()

dep.ts

export default () => console.log("hi")

This doesn't work:

(node:12382) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/errors:463
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/alextes/code/js-notepad/dep.js' imported from /Users/alextes/code/js-notepad/entry.ts
    at new NodeError (node:internal/errors:370:5)
    at finalizeResolution (node:internal/modules/esm/resolve:321:11)
    at moduleResolve (node:internal/modules/esm/resolve:756:10)
    at defaultResolve (node:internal/modules/esm/resolve:867:11)
    at resolve (file:///Users/alextes/code/js-notepad/node_modules/esbuild-node-loader/loader.mjs:26:10)
    at Loader.resolve (node:internal/modules/esm/loader:89:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
    at link (node:internal/modules/esm/module_job:75:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

If I understand correctly, TypeScript strongly feels imports should be written like above. They say "write valid JS first, TypeScript is on top, we'll make sure it works". Indeed my build, and running it with node works great. Buuuuut, I don't want to build all the time, so I use these nifty pre-compilers / loaders, whatever they are.

I thought the purpose of this module was to make that work. I'm clearly misunderstanding something. Could you clarify what this module is trying to do?

Support file loader?

I use it in pair with uvu to transpile typescript. When I'm trying to test a file than imports, for instance, a png file (which is set up as --loader:.png=file in my esbuild cli), node throws with

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".png" for /blabla/star.png
    at defaultGetFormat (internal/modules/esm/get_format.js:71:15)
    at getFormat (file://node_modules/esbuild-node-loader/loader.mjs:162:10)

It's obviously not a problem of this loader, but I'm stuggling to understand how to properly apporach it. Since esbuild supports file loader, maybe it makes sense to somehow support it inside this loader? Or it's better to chain loaders via https://github.com/lukeed/loadr and provide some fake one for certain extensions. I'd appreciate some guidance

Support for `.js` extension

Not sure if it is the job of this package to handle .js files. However, I am using this package to run my tests and using tsc to create the distribution build.

TypeScript forces the import file extension to be .js and not .ts, which does not work with this package. For example:

Following works with esbuild-node-loader, but tsc does not compile it

import Foo from './foo.ts'

Following works with tsc, but not with esbuild-node-loader.

import Foo from './foo.js'

esbuild plugin support

Hello,

I'd like to use esbuild-node-loader with Linaria but it requires to use a plugin for esbuild.

Would be cool for this loader to support plugins (if it's possible)

Resolve tsconfig from current working dir

According to the esbuild docs here, the transform API doesn't read from the filesystem, and doesn't resolve the tsconfig. I'm using MobX, and I need to define useDefineForClassFields in tsconfig for it to work correctly, but esbuild node loader doesn't pick it up.

The fix here would be to resolve it and pass it as tsconfigRaw:

  const {
    code: js,
    warnings,
    map: jsSourceMap,
  } = transformSync(rawSource.toString(), {
    sourcefile: filename,
    sourcemap: 'both',
    loader: new URL(url).pathname.match(extensionsRegex)[1],
    target: `node${process.versions.node}`,
    format: format === 'module' ? 'esm' : 'cjs',
+   tsconfigRaw: resolveTsConfig(),
  })
function resolveTsConfig(fromDir = process.cwd()) {
  const tsconfigPath = join(fromDir, 'tsconfig.json')
  if (fs.existsSync(tsconfigPath)) {
    return fs.readFileSync(tsconfigPath, 'utf8')
  }

  const parentDir = dirname(fromDir)
  if (parentDir === fromDir) return undefined

  return resolveTsConfig(parentDir)
}

Why `https` import support?

Hey, thanks for building the package!

Re: #34

I've looked at the code and I think it's a little dangerous to allow https imports without even mentioning anything in the README.md. I discovered this by an accident as I was trying to understand how to configure stuff. I tested this behaviour and seems to be working and I was able to import an arbitrary JS file hosted on arbitrary domain using this loader.

I'm new to this concept and I understand that there is Deno and --experimental-network-imports in Node but I didn't expect this behaviour from a small loader library. I also understand that this is how browsers work, but they have a ton of security around that, like CORS and policies you can setup to allow only certain domains.

I feel this at least should be mentioned in the README.md so people who care about security maybe consider this before they adopt the package.

Is it really crucial for this library to support it? How do I disable it? How can I allow only certain domains I trust?

Cheers!

tsconfig paths support?

I'm not sure that this module is trying to make compatible with tsconfig.json

When I use esmo, I got some error like this

esmo ./folder/file.ts

My tsconfig.json

{
"paths": {
      "@/*": ["./src/*"],
      "~/*": ["./*"]
  }
}

Because there is

import { func } from '@/module'

This error occurs

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/parsers' imported from

Is there a plan to resolve this issue or support usage like this?

Unknown file extension ".tsx"

When trying to run node --loader=esbuild-node-loader index.test.tsx I get this error:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx" for /home/v1rtl/Coding/use-onboard/tests/index.test.tsx

my tsconfig:

{
  "include": ["src"],
  "compilerOptions": {
    "module": "ESNext",
    "target": "ES2019",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "isolatedModules": true,
    "preserveSymlinks": true,
    "noEmit": true,
    "declaration": true,
    "declarationDir": "dist",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "jsx": "react",
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "noFallthroughCasesInSwitch": true
  },
  "exclude": ["node_modules"]
}

Help on Windows Support

The current implementation failed on Windows, while unfortunately I don't have a Windows to test out. Would use some help on the fix. Thanks.

Incorrect line numbers in stack traces

When running with this loader, stack trace errors are incorrect.

Recipe

Create a basic test file that has a comment in it, like so:

$ cat test.ts
//
// This is a comment
//

throw Error('BLARG')

Run using bare node.js and notice the the log output (correctly) says the error is on line 5:

$ node test.ts
/private/tmp/_util/test.ts:5
throw Error('BLARG')
^

Error: BLARG
    at Object.<anonymous> (/private/tmp/_util/test.ts:5:7)
    ...

Now run using this loader, and notice that the error is logged as appearing on line 1:

$ node --loader=esbuild-node-loader test.ts
(node:72676) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
file:///private/tmp/_util/test.ts:1
throw Error("BLARG");
      ^

Error: BLARG
    at file:///private/tmp/_util/test.ts:1:7
...

Environment

$ npx envinfo --system --browsers --npmPackages --binaries

  System:
    OS: macOS 12.0.1
    CPU: (8) x64 Apple M1
    Memory: 20.41 MB / 16.00 GB
    Shell: 5.1.8 - /usr/local/bin/bash
  Binaries:
    Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
    Yarn: 1.22.17 - ~/.nvm/versions/node/v16.14.0/bin/yarn
    npm: 8.3.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
    Watchman: 2022.02.14.00 - /usr/local/bin/watchman
  Browsers:
    Chrome: 100.0.4896.75
    Firefox: 98.0.2
    Safari: 15.1
  npmPackages:
    esbuild-node-loader: 0.8.0 => 0.8.0

Error: the URL must be of a scheme file

When I run my file with esmo or node --loader=esbuild-node-loader I get the following error:

$ pnpx esmo src/index.ts
(node:1316088) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/errors:464
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file
    at new NodeError (node:internal/errors:371:5)
    at fileURLToPath (node:internal/url:1372:11)
    at resolve (file:///home/ohmree/code/js/contest_buddy/node_modules/.pnpm/[email protected]/node_modules/esbuild-node-loader/loader.mjs:20:16)
    at Loader.resolve (node:internal/modules/esm/loader:89:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
    at link (node:internal/modules/esm/module_job:75:36) {
  code: 'ERR_INVALID_URL_SCHEME'
}

Where the contents of index.ts are as follows:

import fs from 'node:fs';
import path from 'node:path';
import {App} from '@tinyhttp/app';
import {logger} from '@tinyhttp/logger';
import {json} from 'milliparsec';
import {PrismaClient} from '@prisma/client';
import clientViteConfig from 'client/vite.config';
import {createServer as createViteServer} from 'vite';
import sirv from 'sirv';

const prisma = new PrismaClient();

const app = new App();
const api = new App();
const isProd = process.env['NODE_ENV'] === 'production';

(async () => {
  api
    .use(json())
    .get('/users', async (_request, response) => {
      response
        .status(200)
        .json(await prisma.user.findMany())
        .end();
    })
    .get('/users/:id', async (request, response) => {
      const user = await prisma.user.findUnique({
        where: {id: request.params['id']}
      });
      if (user) {
        response.status(200).json(user).end();
      } else {
        response.status(404).json({message: 'User not found'}).end();
      }
    });

  app.use(logger()).use('/api', api);

  if (isProd) {
    app.use(sirv(path.resolve(__dirname, '../client')));
  } else {
    const viteServer = await createViteServer({
      server: {
        middlewareMode: 'ssr',
        cors: true,
        hmr: true
      },
      ...clientViteConfig
    });
    console.log(viteServer.config.server);
    app.use(viteServer.middlewares);
    app.use('*', async (request, response) => {
      const url = request.originalUrl;
      try {
        const template = fs.readFileSync(
          path.resolve(__dirname, '../../client/index.html'),
          'utf-8'
        );
        const appHtml = await viteServer.transformIndexHtml(url, template);
        response.status(200).set({'Content-Type': 'text/html'}).end(appHtml);
      } catch (_error: unknown) {
        const error = _error as Error;
        viteServer.ssrFixStacktrace(error);
        console.error(error);
        response.status(500).end(error.message);
      }
    });
  }

  app.listen(3000);
})();

Is this a bug in esbuild-node-loader?

A mode that disallows .ts and no extension files

Hello Again ๐Ÿ‘‹

I use esbuild-node-loader mainly for running tests and development and still rely on TypeScript tsc for creating the final distribution build.

Quite often I run into this problem where I forget to add extension .js to an imported file, run my tests using esbuild-node-loader and everything works fine. Then I create the distribution build, publish the package on npm and boom everything breaks because the tsc does not rewrite import extensions.

So I thought to check with you and see if there can be a mode that only allows .js extensions, so that the code can break early in the development lifecycle

bug ERR_UNSUPPORTED_DIR_IMPORT

test in #9

> [email protected] generate
> node --experimental-loader esbuild-node-loader src/schema.ts

(node:90371) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/errors:464
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/Users/alexanderniebuhr/Documents/projects/frickegroup/apollo/src/graphql' is not supported resolving ES modules imported from /Users/alexanderniebuhr/Documents/projects/frickegroup/apollo/src/schema.ts
    at new NodeError (node:internal/errors:371:5)
    at finalizeResolution (node:internal/modules/esm/resolve:385:17)
    at moduleResolve (node:internal/modules/esm/resolve:867:10)
    at defaultResolve (node:internal/modules/esm/resolve:978:11)
    at resolve (file:///Users/alexanderniebuhr/Documents/projects/frickegroup/apollo/node_modules/esbuild-node-loader/loader.mjs:30:10)
    at Loader.resolve (node:internal/modules/esm/loader:89:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
    at link (node:internal/modules/esm/module_job:75:36) {
  code: 'ERR_UNSUPPORTED_DIR_IMPORT',
  url: 'file:///Users/alexanderniebuhr/Documents/projects/frickegroup/apollo/src/graphql'

ERR_INVALID_FILE_URL_HOST with --experimental-specifier-resolution=node

I have a TypeScript yarn monorepo, and run things with --experimental-specifier-resolution=node and get this error:

TypeError [ERR_INVALID_FILE_URL_HOST]: File URL host must be "localhost" or empty on darwin
    at statSync (node:fs:1529:10)
    at fileExists (node:internal/modules/esm/resolve:258:10)
    at resolveExtensions (node:internal/modules/esm/resolve:333:9)
    at resolveExtensionsWithTryExactName (node:internal/modules/esm/resolve:320:10)
    at finalizeResolution (node:internal/modules/esm/resolve:372:16)
    at moduleResolve (node:internal/modules/esm/resolve:893:10)
    at defaultResolve (node:internal/modules/esm/resolve:1004:11)
    at resolve (file:///Users/adelnizamutdinov/Projects/diagrams/node_modules/esbuild-node-loader/loader.mjs:30:10)
    at Loader.resolve (node:internal/modules/esm/loader:89:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)

Happens here

// Let Node.js handle all other specifiers.
return defaultResolve(specifier, context, defaultResolve)

So we let Node handle stuff and it wants localhost? I have no idea what's happening here

I'd love to open a PR but have no idea where to start

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.