Flow: No documentation around custom resolvers

Created on 18 Oct 2018  路  10Comments  路  Source: facebook/flow

tracking: adonisjs/discussion/issues/65

Aside from the description on #6132 (which doesn't give many usage details), there's also no documentation on the .flowconfig webpage. I do see in the changelog it says:

Allow custom module resolvers (experimental, only works with the haste resolver system).

But I'd at least like to see an implementation example in the PR. I know there have been a few variants of "custom resolver" requests including #2939, #293.

So I'm using a framework adonisjs/adonis-framework . The framework operates similarly to @laravel/@symfony in that you have an IoC container and allows for dependency injection. The IoC is a package called adonis-fold. Real world use looks something like this:

'use strict'

const Redis = use('Redis')
const User = use('App/Models/User')

class UsersController {

  async index () {
    const cachedUsers = await Redis.get('users')
    if (cachedUsers) {
      return JSON.parse(cachedUsers)
    }

    const users = await User.all()
    await Redis.set('users', JSON.stringify(users))
    return users
  }
}

use() is a global that hits the IoC container which binds the namespace 'Redis' to something like this:

const Redis = require('./Redis')
const { ioc } = require('@adonisjs/fold')

ioc.bind('My/Redis', function (app) {
  const Config = app.use('Adonis/Src/Config')
  return new Redis(Config)
})

The use() keyword can be used to load any of the providers in adonis which are loaded as an array when the application boots. Essentially, we want a way to create a custom resolver as is described here but for flow. The project is also trying to get setup on typescript so the issues do overlap quite a bit.

For reference this is the code for the use method:

  use (namespace) {
    if (this._hasFake(namespace)) {
      return this._resolveFake(namespace)
    }

    if (this._isBinding(namespace)) {
      return this._resolveBinding(namespace)
    }

    if (this._isAlias(namespace)) {
      return this.use(this._aliases[namespace])
    }

    if (this._isAutoloadedPath(namespace)) {
      return this._resolveAutoloadedPath(namespace)
    }

    return this._require(namespace)
  }

How can I resolve this?

Needs docs

Most helpful comment

@jakesyl here is even better example: https://github.com/facebook/flow/pull/6132/files#diff-146f2d34133e80d0efbc6c7b6e6e7bcf

All 10 comments

The custom resolver option takes the path to a long-running binary that Flow starts and communicates with using a line-by-line stdin/stdout formatted with JSON. An implementation can be found in the PnP resolver used by Yarn:

https://github.com/yarnpkg/yarn/blob/master/src/util/generate-pnp-map-api.tpl.js#L832-L849

@arcanis Wow this is pretty cool! Does it mean that we could validate e.g css modules with custom resolver? Or at least that the css class names are present?

No, it's just a resolver (it just tells Flow which file to load when file X makes a require(Y) call), it doesn't allow you to do this kind of complicated logic (yet, at least) 馃檪

Oh ok, thanks! Do you think that it would be difficult to implement? I see that this could have _huge_ potential to build full blown plugin system to flow!

It'll doable but difficult. The main issue tho it's that it'll likely involve some architectural changes in the way Flow loads its files, which means that we first have to reach a consensus on what's the best way to do it (and if we want to do it!).

@arcanis thanks, that should work for our use case. As far as actually passing something like https://github.com/yarnpkg/yarn/blob/master/src/util/generate-pnp-map-api.tpl.js#L832-L849 to flow, how is that done would that use the flow config?

Yeah, I think that @villesau is right here- this is a prerequisite to a system that can process files like css modules and definitely a good idea.

@jakesyl I played around a bit with the solution, this is the minimum setup to get started:

.flowconfig:

[options]
module.system=haste
module.resolver=./test.js

And here is my hacky test.js which didn't do anything meaningful but managed to return message path-to-file? to flow:

#!/usr/bin/env node
const StringDecoder = require('string_decoder');
let buffer = '';
const decoder = new StringDecoder.StringDecoder();

process.stdin.on('data', chunk => {
  buffer += decoder.write(chunk);

  do {
    const index = buffer.indexOf('\n');
    if (index === -1) {
      break;
    }
    const line = buffer.slice(0, index);
    buffer = buffer.slice(index + 1);
    // success format (i think)
    process.stdout.write(`${JSON.stringify([null, 'path-to-file?'])}\n`);
    // error format (i think):
    // process.stdout.write(
    //   `${JSON.stringify([
    //     {
    //       code: 'what should I put here?',
    //       message: 'this message appears somewhere?',
    //       data: {
    //         request: 'not sure what to put here',
    //         issuer: 'or here',
    //         realIssuer: 'and here',
    //       },
    //     },
    //     null,
    //   ])}\n`,
    // );
  } while (true);
});

Important part is #!/usr/bin/env node as it allows flow to just run it.

This is what flow gives you in stdin: ["util","/path-to/flow-typed/cli/src/lib/stubUtils.js"], util being module name and path being path to file

Thanks! Going to play around with this a little bit.

@jakesyl here is even better example: https://github.com/facebook/flow/pull/6132/files#diff-146f2d34133e80d0efbc6c7b6e6e7bcf

Unfortunately https://github.com/facebook/flow/commit/87820b8e21e626126e2a8ab7e3a70f253a24bafd (part of 0.110) deleted module.resolver under the dubious guise of "security holes" (which is a bit of an odd rationale when we're talking about a package ecosystem with "postinstall" scripts). This means Yarn Berry / PnP won't be supported in Flow for the foreseeable future.

Was this page helpful?
0 / 5 - 0 ratings