Flow: module.system.node.main_field

Created on 26 Jan 2018  ยท  8Comments  ยท  Source: facebook/flow

TL;DR: A new module.system.node.main_field option for trying to resolve different package.json fields (other than package.json#main)

[options]
module.system.node.main_field='source'
module.system.node.main_field='main'

package.json#main or package.json#module

It is not uncommon to see package.json files that look like this:

{
  "name": "my-package",
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js"
}

Tools like Webpack, Rollup, and others will look at fields like package.json#module in addition to package.json#main when resolving dependencies.

Workspaces

The specific reason that I want Flow to have something similar is to support Workspaces.

Right now in order to support workspaces you need to have some very brittle module.name_mapper configuration:

# .flowconfig
[options]
module.name_mapper='^@scope\/\([a-zA-Z0-9_\-]+\)$' -> '<PROJECT_ROOT>/packages/[^/]+/\1/src/index'

Effectively: @scope/*/packages/*/src/index.js

This can break in a number of different ways, but I won't dive into all those.

module.system.node.main_field

If we could configure Flow like:

[options]
module.system.node.main_field='source'
module.system.node.main_field='main'

We could write our workspace package.json files like this:

{
  "name": "my-package",
  "main": "dist/index.js",
  "source": "src/index.js"
}

And Flow could check for package.json#source before package.json#main.

Implementation

The basic resolve process in Flow would look like this:

For each main_field:

  1. Check if the package.json has a field with that name
  2. If the package.json does not have the field, continue to next main_field
  3. If it field exists, check for a file at that location.
  4. If no file at that location exists, continue to the next main_field
  5. If the file exists, use that file as the resolved module and stop looking

The relevant bits of code seem to be:

https://github.com/facebook/flow/blob/9c75e15cc8809170933236c2add89a4646b04f44/src/parser_utils/package_json.ml#L40-L63
https://github.com/facebook/flow/blob/2ff56f4e7620b2d3d28cb862047449d3cbfd615b/src/services/inference/module_js.ml#L328-L371

feature request

Most helpful comment

Ended up using this to procrastinate going to the gym ๐Ÿ˜

All 8 comments

This doesn't seem too difficult. I'll try to find time to take a swing at this this week.

Ended up using this to procrastinate going to the gym ๐Ÿ˜

@thejameskyle thanks for spec'ing this out so thoroughly!

My current implementation differs from your request slightly. You wrote

  1. If it field exists, check for a file at that location.
  2. If no file at that location exists, continue to the next main_field

Whereas my PR just chooses the first main_field field it finds in the package.json.

I built it that way since it was easier to code, it's a slightly more simple behavior, and because I didn't read your spec carefully enough. How important is the file-exists behavior to you? It's a little tricky to build, since files appearing and disappearing can change which "main_field" is selected. It's also a more complicated behavior, which can be trickier for users to understand.

@gabelevi @thejameskyle We need to decide which name we usually want to use. Something like "flow_source" or similar. Otherwise many projects which also use such naming can be treated as untyped.

@gabelevi the behavior is important when it comes to consuming published packages that have an main field pointing to the uncompiled source but the source was omitted from npm.

{
  "main": "dist/index.js",
  "source": "src/index.js",
  "files": ["dist"]
}

I've tried a few methods for doing something like this and I've settled on a pretty simple solution, although it does requiring publishing src/. It works like this:

my-pkg/
โ”œโ”€ dist/
โ”‚  โ”œโ”€ index.js
โ”‚  โ”œโ”€ index.js.flow
โ”‚  โ””โ”€ index.mjs
โ”œโ”€ src/
โ”‚  โ””โ”€ index.js
โ””โ”€ package.json

package.json

{
  "name": "my-pkg",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "files": ["dist", "src"]
}

dist/index.js.flow

// @flow
export * from "../src/index.js";

All that's required is the extra index.js.flow stub file in dist/. This works perfectly inside yarn workspaces and for folks consuming the package via npm.

@rtsao Yeah, but it still requires you to have a build step to make sure everything is up to date before Flow will work

+1 for this feature.

First time flow user, this is one of the first things I looked for.

Was this page helpful?
0 / 5 - 0 ratings