3

I want to create a library using Typescript. This library can be used inside Node and browser environments, so the configuration supplies support for both

( tsconfig.json )

{
  "compilerOptions": {
    "baseUrl": ".",
    "declaration": true,
    "esModuleInterop": true,
    "lib": [ "esnext", "dom" ],
    "module": "commonjs",
    "outDir": "dist",
    "resolveJsonModule": true,
    "strict": true,
    "target": "es2019",
  },
  "include": [
    "./**/*.ts"
  ],
  "exclude": [
    "./dist"
  ]
}

I'm using esbuild as a bundler. The package.json contains

{
  "name": "my-lib",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc && esbuild ./dist/index.js --bundle --minify --sourcemap --outfile=./bundle/index.js"
  },
  "dependencies": {
    "esbuild": "0.14.36"
  },
  "devDependencies": {
    "typescript": "4.6.3"
  }
}

The library makes use of some "private" helper functions, I'm using the following sample code

import { TextEncoder } from 'util';

const encodeInput = function (input: string): Uint8Array {
  const textEncoder = new TextEncoder();

  return textEncoder.encode(input);
}

Running the build script command throws an esbuild error telling me that this only works for Node environments, not in the browser, which makes sense because

How can I make sure this library works in "both" worlds?

  • Have you looked into this https://stackoverflow.com/questions/40748249/creating-a-typescript-library-that-can-be-used-in-browser-and-nodejs it talks about using `UMD` to achieve this. – Sebastian Gbudje Apr 15 '22 at 16:32

2 Answers2

3

TextEncoder is a global in Node, so you don't need to import it. Use it directly, without the import statement, just like in the browser.

You also don't need to instantiate a new TextEncoder every time the function is invoked: it is a small performance optimization to instantiate it once, and then just alias the encode method. See below.

const encoder = new TextEncoder();
const encode = encoder.encode.bind(encoder);

console.log(encode('hello world'));
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • and what about other classes e.g. the crypto API? As mentioned here https://stackoverflow.com/questions/71856643/make-use-of-the-textencoder-class-in-node-and-browser-environments#comment127070979_71909011 there is a Node crypto API and a webcrypto API – Question3r Apr 20 '22 at 12:30
  • 1
    @Question3r This question is about `TextEncoder`. (However, [`crypto` is also a global](https://nodejs.org/api/globals.html#crypto_1) in Node versions >=`17.6.0`.) You are always welcome to [ask a new question](https://stackoverflow.com/questions/ask) for another topic. – jsejcksn Apr 20 '22 at 14:17
0

You can make use of the UMD (Universal Module Definition) tool to get around this problem.

In your tsconfig.json, just change module: "commonjs", to module: "umd", and it should build properly from there to work in all environments.

Barry Michael Doyle
  • 9,333
  • 30
  • 83
  • 143
  • but this doesn't work with the webcrypto api right? Is this package obsolete https://www.npmjs.com/package/one-webcrypto ? – Question3r Apr 18 '22 at 10:56
  • @Barry I'm not sure. This might work but third party packages don't work anymore ( they are related to the node resolution strategy ) –  Apr 19 '22 at 11:34