Using Future JS Syntax in Svelte with Babel

The other, less common but still handy usecase for Babel with Svelte is transpiling future syntax inside <script> tags. For example, the Stage 3 Optional Chaining proposal is popular, but not yet in browsers, and more relevantly, is not yet understood by Acorn, which is what Svelte uses to parse <script> tags.

Example usage:
<script>
  let foo = {
    bar: {
      baz: true,
    },
  };
  let sub = foo?.ban?.baz;
</script>

<main>
  <h1>Hello {sub}!</h1>
</main>

When we try to run this, Acorn throws a ParseError: Unexpected token error.

Here, the fix is to use Svelte’s Preprocess feature:

// rollup.config.js
// ...

export default {
	// ...
  plugins: [
    svelte({
      // ...
      preprocess: {
        script: ({ content }) => {
          return require("@babel/core").transform(content, {
            plugins: ["@babel/plugin-proposal-class-properties"],
          });
        },
      },
    }),
 // ...

Of course, make sure that @babel/core and whatever plugins you use are installed.

Alternatively, you might wish to use svelte-preprocess instead, for its’ other features:

import preprocess from "svelte-preprocess";
// ...
preprocess: preprocess({
  babel: {
    presets: [
      [
        "@babel/preset-env",
        {
          loose: true,
          // No need for babel to resolve modules
          modules: false,
          targets: {
            // ! Very important. Target es6+
            esmodules: true,
          },
        },
      ],
    ],
  },
});
// ...

Important note: Don’t try to transpile to ES5 here. This would produce unnecessary bloat as you would be transpiling on a per-component basis - sharing Babel helpers is the reason you use bundler plugins instead. Keep it light, only transpile the new stuff you use. This also makes it easy to remove in future.

You may also wish to use new JS syntax inside the JS expressions in your template markup. There is currently no easy way to do that, but watch this issue.