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

Document a How To on using Svelte with Babel #3388

Closed
arggh opened this issue Aug 10, 2019 · 19 comments
Closed

Document a How To on using Svelte with Babel #3388

arggh opened this issue Aug 10, 2019 · 19 comments
Labels

Comments

@arggh
Copy link
Contributor

arggh commented Aug 10, 2019

Is your feature request related to a problem? Please describe.

It's a common request to be able to serve a website to users with older browsers, like IE11 or iOS Safari 9.

This requires all sorts of polyfilling & transpilation happening behind the scenes.

However, there's a few gotchas in getting Babel, the de facto tool for transpiling modern code into digestable ES5, to work together with Rollup/Webpack and Svelte. Would be nice, if there was a well documented, visibly linked and thoroughly explained starting point to fork from. Or at least something to copy-paste from.

For example, this golden nugget of information was hidden in a comment inside a closed issue:

Also notice that at least for rollup-plugin-babel, you will have to explicitly configure it to handle Svelte's components as they are recognized from their original extension. So add something like extensions: ['.js', '.html', '.svelte'] when enabling Babel.

Trying to search the Svelte website for "Babel" gets exactly 0 results. Which is stupid.

Describe the solution you'd like
I'd like a clear how-to on the Svelte website with copy-paste config examples on getting Babel to run with Svelte, so people could spend their time doing actual work or being outdoors ☀️

How important is this feature to you?
It's not very important to myself anymore, as I already sacrificed a fair amount of my own time to fiddle together a working setup. However, I believe it is useful for the general public, and very easy to implement.

@Conduitry Conduitry added the docs label Aug 10, 2019
@samuelgozi
Copy link
Contributor

samuelgozi commented Aug 11, 2019

In the official website, you've got instructions that will clone a template into your machine, and I don't recall any gotcha with the configuration(either webpack or rollup) other than the fact that you have to turn off HMR with webpack because it's broken.

If you would like to see my configuration, here it is(webpack):

}
// ...
	module: {
		rules: [
			{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
			{ test: /\.svelte$/, exclude: /node_modules/, loader: 'svelte-loader', options: { css: false } },
			{ test: /\.less$/, use: [ExtractCssChunks.loader, 'css-loader', 'less-loader'] }
		]
	},
// ...

I omitted the irrelevant parts.

@srobertson421
Copy link

+1 Would love to see a correct way to include transpilation and polyfills with rollup and svelte

@rmtracey
Copy link

rmtracey commented Sep 26, 2019

Just sharing my solution for getting Babel working for anyone struggling with this:

In rollup.config.js:

babel({
  extensions: ['.js', '.mjs', '.html', '.svelte'],
  include: ['src/**', 'node_modules/svelte/**'],
}),

In babel.config.js:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
      },
    ],
  ],
}

Note that I'm using babel.config.js, and NOT .babelrc. From what I understand, the files in the Svelte package need to be run through Babel as well, which is why the following not-obvious things need to be configured:

  • The .mjs extension needs to be included in Babel, as some files in the Svelte package use that extension. Note that while Babel does include .mjs files by default, we need to explicitly include it anyway since we need to provide our own custom list of extensions that includes .svelte.
  • node_modules/svelte/** needs to be specifically included in Babel along with your source files if you don't want Babel transpiling the rest of your node_modules. You can't just do a blanket ignore on node_modules like you normally would.
  • Babel MUST be configured through babel.config.js, rollup-plugin-babel, or babel-loader, as from what I understand, Babel does not support transpiling anything in node_modules if you configure it using .babelrc.

@northkode
Copy link

northkode commented Oct 23, 2019

After setting up babel, i'm now getting this error

bundle-es2015.js?v=1571843170772:1 Uncaught ReferenceError: _classCallCheck is not defined

cant find any thing that talks about this.

edit
Looks like rollup tries to do the unresolved dependencies and the runtime regeneratory and helpers arent being compiled in..

@damienMellet
Copy link

@northkode do you have the "@babel/runtime" dependencies installed ?

I just had the same problem than you with : _classCallCheck is not defined

After installing "@babel/runtime" to my svelte project, it worked.

@ayZagen
Copy link

ayZagen commented Nov 13, 2019

@damienMellet can you share a snippet ? I am struggling with it too.

@caschbre
Copy link

I'm using babel with svelte and sapper. Here are snippets of my package.json and rollup.config.js file if it's helpful...

{
  "name": "svelte-app",
  "version": "2.0.0",
  "browserslist": [
    "defaults"
  ],
  "devDependencies": {
    "@ampproject/rollup-plugin-closure-compiler": "^0.12.1",
    "@babel/core": "^7.6.4",
    "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.6.2",
    "@babel/preset-env": "^7.6.3",
    "@material/typography": "^3.1.0",
    "@rollup/plugin-alias": "^2.2.0",
    "@smui/button": "^1.0.0-beta.17",
    "@smui/common": "^1.0.0-beta.15",
    "@smui/data-table": "^1.0.0-beta.17",
    "@smui/dialog": "^1.0.0-beta.17",
    "@smui/form-field": "^1.0.0-beta.17",
    "@smui/icon-button": "^1.0.0-beta.17",
    "@smui/linear-progress": "^1.0.0-beta.17",
    "@smui/paper": "^1.0.0-beta.17",
    "@smui/radio": "^1.0.0-beta.17",
    "@smui/select": "^1.0.0-beta.17",
    "@smui/textfield": "^1.0.0-beta.17",
    "autoprefixer": "^9.7.1",
    "eslint": "^6.6.0",
    "eslint-config-airbnb": "^18.0.1",
    "eslint-config-prettier": "^6.5.0",
    "eslint-plugin-import": "^2.18.2",
    "eslint-plugin-jsx-a11y": "^6.2.3",
    "eslint-plugin-prettier": "^3.1.1",
    "node-sass": "^4.13.0",
    "npm-run-all": "^4.1.5",
    "postcss": "^7.0.21",
    "prettier": "^1.18.2",
    "prettier-plugin-svelte": "^0.7.0",
    "rollup": "^1.12.0",
    "rollup-plugin-alias": "^2.2.0",
    "rollup-plugin-babel": "^4.3.3",
    "rollup-plugin-commonjs": "^10.0.0",
    "rollup-plugin-livereload": "^1.0.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-postcss": "^2.0.3",
    "rollup-plugin-replace": "^2.2.0",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "sirv-cli": "^0.4.4",
    "svelte": "^3.0.0",
    "svelte-inspect": "^0.1.0",
    "svelte-select": "^3.1.1"
  },
  "dependencies": {},
  "scripts": {...}
}
import babel from 'rollup-plugin-babel';
import svelte from 'rollup-plugin-svelte';
import alias from 'rollup-plugin-alias';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import replace from 'rollup-plugin-replace';
import pkg from './package.json';

const production = !process.env.ROLLUP_WATCH;
const name = pkg.name
  .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3')
  .replace(/^\w/, m => m.toUpperCase())
  .replace(/-\w/g, m => m[1].toUpperCase());

const jsName = name + '.min.js';
const cssName = name + '.min.css';

export default {
  input: './src/main.js',
  output: {
    sourcemap: !production,
    format: 'iife',
    name: name,
    file: 'dist/' + jsName
  },
  plugins: [
    alias({
      resolve: ['.js', '.mjs', '.html', '.svelte']
    }),
    svelte({
      dev: !production,
      // we'll extract any component CSS out into
      // a separate file — better for performance
      css: css => {
        css.write('dist/' + cssName);
      },
      emitCss: true
    }),
    replace({
      'process.env.NODE_ENV': !production
        ? JSON.stringify('development')
        : JSON.stringify('production')
    }),
    babel({
      extensions: ['.js', '.mjs', '.html', '.svelte'],
      include: [
        'src/**',
        'src/services/**',
        'node_modules/svelte/**',
        'node_modules/svelte-select/**'
      ],
      exclude: ['node_modules/@babel/**'],
      runtimeHelpers: true
    }),
    resolve({
      browser: true,
      dedupe: importee =>
        importee === 'svelte' || importee.startsWith('svelte/')
    }),
    commonjs({
      include: ['node_modules/**']
    }),
    postcss({
      extract: true,
      minimize: production,
      use: [
        [
          'sass',
          {
            includePaths: ['./src/theme', './node_modules']
          }
        ]
      ]
    }),
    // Watch the `public` directory and refresh the
    // browser on changes when not in production
    !production &&
      livereload({
        watch: 'dist'
      }),

    // If we're building for production (npm run build
    // instead of npm run dev), minify
    // @todo look into closure.
    production && terser()
  ],
  watch: {
    clearScreen: false
  }
};

@damienMellet
Copy link

@ayZagen Here is my config package.json :

{
  "name": "svelte-app",
  "version": "1.0.0",
  "devDependencies": {
    "@babel/core": "^7.7.2",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.6.2",
    "@babel/preset-env": "^7.7.1",
    "@babel/runtime": "^7.7.2",
    "node-sass": "^4.13.0",
    "rollup": "^1.12.0",
    "rollup-plugin-babel": "^4.3.3",
    "rollup-plugin-commonjs": "^10.0.0",
    "rollup-plugin-livereload": "^1.0.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "svelte": "^3.0.0",
    "svelte-preprocess-sass": "^0.2.0"
  },
  "dependencies": {
    "core-js": "^3.4.1",
    "sirv-cli": "^0.4.4"
  },
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public --single",
    "start:dev": "sirv public --single --dev"
  }
}

and rollup.config.js

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import rollup_start_dev from './rollup_start_dev';
import { sass } from 'svelte-preprocess-sass';
import babel from 'rollup-plugin-babel';

const production = !process.env.ROLLUP_WATCH;

export default {
	input: 'src/main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'public/bundle.js'
	},
	plugins: [
		svelte({
			// enable run-time checks when not in production
			dev: !production,
			// we'll extract any component CSS out into
			// a separate file — better for performance
			css: css => {
				css.write('public/bundle.css');
			},
			preprocess: {
        style: sass(),
      },
		}),

		// If you have external dependencies installed from
		// npm, you'll most likely need these plugins. In
		// some cases you'll need additional configuration —
		// consult the documentation for details:
		// https://github.com/rollup/rollup-plugin-commonjs
		resolve({
			browser: true,
			dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
		}),
		commonjs(),

		// In dev mode, call `npm run start:dev` once
		// the bundle has been generated
		!production && rollup_start_dev,

		// Watch the `public` directory and refresh the
		// browser on changes when not in production
		!production && livereload('public'),

		//compile to old es5 for ie11
		babel({
			extensions: [ '.js', '.mjs', '.html', '.svelte' ],
			runtimeHelpers: true,
			exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ],
			presets: [
				[
					'@babel/preset-env',
					{
						targets: '> 0.25%, not dead',
						useBuiltIns: 'usage',
  					corejs: 3
					}
				]
			],
			plugins: [
			'@babel/plugin-syntax-dynamic-import',
			[
					'@babel/plugin-transform-runtime',
					{
						useESModules: false
					}
				]
			]
		}),
		// If we're building for production (npm run build
		// instead of npm run dev), minify
		production && terser()
	],
	watch: {
		clearScreen: false
	}
};

@ayZagen
Copy link

ayZagen commented Nov 14, 2019

@caschbre @damienMellet Thanks a lot! I was using webpack so I am posting here my configs in case anyone struggles with it too. These configs are also configured to transpile typescript and svelte with babel too.

//babel-config.js
module.exports = function(api) {
    api.cache(true);

    const presets = [
        ["@babel/preset-env", {
            useBuiltIns: "entry", // or "entry"
            corejs: 2,
        }],
        "@babel/typescript",
    ];
    const plugins = [
        "@babel/proposal-class-properties",
        "@babel/proposal-object-rest-spread",
        "@babel/plugin-proposal-optional-chaining",
    ];

    return {
        comments: true,
        ignore: [/[\/\\]core-js/, /@babel[\/\\]runtime/],
        presets,
        plugins,
    };
};
//webpack.config.js
const path = require("path");
const webpack = require("webpack");

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const sveltePreprocess = require("svelte-preprocess");
const TerserPlugin = require("terser-webpack-plugin");

const mode = process.env.NODE_ENV || "development";

module.exports = {
    mode,
    entry: "./src/index.ts",
    output: {
        library: "MyLibrary",
        libraryTarget: "umd",
        libraryExport: "default",
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.min.js",
        umdNamedDefine: true
    },
    resolve: {
       // here is the deal breaker.
       //mjs extension must be before js to use correct svelte lib files.
        extensions: [".mjs", ".ts", ".tsx", ".js", ".json", ".svelte"],
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    output: {
                        comments: false, // in case of magic comments
                    },
                },
                extractComments: false,
            }),
        ],
    },
    module: {
        rules: [
            {
                test: /\.(js|ts|mjs|svelte)$/,
                use: {
                    loader: 'babel-loader'
                },
            },
            {
                test: /\.svelte$/,
                exclude: /node_modules/,
                use: {
                    loader: "svelte-loader",
                    options: {
                        emitCss: false, // I would like to keep my css in js so If you want to have separate files change this to be true
                        hotReload: true,
                        preprocess: {
                            style: sveltePreprocess({}).style
                        },
                    },
                },
            },
            {
                test: /\.(sass|scss)$/,
                use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
            },
        ],
    },
    plugins: [
        new CleanWebpackPlugin()
    ]
};
// I haven't tried building declaration files 
//tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "declaration": true,
    "outDir": "dist/types",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}

@christopherbecker
Copy link

Just sharing my solution for getting Babel working for anyone struggling with this:

In rollup.config.js:

babel({
  extensions: ['.js', '.mjs', '.html', '.svelte'],
  include: ['src/**', 'node_modules/svelte/**'],
}),

In babel.config.js:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
      },
    ],
  ],
}

Note that I'm using babel.config.js, and NOT .babelrc. From what I understand, the files in the Svelte package need to be run through Babel as well, which is why the following not-obvious things need to be configured:

  • The .mjs extension needs to be included in Babel, as some files in the Svelte package use that extension. Note that while Babel does include .mjs files by default, we need to explicitly include it anyway since we need to provide our own custom list of extensions that includes .svelte.
  • node_modules/svelte/** needs to be specifically included in Babel along with your source files if you don't want Babel transpiling the rest of your node_modules. You can't just do a blanket ignore on node_modules like you normally would.
  • Babel MUST be configured through babel.config.js, rollup-plugin-babel, or babel-loader, as from what I understand, Babel does not support transpiling anything in node_modules if you configure it using .babelrc.

This doesn't work for me, would you mind sharing some more example code?

When I follow what you've done, the build still builds ES6 and doesn't work in IE11.

@rmtracey
Copy link

Just sharing my solution for getting Babel working for anyone struggling with this:
In rollup.config.js:

babel({
  extensions: ['.js', '.mjs', '.html', '.svelte'],
  include: ['src/**', 'node_modules/svelte/**'],
}),

In babel.config.js:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
      },
    ],
  ],
}

Note that I'm using babel.config.js, and NOT .babelrc. From what I understand, the files in the Svelte package need to be run through Babel as well, which is why the following not-obvious things need to be configured:

  • The .mjs extension needs to be included in Babel, as some files in the Svelte package use that extension. Note that while Babel does include .mjs files by default, we need to explicitly include it anyway since we need to provide our own custom list of extensions that includes .svelte.
  • node_modules/svelte/** needs to be specifically included in Babel along with your source files if you don't want Babel transpiling the rest of your node_modules. You can't just do a blanket ignore on node_modules like you normally would.
  • Babel MUST be configured through babel.config.js, rollup-plugin-babel, or babel-loader, as from what I understand, Babel does not support transpiling anything in node_modules if you configure it using .babelrc.

This doesn't work for me, would you mind sharing some more example code?

When I follow what you've done, the build still builds ES6 and doesn't work in IE11.

Sure. Below are my package.json, babel.config.js, and rollup.config.js. All of my other files are just the defaults from the sveltejs/template repo.

package.json

{
  "name": "svelte-app",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public"
  },
  "devDependencies": {
    "@babel/core": "^7.7.4",
    "@babel/preset-env": "^7.7.4",
    "rollup": "^1.12.0",
    "rollup-plugin-babel": "^4.3.3",
    "rollup-plugin-commonjs": "^10.0.0",
    "rollup-plugin-livereload": "^1.0.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "svelte": "^3.0.0"
  },
  "dependencies": {
    "core-js": "^3.4.5",
    "sirv-cli": "^0.4.4"
  }
}

babel.config.js

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
      },
    ],
  ],
}

rollup.config.js

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';

const production = !process.env.ROLLUP_WATCH;

export default {
	input: 'src/main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'public/build/bundle.js'
	},
	plugins: [
		svelte({
			// enable run-time checks when not in production
			dev: !production,
			// we'll extract any component CSS out into
			// a separate file — better for performance
			css: css => {
				css.write('public/build/bundle.css');
			}
		}),

		// If you have external dependencies installed from
		// npm, you'll most likely need these plugins. In
		// some cases you'll need additional configuration —
		// consult the documentation for details:
		// https://github.com/rollup/rollup-plugin-commonjs
		resolve({
			browser: true,
			dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
		}),
		commonjs(),

		babel({
			extensions: ['.js', '.mjs', '.html', '.svelte'],
			include: ['src/**', 'node_modules/svelte/**'],
		}),

		// In dev mode, call `npm run start` once
		// the bundle has been generated
		!production && serve(),

		// Watch the `public` directory and refresh the
		// browser on changes when not in production
		!production && livereload('public'),

		// If we're building for production (npm run build
		// instead of npm run dev), minify
		production && terser()
	],
	watch: {
		clearScreen: false
	}
};

function serve() {
	let started = false;

	return {
		writeBundle() {
			if (!started) {
				started = true;

				require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
					stdio: ['ignore', 'inherit', 'inherit'],
					shell: true
				});
			}
		}
	};
}

@nerdess
Copy link

nerdess commented Mar 25, 2020

@christopherbecker did you find a solution? i have the same issue, still getting es6 code using @rmtracey 's configuration...

@nerdess
Copy link

nerdess commented Mar 29, 2020

got it working now using this guide: https://blog.az.sg/posts/svelte-and-ie11/

@yazonnile
Copy link

For webpack + svelte + babel
https://github.com/yazonnile/svelte-webpack-babel

@tyteen4a03
Copy link

I am having issues with getting this config to accept the null coalescing operator in Svelte component code. Any ideas how I can use this syntax?

@Klustre
Copy link

Klustre commented Jun 3, 2020

@ayZagen I've tested your setup and @babel/plugin-proposal-optional-chaining fails in Svelte components. Is it working for you?

@ayZagen
Copy link

ayZagen commented Jun 3, 2020

@Klustre I have used optional chaining in ts files but not in svelte components. So as you asked, I tried and it fails on svelte components. To make it work you need to update svelte-loader's preprocessor like following:

preprocess: sveltePreprocess({
    babel: {
        presets: [
            ["@babel/preset-env", {
                loose: true,
                modules: false,
                targets: {
                    esmodules: true,
                }
            }],
            "@babel/typescript",
        ] ,
        plugins: [
            "@babel/proposal-class-properties",
            "@babel/proposal-object-rest-spread",
            "@babel/plugin-proposal-optional-chaining",
        ]
    }
})

Hope it helps you

@Klustre
Copy link

Klustre commented Jun 4, 2020

Thanks a lot @ayZagen 👍

@dummdidumm
Copy link
Member

Closing this, as I think by now there are enough resources out there (including https://sveltesociety.dev/recipes/build-setup/transpiling-es6-to-es5-for-legacy-browser-support) and also in this thread (thanks for everyone posting their solutions!) which help people setting up Bable with Svelte. With the rise of Vite, it's furthermore unlikely that people will setup this stuff as it was expected 4 years ago when this issue was opened.

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Mar 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests