Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow support #2238

Closed
Eazymov opened this issue Feb 25, 2021 · 18 comments
Closed

Flow support #2238

Eazymov opened this issue Feb 25, 2021 · 18 comments
Labels
enhancement New feature or request has workaround

Comments

@Eazymov
Copy link

Eazymov commented Feb 25, 2021

Describe the bug

I'm trying to write vite-plugin for removing Flow.js types and i'm getting the error below

Reproduction

  1. yarn create @vitejs/app template - vanilla
  2. (inside generated folder)
yarn install
yarn add -D flow-remove-types
  1. Set main.js content:
/* @flow */

type Type = {};
  1. Create vite.config.js:
import removeTypes from "flow-remove-types";

export default {
  plugins: [
    {
      name: "vite-plugin-flow",
      enforce: "pre",
      transform(code) {
        const output = removeTypes(code);

        return {
          code: output.toString(),
          map: null,
        };
      },
    },
  ],
};
  1. yarn dev // error

But it starts working if:

  1. Remove types from main.js
  2. yarn dev
  3. Add types to main.js

Sorry if i missed something in the docs.

System Info

  • vite version: 2.0.3
  • Operating System: Ubuntu 18.04
  • Node version: 14.15.4
  • Package manager (npm/yarn/pnpm) and version: [email protected]

Logs (Optional if provided reproduction)

  1. Run vite or vite build with the --debug flag.
  vite:config bundled config file loaded in 122ms +0ms
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'vite:pre-alias',
  vite:config     'alias',
  vite:config     'vite-plugin-flow',
  vite:config     'vite:dynamic-import-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:client-inject',
  vite:config     'vite:import-analysis'
  vite:config   ],
  vite:config   server: {},
  vite:config   configFile: '/home/eazymov/Documents/projects/test/vite.config.js',
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: undefined,
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     server: {}
  vite:config   },
  vite:config   root: '/home/eazymov/Documents/projects/test',
  vite:config   base: '/',
  vite:config   resolve: { dedupe: undefined, alias: [ [Object] ] },
  vite:config   publicDir: '/home/eazymov/Documents/projects/test/public',
  vite:config   command: 'serve',
  vite:config   mode: 'development',
  vite:config   isProduction: false,
  vite:config   optimizeCacheDir: '/home/eazymov/Documents/projects/test/node_modules/.vite',
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge16', 'firefox60', 'chrome61', 'safari11' ],
  vite:config     polyfillDynamicImport: true,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: false,
  vite:config     rollupOptions: {},
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     minify: 'terser',
  vite:config     terserOptions: {},
  vite:config     cleanCssOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: false,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     brotliSize: true,
  vite:config     chunkSizeWarningLimit: 500
  vite:config   },
  vite:config   env: { BASE_URL: '/', MODE: 'development', DEV: true, PROD: false },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen]
  vite:config   },
  vite:config   createResolver: [Function: createResolver]
  vite:config } +2ms
  vite:deps Crawling dependencies using entries:
  vite:deps   /home/eazymov/Documents/projects/test/index.html +0ms
  vite:resolve 1ms   /main.js -> /home/eazymov/Documents/projects/test/main.js +0ms
 > main.js:3:5: error: Expected ";" but found "Type"
    3 │ type Type = {};
      ╵      ~~~~

error when starting dev server:
Error: Build failed with 1 error:
main.js:3:5: error: Expected ";" but found "Type"
    at failureErrorWithLog (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:1171:15)
    at buildResponseToResult (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:907:32)
    at /home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:1002:20
    at /home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:553:9
    at handleIncomingPacket (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:642:9)
    at Socket.readFromStdout (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:520:7)
    at Socket.emit (events.js:315:20)
    at addChunk (internal/streams/readable.js:309:12)
    at readableAddChunk (internal/streams/readable.js:284:9)
    at Socket.Readable.push (internal/streams/readable.js:223:10)
error Command failed with exit code 1.
  1. Provide the error log here.
$ vite
 > main.js:3:5: error: Expected ";" but found "Type"
    3 │ type Type = {};
      ╵      ~~~~

error when starting dev server:
Error: Build failed with 1 error:
main.js:3:5: error: Expected ";" but found "Type"
    at failureErrorWithLog (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:1171:15)
    at buildResponseToResult (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:907:32)
    at /home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:1002:20
    at /home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:553:9
    at handleIncomingPacket (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:642:9)
    at Socket.readFromStdout (/home/eazymov/Documents/projects/test/node_modules/esbuild/lib/main.js:520:7)
    at Socket.emit (events.js:315:20)
    at addChunk (internal/streams/readable.js:309:12)
    at readableAddChunk (internal/streams/readable.js:284:9)
    at Socket.Readable.push (internal/streams/readable.js:223:10)
error Command failed with exit code 1.
@Eazymov Eazymov changed the title Code preprocessing. Removing flow types Plugin. Code preprocessing. Removing flow types Feb 25, 2021
@yyx990803
Copy link
Member

This is an error during Vite's pre-scanning that uses esbuild to look for dependencies. Esbuild cannot handle flow syntax because it assumes .js files only contain valid JS syntax.

Looks like the only way to solve this is to expose a way to add plugins to the pre-scanning phase as well :/

@Eazymov
Copy link
Author

Eazymov commented Feb 26, 2021

Many people use Flow, would be great if Vite could support it :)

@yyx990803 yyx990803 changed the title Plugin. Code preprocessing. Removing flow types Flow support Feb 26, 2021
@yyx990803 yyx990803 added enhancement New feature or request and removed pending triage labels Feb 26, 2021
@henrikbjorn
Copy link

Maybe it is possible to use this. https://www.npmjs.com/package/esbuild-plugin-flow Not sure if Vite allows passing additional plugins to esbuild directly.

@729993031 729993031 mentioned this issue Mar 22, 2021
2 tasks
@patak-dev
Copy link
Member

@henrikbjorn @Eazymov were you able to try https://www.npmjs.com/package/esbuild-plugin-flow ? Vite does support passing options to esbuild https://vitejs.dev/config/#esbuild. I think that if this works, we could close this issue.

@Eazymov
Copy link
Author

Eazymov commented Mar 28, 2021

@henrikbjorn @Eazymov were you able to try https://www.npmjs.com/package/esbuild-plugin-flow ? Vite does support passing options to esbuild https://vitejs.dev/config/#esbuild. I think that if this works, we could close this issue.

As i can see passing plugins is not supported

export interface ESBuildOptions extends TransformOptions {
include?: string | RegExp | string[] | RegExp[]
exclude?: string | RegExp | string[] | RegExp[]
jsxInject?: string
}

@patak-dev
Copy link
Member

You are right, ESBuildOptions extends TransformOptions from esbuild, and I see that plugins are only supported during build and not for transform.
Evan Wallace explained the rationale here evanw/esbuild#779 (comment)

The reason why you can't use on-load plugins with the transform API is that it's pointless. The transform API takes the code as a string so if you want to transform the string before esbuild processes it, you can just transform the string yourself first before giving it to esbuild. You don't need the plugin API for that.

@henrikbjorn
Copy link

You are right, ESBuildOptions extends TransformOptions from esbuild, and I see that plugins are only supported during build and not for transform.
Evan Wallace explained the rationale here evanw/esbuild#779 (comment)

The reason why you can't use on-load plugins with the transform API is that it's pointless. The transform API takes the code as a string so if you want to transform the string before esbuild processes it, you can just transform the string yourself first before giving it to esbuild. You don't need the plugin API for that.

Is there a way to transform the code before it is given to esbuild? I seem to remember that was possible in Vite1.

@TrySound
Copy link
Contributor

@henrikbjorn You can add rollup/plugin-babel.

@henrikbjorn
Copy link

@henrikbjorn You can add rollup/plugin-babel.

If Rollup plugins are the same as vite plugins, how would that work any differently as it does now?

@TrySound
Copy link
Contributor

TrySound commented Apr 6, 2021

@henrikbjorn You are not constrained to .jsx and .tsx extensions with it. You can put any syntax there vite does not support.

@henrikbjorn
Copy link

@henrikbjorn You are not constrained to .jsx and .tsx extensions with it. You can put any syntax there vite does not support.

The problem here is that we can't change the code, before it is given to ESBuild. That constraint is the same for Rollup plugins, when using the server. In that case, I can't see how using a Rollup will work differently than a small plugin that tries and strip flow types.

@TrySound
Copy link
Contributor

TrySound commented Apr 6, 2021

See https://vitejs.dev/guide/api-plugin.html#plugin-ordering
You probably want to add enforce: 'pre' option

@henrikbjorn
Copy link

See https://vitejs.dev/guide/api-plugin.html#plugin-ordering
You probably want to add enforce: 'pre' option

Look at the bug report here. Does not work, this is exactly the plugin used by the author of this issue.

@chrisvasz
Copy link

chrisvasz commented Apr 29, 2021

Disclaimer: the following is an ugly hack, but it got us moving. My first attempt used flow-remove-types, but we ended up needing to parse decorators for esbuild also, so I switched to Babel. I will happily tear this code out when there is official support in vite for this use-case!

TLDR: temporarily replace fs.readFileSync with a custom version that compiles away non-standard syntax until the server starts up and the normal vite plugins can take over.

const { createServer } = require('vite');
const fs = require('fs');
const babelCore = require('@babel/core');

const readFileSync = fs.readFileSync;

(async () => {
  intercept();
  const server = await createServer();
  await server.listen();
  unintercept();
})();

function intercept() {
  fs.readFileSync = (...params) => {  // 🤮 
    const [path] = params;
    const src = readFileSync(...params);
    return babel(path, src);
  };
}

function unintercept() {
  fs.readFileSync = readFileSync;
}

function babel(path, src) {
  const extensions = ['.js', '.jsx'];
  if (typeof src !== 'string') return src;
  if (path.includes('/node_modules/')) return src;
  if (!extensions.some(ext => path.endsWith(ext))) return src;
  return babelCore.transformSync(src, {
    // Transform just enough to allow vite to get through the precompilation stage
    filename: path,
    presets: ['@babel/flow'],
    plugins: [
      '@babel/plugin-syntax-jsx',
      '@babel/plugin-syntax-class-properties',
      ['@babel/plugin-proposal-decorators', { legacy: true }],
    ],
  }).code;
}

@nihalgonsalves
Copy link
Member

nihalgonsalves commented May 11, 2021

You can specify your own esbuild plugins with #2991, released in v2.3.0.

These plugins are not applied during build (only dep scan and optimisation), so you will have to create both an esbuild & Vite/rollup entry if you need certain behaviour in both cases.

We can continue the discussion about pre-dep-optimization Vite hooks in #3124.

@wehriam
Copy link

wehriam commented Jun 17, 2021

See @bunchtogether/vite-plugin-flow for flow support.

@Shinigami92
Copy link
Member

@wehriam Hey, did you already created a PR in https://github.com/vitejs/awesome-vite ?

@github-actions
Copy link

This issue has been locked since it has been closed for more than 14 days.

If you have found a concrete bug or regression related to it, please open a new bug report with a reproduction against the latest Vite version. If you have any other comments you should join the chat at Vite Land or create a new discussion.

@github-actions github-actions bot locked and limited conversation to collaborators Jul 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request has workaround
Projects
None yet
Development

No branches or pull requests

9 participants