alangpierce / sucrase

Super-fast alternative to Babel for when you can target modern JS runtimes
https://sucrase.io
MIT License
5.66k stars 142 forks source link

Can we run sucrase in the browser like babel-standalone? #507

Open alexilyaev opened 4 years ago

alexilyaev commented 4 years ago

For the same reasons babel-standalone exists. Would be nice to be able to run sucrase in browser code.

e.g.

<div id="root"></div>
<script src="https://unpkg.com/.../sucrase.js"></script>
<script type="text/sucrase" src="app.js"></script>

Right now babel-standalone doesn't support React Fragments (it's stuck on babel v7 beta 3).

alangpierce commented 4 years ago

Hey @alexilyaev , Sucrase itself runs fine in the browser (it doesn't have any node dependencies), though at the moment there's no pre-packaged browser build attaching it to the global scope, and there's no support for text/sucrase-style scripts.

In the past, I've been hesitant about a browser bundle the attaches Sucrase as a global since it's kind of the "old way" of using libraries, though the text/sucrase type of use case does seem compelling. In your case you'd just use the jsx transform, is that right? Seems like the typescript, flow, and imports transforms would only make sense in environments with additional tooling where you might as well have webpack (or similar) set up, though I could see a use case for TS/Flow checking and still wanting the script directly loaded to the browser rather than through webpack.

alexilyaev commented 4 years ago

Well, my use case is teaching React with no bundler, currently it looks like this:

<div id="root"></div>

<script src="https://unpkg.com/react@16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>

<script type="text/babel" src="app.js"></script>
// app.js
const App = () => {
  return <h1>Hello World!</h1>;
};

ReactDOM.render(<App />, document.querySelector('#root'));

So React is global. import and require don't work in this context. TypeScript would have been a nice addition as it useful for teaching.

Right now I'm only missing the React Fragment <>...</> syntax support, which was added in babel-standalone v7.0.0-beta.31...

EDIT:

I was using an old package of babel-standalone, the new one has the latest versions: https://unpkg.com/browse/@babel/standalone@7.8.4/

So this one does support the React Fragment syntax (and TS as well).

<script src="https://unpkg.com/@babel/standalone@7.8.4/babel.min.js"></script>

But, that file weighs 1.6 MB. So, if sucrase could do it leaner, I'd be happy about that.

tomByrer commented 4 years ago

duplicate of #506

BTW @alexilyaev, once this happens, you can use jsDelivr CDN to use only 1 request. Try this out instead of all your 3 unpkg requests. though mine uses the current Babelv7.8.x & includes MaterialUI:

<script src="https://cdn.jsdelivr.net/combine/npm/react@16.12/umd/react.development.js,npm/react-dom@16.12/umd/react-dom.development.js,npm/@material-ui/core@4.8/umd/material-ui.development.js,npm/@babel/standalone@7.8/babel.min.js" crossorigin="anonymous"></script>

Note my versioning: Patch releases are auto-updated, but the Minor versions are fixed. Takes about 5 hits to your local POP before this concocted request is cached. Details: https://www.jsdelivr.com/features

alexilyaev commented 4 years ago

@tomByrer That looks very interesting, I'll check it out, thanks!

tomByrer commented 4 years ago

@alangpierce Any extra consideration for a public standalone please? I'd love to use it via jsDelivr as I posted above.

@alexilyaev My link work for you? I've updated the versions successfully today.

alexilyaev commented 4 years ago

@tomByrer Your link does work, nice one liner.

For the training I eventually downloaded the files via npm and linked to them in the example files. Even managed to add MobX with Decorators support, which I was very happy about. In that sense, Babel does provide quite a bit of flexibility.

<body>
  <div id="root"></div>
</body>

<script src="../../../../node_modules/react/umd/react.development.js"></script>
<script src="../../../../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../../../../node_modules/mobx/lib/mobx.umd.js"></script>
<script src="../../../../node_modules/mobx-react-lite/dist/index.js"></script>
<script src="../../../../node_modules/mobx-react/dist/mobxreact.umd.development.js"></script>
<script src="../../../../node_modules/@babel/standalone/babel.min.js"></script>
<script>
/**
 * This is only needed for this training course, to be able to write modern JS
 * without running a build/bundler.
 */

const Babel = window.Babel;

/**
 * Configure @babel/standalone transformations:
 * https://babeljs.io/docs/en/babel-standalone
 */
Babel.registerPreset('ai-react', {
  presets: [
    // Enable legacy decorators for MobX
    // https://babeljs.io/docs/en/babel-preset-stage-2
    [Babel.availablePresets['stage-2'], { decoratorsLegacy: true }],
    Babel.availablePresets.react
  ],
  plugins: [
    // Map package imports to their global namespaces
    // https://babeljs.io/docs/en/babel-plugin-transform-modules-umd
    [
      Babel.availablePlugins['transform-modules-umd'],
      {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
          'mobx-react': 'mobxReact',
          'mobx-react-lite': 'mobxReactLite'
        },
        exactGlobals: true
      }
    ]
  ]
});
</script>

<script
  type="text/babel"
  data-plugins=""
  data-presets="ai-react"
  src="app.js"
></script>

For my use case it's good enough. But I can imagine some online editors wanting to use sucrase in the browser and babel being too big for them.

osdevisnot commented 4 years ago

@alexilyaev can you try importing from pika CDN?

import * as sucrase from 'https://cdn.pika.dev/sucrase'

alexilyaev commented 4 years ago

@alexilyaev can you try importing from pika CDN?

import * as sucrase from 'https://cdn.pika.dev/sucrase'

I didn't understand how does that help me achieve what babel-standalone is doing? Looking at my code example above, where would I fit that line? And how would it map import React from 'react'; in my app.js?

Example app.js that needs to work in the browser:

import React from 'react';
import ReactDOM from 'react-dom';

const root = <button className="btn-primary">Click me!</button>;

ReactDOM.render(root, document.querySelector('#root'));
nestarz commented 3 years ago

Here an example on how you can setup sucrase to work in the browser using a service worker: https://gist.github.com/nestarz/dcaa70b5ecac5fa7e66103658f7ff0b7

The caveat is that I copied paste the code (from skypack) of sucrase inside the sw.js.

curran commented 3 years ago

I would be interested in a browser build for Sucrase that introduces a global. My use case is a browser-based code editor. I have it up and running using Buble, but looking to migrate to Sucrase as Buble does not support optional chaining (and Buble has tons of unused stuff for transforming ES6).

Here's an example of where it would be used:

image

https://vizhub.com/curran/c3b14112dae34ef395999cef5783324f?edit=files&file=index.js

In the past, I've been hesitant about a browser bundle the attaches Sucrase as a global since it's kind of the "old way" of using libraries

Indeed, it's the "old way", but IMO it's fine. Lots of things still operate the "old way" with script tags and browser globals, and it's a tried and true approach. Also, what are the downsides of introducing a new dist file? Not many. It poses no risk of breakage.

ClassicOldSong commented 2 years ago

It'll be nice if Sucrase could include a UMD or IIFE version in release. Then we'll be able to use it directly via importScripts in Service Workers.

I'm writing an on-the-fly build tool using Service Worker, whose goal is to rid of node completely for modern frontend development/production, and I'm finding an alternative to esbuild since it could go out of stack strangely in Safari, but lacking of UMD/IIFE support won't make me able to create a plugin for Sucrase.

Currently there's only a CJS version and https://bundle.run/sucrase could do nothing about it.

curran commented 2 years ago

I agree.

It seems this work was done already here https://github.com/alangpierce/sucrase/pull/526

That PR was closed in favor of using

import * as sucrase from 'https://cdn.pika.dev/sucrase@3.14.0'

, but I think it would be great to include a browser build with the Sucrase package distribution directly, rather than introduce a dependency on a third party service/CDN.

ClassicOldSong commented 2 years ago

What's making things worse is that these CDNs are all serving only ESM, while import in Service Workers still require quite modern Chrome to be functional.

curran commented 2 years ago

I'm proposing that a UMD build be created for use over CDNs, not the ESM build.

altbdoor commented 10 months ago

For what its worth, I tried building sucrase with Rollup and IIFE in https://github.com/altbdoor/sucrase-build-iife#usage

I mostly followed the existing work in #526 , with minor adjustments to make things work. I managed to get things work in a service worker, to compile JSX to JS.

curran commented 10 months ago

Very nice! I could imagine this going into mainline Sucrase as an additional npm script that runs on prepublish:

          npx rollup -i ./package/dist/index.js \
            -f iife \
            -n sucrase \
            --banner "/* sucrase v$VERSION */" \
            -p '@rollup/plugin-node-resolve' \
            -p '@rollup/plugin-commonjs' \
            -p '@rollup/plugin-terser' \
            -o dist/sucrase.browser.js

From your work in https://github.com/altbdoor/sucrase-build-iife/blob/master/.github/workflows/build.yml#L79C1-L86C39