fp-ts icon indicating copy to clipboard operation
fp-ts copied to clipboard

Simplify build in 3.0.0

Open m-bock opened this issue 4 years ago • 8 comments

Version 3.0.0 comes with an improved build process. Currently it does some code patching of import statements, as the output file structure gets modified. The reason for this is that every module needs to exists inside a directory, as it contains a "packaga.json", and source files for "lib", "es6" as well as "d.ts" typings.

E.g. see: https://github.com/gcanti/fp-ts/blob/3.0.0/scripts/build2.ts#L49

The path of the "Alt" module is rewritten to "Alt/Alt".

I was wondering if the code rewriting can be omitted when paths are normalized like so: src/foo/bar/baz.ts -> dist/foo/bar/baz/index.js

Looks like less intrusive, but maybe I oversee something.

m-bock avatar Jul 03 '21 07:07 m-bock

Hi @gcanti, if you think the distinction between lib/ vs es6/ to be an implementation detail and want to hide it away, Node.js's subpath exports and conditional exports might help the situation.

Instead of placing package.json per subdirectory, recent versions of Node.js offers subpath exports, a concise and standard (important!) way to specify how the library should be imported. (Webpack 5 also supports it.) This will prevent a library consumer from importing fp-ts/lib/* and force to import from fp-ts/*.

In addition to that, thanks to conditional exports, lib/ and es6/ will automatically be selected.

If we adopt them, the package.json on the package root will look like this:

{
  "exports": {
    "import": "./es6/index.js",
    "require": "./lib/index.js",
    "./Alt": {
      "import": "./es6/Alt.js",
      "require": "./lib/Alt.js"
    },
    // ...
  }
}

Though the current "multiple package.json" style works well, there is a problem decreasing developers' experience: the language server inserts an import statement not from fp-ts/* but from fp-ts/lib/*. I'm not sure the if language server already supports these features (I believe it does though), this change should make it easier to interact with fp-ts. So why not moving to it on v3?

ryota-ka avatar Sep 08 '21 08:09 ryota-ka

^ I was playing about with this for fp-ts-std. I haven't tested it much yet but it's worth seeing if we could take advantage of wildcards in conditional exports:

{
  "exports": {
    "./*": {
      "require": "./lib/*.js",
      "import": "./es6/*.js"
    }
  }
}

Edit: In use over there as of 0.12!

samhh avatar Sep 08 '21 10:09 samhh

@ryota-ka Great suggestion! This is one of the things I would really like to see in fp-ts@3

raveclassic avatar Sep 08 '21 12:09 raveclassic

I also think this makes a lot of sense to do. Typescript should understand package.json exports in their next release. https://github.com/microsoft/TypeScript/issues/45418

TylorS avatar Sep 11 '21 01:09 TylorS

@ryota-ka @samhh thanks for your suggestions, just released [email protected] (npm i fp-ts@next), let me know if it's working as expected

gcanti avatar Mar 23 '22 10:03 gcanti

@gcanti I tried it with typescript v4.6.3 and webpack v5.70.0.

  • "Go to definition" in VSCode works nice both with --module=CommonJS and --module=ESNext --moduleResolution=Node
  • Node.js also loads the correct module when using CommonJS
  • Webpack seems to have resolved the correct module corresponding to the module option in tsconfig.json

Aside, files under esm/ directory are not valid ESM files in fact (import statements need file extensions in ESM), so it's still impossible to import ESM files from Node.js. I hope it'll be resolved with coming --module=Node12 option.

ryota-ka avatar Mar 28 '22 07:03 ryota-ka

I've noticed in 3.0.0 branch there's different targets in the relevant tsconfigs. I'm not sure this makes sense: I may be using a modern bundler but need libraries to ship with older browser support, or I may be using an old bundler but not need browser support going back to ES5.

My approach was to unify each around the minimum of browser support and Node.js support however I may have overlooked something.

samhh avatar Mar 28 '22 13:03 samhh

As mentioned by @ryota-ka, current esm/ is invalid to be consumed by ESM natively, I think easiest fix would be change the default entry '.' to CommonJS-only (before the extensions issue been fully addressed).

in: https://github.com/gcanti/fp-ts/blob/799d80b7ca2faeb9953f249e30d69df55a921d57/scripts/build3.ts#L39-L44

change to:

exports: { 
  '.': './cjs/index.js',
  './*': { 

It's not ideal but at least gains interoperability from ESM to CommonJS.

imcotton avatar Apr 01 '22 08:04 imcotton