Setting Up a JavaScript Build Process using Rollup
Tags: javascript, webdev, tutorial, rollup
Published: 2021-01-16 12:48:54

**Notes**:
- The following instructions were inspired and updated from [this blog post](https://www.learnwithjason.dev/blog/learn-rollup-js/);
- Since these instructions where written, some package might have been deprecated or moved, make sure to check their status, on their website or GitHub/npm pages;
- This setup will use Node.js and npm (node package manager). Follow [this link](https://nodejs.org/en/download/) for installation instructions.

**Content**:
1. [Goals](#goals)
2. [Getting Started](#getting-started)
3. [Rollup](#rollup)
    - [Testing the Configuration](#testing-the-configuration)
4. [Babel](#babel)
5. [ESLint](#eslint)
6. [Third-Party and Non-ES Modules Support](#third-party-and-non-es-modules-support)
7. [Enabling the ENV Variable](#enabling-the-env-variable)
8. [Terser](#terser)
9. [Less and PostCSS](#less-and-postcss)
10. [Automating Builds](#automating-builds)
11. [Building Multiple Modules](#building-multiple-modules)
12. [Final Notes](#final-notes)
    - [Using Builds](#using-builds)
    - [Managing Warnings](#managing-warnings)
    - [Final Project Structure](#final-project-structure)

## Goals

The goal of this setup is to allow the development and build of multiple JavaScript front-end modules and associated stylesheets using Rollup.
The idea is to have a `src` folder where scripts and styles can be developed. These sources would then be processed and bundled together in an `app` folder, ready to be used in the browser:
```
-- project/
   |-- app/
   |   |-- dist/
   |   |   | // build modules (scripts and styles) go here
   |   |-- [main.js]
   |   |-- [main.css]
   |   |-- index.html
   |-- src/
   |   |-- scripts/
   |   |   | // JavaScript source code goes here
   |   |-- styles/
   |   |   | // Less stylesheet sources go here
```

While setting up this process, we will explore many aspects of the modern JavaScript ecosystem: bundling, transpiling, linting, and minifying.

## Getting Started

First, we are going to create a project directory, add the above project structure, and enter the project directory:
```bash
mkdir -p project/{app/dist, src/{scripts,styles}} 
touch project/app/index.html
cd project 
```
We can then initialise a node project by typing the next command and following the instructions:
```node
npm init
```
It will create a `package.json` file for us, which describes the scope and dependencies of the project.

## Rollup

[Rollup.js](https://rollupjs.org/guide/en/) is a module bundler for JavaScript: it gets pieces of code that are dependent on each other to create a larger, self-contained module. It uses the standardized module format introduced in ES6. Rollup also uses a *tree-shaking* approach to bundling, removing unused pieces of code which could bulk your module unnecessarily.

To add it to the project, we type in the following command:
```node
npm install --save-dev rollup
```
`--save-dev` is an option that tells npm we want this library to be saved as a project dependency, in particular for development purposes (as opposed to dependencies for the code itself). If you check the `package.json` file, you will see the following added:
```json
// package.json
"devDependencies": {
    "rollup": "^2.36.1"
}
```
Although the version number might be different.

Next, we are going to create a configuration file for Rollup, `rollup.config.js`:
```js
// rollup.config.js
export default {
  input: 'src/scripts/foo.js',
  output: {
    file: 'app/dist/foo.js',
    format: 'iife',
    name: 'Foo',
    sourcemap: 'inline'
  }
}
```
- `input` is the file we want Rollup to process and bundle sources from;
- `output` contains the options for our built module:
  - `file` is where we want the bundle saved;
  - `format` lets us choose one of the many JavaScript flavor our bundle will have, check the options list [there](https://rollupjs.org/guide/en/#outputformat). Here we choose `iife` which will wrap the module in a self-executed function (immediately-invoked function expression), making the module declare itself in its own scope to avoid clashing with other scripts;
  - `name` is the name we want to use when referring to the module in the front-end app, e.g. `const bar = Foo.bar();`, note that it is only useful if the script we build has an `export` statement;
  - `sourcemap` lets us describe how we want the module sourcemap to be generated, a sourcemap is extremely useful when debugging code. Here chose `inline` to have it contained in the generated bundled module file.

### Testing the Configuration

Let's give a quick test to Rollup and our configuration. Inside `src/scripts` we will create a directory `utils` and add an `operations.js` file in it:
```bash
mkdir src/scripts/utils
touch src/scripts/utils/operations.js
```
`operations.js` will contain two functions, `sum` and `prod`, both returning the sum  and the product of two arguments respectively. These two functions are exported by the `operations` module:
```js
// src/scripts/operations.js
const sum = (a,b)=>{ return a+b; }
const prod = (a,b)=>{ return a*b; }
export {sum, prod};
```
Inside `src/scripts` we will create the module `foo.js`:
```bash
touch src/scripts/foo.js
```
Which will load the functions from `operations.js` and log the result of a sum on two variables:
```js
// src/scripts/foo.js
import {sum, prod} from './utils/operations.js';
const A = 4;
const B = 5;
export default function(){
    console.log(sum(A,B));
}
```
We can then run Rollup on `src/scripts/foo.js`, note the option `-c` which tells Rollup to use the configuration file we have made earlier:
```bash
./node_modules/.bin/rollup -c
```
And then check the resulting module in `app/dist/foo.js`: 
```js
// app/dist/foo.js
var Foo = (function () {
    'use strict';

    const sum = (a,b)=>{
        return a+b;
    };

    const A = 4;
    const B = 5;

    function foo () {
      console.log(sum(A, B));
    }

    return foo;

}());
//# sourceMappingURL= ...
```
Right then we can note a few things:
- the content of `operations.js` and `foo.js` have been bundled together;
- only the function `sum` was extracted from operations, that the *tree-shaking* from Rollup: because `foo.js` does not use `prod`, there is no need to bundle it;
- the sourcemap has been added to the file

## Babel

[Babel](https://babeljs.io/) is a JavaScript transpiler, taking code following modern JavaScript standards and producing the corresponding code in earlier versions of JavaScript with more browser support. We are first going to add two packages from Babel:
```node
 npm install --save-dev @babel/core @babel/preset-env
```
And then one [Rollup plugin](https://github.com/rollup/plugins/tree/master/packages/babel) to integrate Babel:
```node
 npm install --save-dev @rollup/plugin-babel
```
Next, we can create the configuration file for Babel, `.babelrc`, telling it which preset to use when transpiling:
```js
// .babelrc
{
    "presets": [
        ["@babel/preset-env", {
            "modules": false
        }]
    ]
}
```
The [`env` preset](https://babeljs.io/docs/en/babel-preset-env) is a smart preset that uses [Browserlist](https://github.com/browserslist/browserslist#queries) under the hood to determine which syntax is best to transpile to.
The final step is to let Rollup know that it should call babel during the bundling process. To do so we are going to update the Rollup configuration file:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';

export default {
    input: 'src/scripts/foo.js',
    output: { ... },
    plugins: [
        babel({
            exclude:'node_modules/**',
            babelHelpers: 'bundled'
        })
    ]
}
```
After importing the babel plugin, we call it in the `plugins` list and instruct it to ignore the code from dependencies
Now, when running Rollup:
```bash
./node_modules/.bin/rollup -c
```
We get the following result in `app/dist/foo.js`:
```js
// app/dist/foo.js
var Foo = (function () {
    'use strict';

    var sum = function sum(a, b) {
      return a + b;
    };

    var A = 8;
    var B = 9;

    function foo () {
      console.log(sum(A, B));
    }

    return foo;

}());
//# sourceMappingURL=
```
Because we used the `defaults` browserslist configuration, the `sum` function has been changed from an arrow definition to a normal `function` statement, and `const` has been replaced with `var`.

If we were to enter `"browserslist": ["last 2 Chrome versions"]` in our `package.json` (meaning we are targeting an environment limited to the last 2 versions of Chrome), there would not be any changes to the bundle, given that the last versions of Chrome are fully compatible with ES6 features.

## ESLint

[ESLint](https://eslint.org/) is a linter, a program that will analyse our code to correct syntax and detect problems (missing brackets/parentheses, unused variables, etc.) during the build process. We are first going to add it to our project:
```node
npm install --save-dev eslint
```
As well as a [Rollup plugin](https://github.com/rollup/plugins/tree/master/packages/eslint) for it:
```node
npm install --save-dev @rollup/plugin-eslint
```

Next, we need to configure ESLint, using the `.eslintrc.json` file, which can be done by using the following command:
```bash
./node_modules/.bin/eslint --init
```
ESLint will then prompt a series of questions to initialise the configuration file:
```
? How would you like to use ESLint?
  > To check syntax and find problems
? What type of modules does your project use?
  > JavaScript modules (import/export)
? Which framework does your project use?
  > None of these
? Does your project use TypeScript?
  > No
? Where does your code run?
  > Browser
? What format do you want your config file to be in?
  > JSON
```
Our project should then include a new `.eslintrc.json` file, with this content:
```json
// .eslintrc.json
{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "rules": {
    }
}
```
We can then add more the list of `rules`, for example, having indents set to 4 spaces, using Unix linebreaks, using single quotes, enforcing semicolons at the end of each statement, and warning us of unused variables:
```json
// .eslintrc.json
{ ...
    "rules":{
        "indent": ["warn", 4],
        "linebreak-style": ["warn", "unix"],
        "quotes": ["warn", "single"],
        "semi": ["warn", "always"],
        "no-unused-vars": ["warn"]
    }
}
```
Next, we can update `rollup.config.js` to include ESLint in the process:
```js 
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';

export default {
    input: 'src/scripts/foo.js',
    output: { ... },
    plugins: [
        eslint({
            fix: true,
            exclude: ['./node_modules/**', './src/styles/**'],
        }),
        babel({ ... })
    ]
}
```

As with Babel, we have first imported it, before including it in the list of plugins. We have instructed it to ignore our styles files, and let fix some of the simpler issues silently (e.g. semicolons, indentation, etc.).
Now, when we run:
```bash
./node_modules/.bin/rollup -c
```
We can notice the following terminal output, informing us that `foo.js` defines (or imports) `prod` but does not use it.
```
/.../project/src/scripts/foo.js
  1:14  warning  'prod' is defined but never used  no-unused-vars

✖ 1 problem (0 errors, 1 warning)
```
And ESLint has fixed some of the trivial syntax issues for us:
```js
// src/scripts/operations.js before build
const sum = (a,b)=>{
    return a+b;
};
const prod = (a,b)=>{
return a*b
}

export {sum, prod};

// src/scripts/operations.js after build
const sum = (a,b)=>{
    return a+b;
};
const prod = (a,b)=>{
    return a*b;
};

export {sum, prod};
```

## Third-Party and Non-ES Modules Support

By default, Rollup does not load third party libraries from `node_modules` properly. To enable that we need to use another Rollup plugin, [node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve):
```node
npm install --save-dev @rollup/plugin-node-resolve
```
Then, while we are developing ES modules, some of our code dependencies in `node_modules` would have been developed in a non-ES module format: CommonJS. Trying to load these in our bundle will ultimately fail, but Rollup has a plugin to help with that, [commonjs](https://github.com/rollup/plugins/tree/master/packages/commonjs):
```node
npm install --save-dev @rollup/plugin-commonjs
```
Once we have added those plugins to the project, we can add them the Rollup configuration:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
    input: 'src/scripts/foo.js',
    output: { ... },
    plugins: [
        resolve(),
        commonjs(),
        eslint({ ... }),
        babel({ ... })
    ]
}
```

## Enabling the ENV Variable

Using environment variables can be helpful in the development process, for example turning logging on and off depending on the type of build we are doing, for development or production.
Let's add the following to `src/scripts/foo.js`:
```js
// src/scripts/foo.js
...
if(ENV != 'production'){
    console.log('not in production');
}
...
```
A piece of code that logs a message when the build is not for production. However, the variable `ENV` is undefined there. To fix that we can add the Rollup plugin [replace](https://github.com/rollup/plugins/tree/master/packages/replace):
```node
npm install --save-dev @rollup/plugin-replace
```
And use it in the configuration file:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';

export default {
    input: 'src/scripts/foo.js',
    output: { ... },
    plugins: [
        resolve(),
        commonjs(),
        eslint({ ... }),
        babel({ ... }),
        replace({
            exclude: 'node_modules/**',
            ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
        })
    ]
}
```
What it will do is replace, in our bundle (excluding code that comes from third-party libraries in `node_modules`), the occurrences of `ENV` with the value of `NODE_ENV` or `development` by default. What we must remember is to update `.eslintrc.json` to let ESLint know that `ENV` is a global variable and not undeclared:
```json
// .eslintrc.json
{
    "env": { ... },
    "globals": {
        "ENV": true
    },
    "extends": "eslint:recommended",
    "parserOptions": { ... },
    "rules": { ... }
}
```
Then when building normally:
```bash
./node_modules/.bin/rollup -c
```
`app/dist/foo.js` will include the following:
```js
// app/dist/foo.js
...
{
    console.log('not in production');
}
...
```
However, building for production:
```bash
NODE_ENV=production ./node_modules/.bin/rollup -c
```
Will remove the code above from `app/dist/foo.js`.

## Terser

Generating a bundle that has many dependencies, from our code or third-party packages, will result in a large JavaScript file. To optimize the bundle it is useful to integrate [Terser](https://terser.org/) into our build process. What Terser does is it removes comments, shorten variables names, cut whitespaces and minify our code to make it the shortest possible.
Again Terser can be integrated with Rollup using a [plugin](https://github.com/TrySound/rollup-plugin-terser):
```node
npm install --save-dev rollup-plugin-terser
```
And configure it in `rollup.config.js`:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import {terser} from 'rollup-plugin-terser';

export default {
    input: 'src/scripts/foo.js',
    output: {
        file: 'app/dist/foo.js',
        format: 'iife',
        name: 'Foo',
        sourcemap: (process.env.NODE_ENV === 'production' ? false : 'inline')
    },
    plugins: [
        resolve(),
        commonjs(),
        eslint({ ... }),
        babel({ ... }),
        replace({ ... }),
        (process.env.NODE_ENV === 'production' && terser())
    ]
}
```
Because it is useful to be able to inspect and see our code during development, we are only letting `terser` execute if `NODE_ENV` is set to `production`. Similarly, we have turned off the sourcemap in production to reduce the bundle size.
If we now build our module for production:
```bash
NODE_ENV=production ./node_modules/.bin/rollup -c
```
Here is how it looks:
```js
// app/dist/foo.js
var Foo=function(){"use strict";return function(){console.log(8+9)}}();
```

## Less and PostCSS

Now that we have addressed our scripts, we can focus on our styles. In this setup, we will look at the CSS preprocessor [Less](http://lesscss.org/) which lets us write CSS simpler, use variables and mixins. We can add it to the project with the following command:
```node
npm install --save-dev less
```
To process Less files we will use [PostCSS](https://postcss.org/), which is a JavaScript build tool for CSS, Less, and other CSS preprocessors. It also comes with a built-in minifier. We can add it to the project with a Rollup [plugin](https://github.com/egoist/rollup-plugin-postcss):
```node
npm install --save-dev rollup-plugin-postcss
```
One of the most interesting features of PostCSS is [Autoprefixer](https://github.com/postcss/autoprefixer). Much like Babel, it checks our `browserslist` requirement to add prefixes to CSS rules, ensuring cross-browser compatibilities. We can add it to the project with the following command:
```node
npm install --save-dev autoprefixer
```

We can now set this up with Rollup, in the configuration file:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import {terser} from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';

export default {
    input: 'src/scripts/foo.js',
    output: { ... },
    plugins: [
        resolve(),
        commonjs(),
        eslint({ ... }),
        babel({ ... }),
        replace({ ... }),
        (process.env.NODE_ENV === 'production' && terser()),
        postcss({
            plugins: [autoprefixer()],
            inject: false,
            extract: true,
            sourceMap: (process.env.NODE_ENV === 'production' ? false : 'inline'),
            minimize: (process.env.NODE_ENV === 'production')
        })
    ]
}
```
As we can see, PostCSS calls Autoprefixer as a plugin, it uses Less in the background automatically when detecting Less files. The `inject` option lets us define whether the JavaScript module will inject styles in the `<head>` of our page (`true`) or not (`false`). Similarly, the `extract` option lets us define whether a separate stylesheet will be generated next to the JavaScript module (`true`) or not (`false`). This stylesheet will have the same filename as the JavaScript module, with a `.css` extension instead. Then, we set the `sourcemap` and `minimize` options depending on `NODE_ENV` as we did with the JavaScript module.
Processing a stylesheet can then be done by simply importing it in our JavaScript module:
```less
/* src/styles/foo.less */
@clr: red;
h1{
  color: @clr;
}
```
```js
// src/scripts/foo.js
import '../styles/foo.less';
...
```
```bash
NODE_ENV=production ./node_modules/.bin/rollup -c
```
```css
/* app/dist/foo.css */
h1{color:red}
```

## Automating Builds

The next step of this setup is to make use of node scripts to automate the build process.
First, we are going to install `reload`, an HTTP server program that comes with a live-reload functionality:
```node
npm install --save-dev reload
```
Reload can then serve `app/` to `localhost` and reload anytime it detects a change.
Meanwhile, Rollup comes with a watch option, `-w`, that keeps it listening to any changes in our source file to automatically re-build them. We can therefore combine the two in one Node script in our `package.json`:
```json
// package.json
...
"scripts": {
    "serve": "./node_modules/.bin/reload -b -d ./app -p 8000 | ./node_modules/.bin/rollup -c -w"
}
...
```
Then, running:
```node
npm run server
```
Will launch both Reload and Rollup: Rollup listening to any changes on the source file and re-building them, and Reload detecting changes in the build files and re-serving them on our test webpage `localhost:8000`.
We can then add a second script for production build:
```json
// package.json
...
"scripts": {
    "serve": "./node_modules/.bin/reload -b -d ./app -p 8000 | ./node_modules/.bin/rollup -c -w",
    "build": "NODE_ENV=production ./node_modules/.bin/rollup -c"
}
...
```
Then, we can run the following to simply build our production application:
```node
npm run build
```

## Building Multiple Modules

Finally, we can setup `rollup.config.js` to allow multiple modules to be bundled separatley:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import {terser} from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';

const srcDir = 'src/scripts/',
    distDir = 'app/dist/';

const plugins = () => [
    resolve(),
    commonjs(),
    eslint({ ... }),
    babel({ ... }),
    replace({ ... }),
    (process.env.NODE_ENV === 'production' && terser()),
    postcss({ ... })
];

function setupBuild(src, dist, name){
    return {
        input: srcDir+src,
        output: {
            file: distDir+dist,
            format: 'iife',
            name,
            sourcemap: (process.env.NODE_ENV === 'production' ? false : 'inline')
        },
        plugins:plugins()
    }
}

export default [
    setupBuild('foo.js', 'foo.js', 'Foo'),
    setupBuild('bar.js', 'bar.js', 'Bar')
]
```
Additional modules can be added using `setupBuild`. Note that we use a function to return plugins to *"clean"* their buffers.

## Final Notes

### Using Builds

The built modules can be simply loaded into an HTML page:
```html
<!-- app.index.html -->
<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="dist/foo.css">
        <script src="dist/foo.js"></script>
    </head>
    <body>
        <h1>Hello World</h1>
        <script>
            Foo();
        </script>
    </body>
</html>
```
As any other JavaScript code, it will get executed upon loading. If the module exports a value (object or function), it can be accessed using the name given in the Rollup configuration (third parameter of `setupBuild`). For example: `Foo()`, `Foo[...]` or `Foo.bar`.

### Managing Warnings

Some third-party libraries like [d3.js](https://d3js.org/) will have circular dependencies within them, which Rollup will warn us about when building the module. To avoid getting many warning messages, we can add a warning filter to in the Rollup configuration:
```js
// rollup.config.js
...
function setupBuild(src, dist, name){
    return {
        input: srcDir+src,
        output: { ... },
        plugins:plugins(),
        onwarn: function(warning, warner){
            // if circular dependency warning
            if (warning.code === 'CIRCULAR_DEPENDENCY'){
                // if comming from a third-party
                if(warning.importer && warning.importer.startsWith('node_modules/')){
                    // ignore warning
                    return;
                }
            }
            // Use default for everything else
            warner(warning);
        }
    }
}
...
```

### Final Project Structure

This how the project directory should look like now:
```
-- project/
   |-- app/
   |   |-- dist/
   |   |   |-- foo.js
   |   |   |-- foo.css
   |   |-- index.html
   |-- src/
   |   |-- scripts/
   |   |   |-- utils/
   |   |   |   |-- operations.js
   |   |   |-- foo.js
   |   |-- styles/
   |   |   |-- foo.less
   |-- .babelrc
   |-- .eslintrc.json
   |-- package-lock.json
   |-- package.json
   |-- rollup.config.js
```
The `package.json` file should contain the following:
```json
// package.json
{
  ...
  "scripts": {
    "serve": "./node_modules/.bin/reload -b -d ./app -p 8000 | ./node_modules/.bin/rollup -c -w",
    "build": "NODE_ENV=production ./node_modules/.bin/rollup -c"
  },
  ...
  "devDependencies": {
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "@rollup/plugin-babel": "^5.2.2",
    "@rollup/plugin-commonjs": "^17.0.0",
    "@rollup/plugin-eslint": "^8.0.1",
    "@rollup/plugin-node-resolve": "^11.1.0",
    "@rollup/plugin-replace": "^2.3.4",
    "autoprefixer": "^10.2.1",
    "eslint": "^7.17.0",
    "less": "^4.1.0",
    "reload": "^3.1.1",
    "rollup": "^2.36.1",
    "rollup-plugin-postcss": "^4.0.0",
    "rollup-plugin-terser": "^7.0.2"
  },
  "browserslist": [
    "defaults"
  ]
  ...
}
```
`.babelrc` should look like this:
```json
// .babelrc
{
    "presets": [
        ["@babel/preset-env", {
            "modules": false
        }]
    ]
}
```
`.eslintrc.json` should look like this:
```json
// .eslintrc.json
{
    "env": {
        "browser": true,
        "es2021": true
    },
    "globals": {
        "ENV": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "rules": {
        "indent": ["warn", 4],
        "linebreak-style": ["warn", "unix"],
        "quotes": ["warn", "single"],
        "semi": ["warn", "always"],
        "no-unused-vars": ["warn"]
    }
}
```
And finally, `rollup.config.js` should have the following:
```js
// rollup.config.js
import babel from '@rollup/plugin-babel';
import eslint from '@rollup/plugin-eslint';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import {terser} from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';

const srcDir = 'src/scripts/',
    distDir = 'app/dist/';

const plugins = () => [
    resolve(),
    commonjs(),
    eslint({
        fix: true,
        exclude: ['./node_modules/**', './src/styles/**']
    }),
    babel({
        exclude: 'node_modules/**',
        babelHelpers: 'bundled'
    }),
    replace({
        exclude: 'node_modules/**',
        ENV: JSON.stringify(process.env.NODE_ENV || 'development')
    }),
    (process.env.NODE_ENV === 'production' && terser()),
    postcss({
        plugins: [autoprefixer()],
        inject: false,
        extract: true,
        sourceMap: (process.env.NODE_ENV === 'production' ? false : 'inline'),
        minimize: (process.env.NODE_ENV === 'production')
    })
]

function setupBuild(src, dist, name){
    return {
        input: srcDir+src,
        output: {
            file: distDir+dist,
            format: 'iife',
            name,
            sourcemap: (process.env.NODE_ENV === 'production' ? false : 'inline')
        },
        plugins:plugins(),
        onwarn: function(warning, warner){
            if (warning.code === 'CIRCULAR_DEPENDENCY'){
                if(warning.importer && warning.importer.startsWith('node_modules/')){
                    return;
                }
            }
            warner(warning);
        }
    }
}

export default[
    setupBuild('foo.js', 'foo.js', 'Foo')
]
```