Wednesday, 26 September 2018

JavaScript modules, export and import

Let's consider web app project root directory which contains the following files:

scripts/common.js
scripts/array_demo.js
views/array_demo.html


Without modules:


scripts/common.js:

function clearElement(id) {...}
function log(text) {...}

scripts/array_demo.js:

function foo() {
    clearElement(...);
    log(...);
};

In HTML file scripts have to be added in order so functions, objects, or primitive values from previously included scripts are visible in scripts that use them:

views/array_demo.html:

         ...
         <script src='/scripts/common.js'></script>
         <script src='/scripts/array_demo.js'></script>
     </body>
 </html>

This can be a problem in case when there are many and/or complex dependencies. The solution is using ECMAScript modules. This will work in HTML5-compliant browsers.


With modules:


What is the difference between a common (classic) JavaScript script and a module?


A classic script is just a standard JavaScript script as you know it. A module script is one that contains an ES6 module, i.e. it uses (or: can use) import and export declarations. [source]

scripts/common.js:

function clearElement(id) {...}
function log(text) {...}

export {
    clearElement,
    log
};

scripts/array_demo.js:

import { clearElement, log} from './common.js';

function foo() {
    clearElement(...);
    log(...);
};

views/array_demo.html:

         ...
         <script type="module" src='/scripts/array_demo.js'></script>
     </body>
 </html>


export and import statements are defined in ECMAScript 2015 (6th Edition, ECMA-262).

module script type is available only in HTML5-compliant browsers [source].

Modules VS Require.JS


Require.JS "modularizes" JS scripts and basically emulates import/export functionality. With ES6 modules available there is no need to use Require.JS.

Further reading:


ECMAScript modules in browsers
ES6 In Depth: Modules
HTML/Element/script (MDN)
ECMAScript 6 modules: the final syntax
export (MDN)
import (MDN)
CommonJS vs AMD vs RequireJS vs ES6 Modules

How to have two pages with same titles withing the same space in Confluence

If you try to create and save a page in Confluence which has the same title as some other page in the same space, you'll get this error:


This is because it is NOT possible to have multiple pages with the same name in a single space. Workarounds include prepending page name with (shortened) parent page name or adding invisible Unicode characters.

References:

https://community.atlassian.com/t5/Questions/Pages-with-same-name-in-space/qaq-p/443236
https://jira.atlassian.com/browse/CONFSERVER-1095
https://community.atlassian.com/t5/Confluence-questions/How-to-create-two-pages-with-the-same-name-in-different/qaq-p/875755
https://community.atlassian.com/t5/Confluence-questions/Not-able-to-create-a-page-with-the-same-title-as-another-page/qaq-p/332910
https://community.atlassian.com/t5/Confluence-questions/Create-multiple-pages-with-the-same-name-within-a-space/qaq-p/138708


Tuesday, 25 September 2018

ESLint VSCode plugin

If we install ESLint plugin for VSCode but not ESLint package itself, we'll get this error in VSCode:



In my post Introduction to ESLint I followed the ESLint guide for its local installation which instructs creating its config file in node_modules/.bin. This might not be the optimal (or better to say, working) solution if we want to use ESLint plugin for VSCode.

I installed that plugin and opened a directory of a test web application which contains JS files and has locally installed eslint. .eslintrc.json was created in .\node_modules\.bin. After couple of seconds upon opening project directory I got this error in ESLint output:

[Info  - 3:18:08 PM] ESLint server stopped.
[Info  - 3:18:08 PM] ESLint server running in node v8.9.3
[Info  - 3:18:08 PM] ESLint server is running.
[Info  - 3:18:11 PM] ESLint library loaded from: c:\dev\github\demo-html-css-js\node_modules\eslint\lib\api.js
[Error - 3:18:11 PM]
Failed to load plugin react: Cannot find module 'eslint-plugin-react'
Happened while validating C:\dev\github\demo-html-css-js\scripts\objects_demo.js
This can happen for a couple of reasons:
1. The plugin name is spelled incorrectly in an ESLint configuration file (e.g. .eslintrc).
2. If ESLint is installed globally, then make sure 'eslint-plugin-react' is installed globally as well.
3. If ESLint is installed locally, then 'eslint-plugin-react' isn't installed correctly.

Consider running eslint --debug C:\dev\github\demo-html-css-js\scripts\objects_demo.js from a terminal to obtain a trace about the configuration files used.


I found here that global .eslintrc located in my user directory could be the cause of this. And indeed, I found c:\Users\User\.eslintrc with the following content:

{
/* See all the pre-defined configs here: https://www.npmjs.com/package/eslint-config-defaults */
"extends": "defaults/configurations/eslint",
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"react"
],
"env": {
"amd": true,
"browser": true,
"jquery": true,
"node": true,
"es6": true,
"worker": true
},
"rules": {

"eqeqeq": 2,
"comma-dangle": 1,
"no-console": 0,
"no-debugger": 1,
"no-extra-semi": 1,
"no-extra-parens": 1,
"no-irregular-whitespace": 0,
"no-undef": 0,
"no-unused-vars": 0,
"semi": 1,
"semi-spacing": 1,
"valid-jsdoc": [
2,
{ "requireReturn": false }
],

"react/display-name": 2,
"react/forbid-prop-types": 1,
"react/jsx-boolean-value": 1,
"react/jsx-closing-bracket-location": 1,
"react/jsx-curly-spacing": 1,
"react/jsx-indent-props": 1,
"react/jsx-max-props-per-line": 0,
"react/jsx-no-duplicate-props": 1,
"react/jsx-no-literals": 0,
"react/jsx-no-undef": 1,
"react/jsx-sort-prop-types": 1,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-danger": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-direct-mutation-state": 1,
"react/no-multi-comp": 1,
"react/no-set-state": 0,
"react/no-unknown-property": 1,
"react/prop-types":0,
"react/react-in-jsx-scope": 0,
"react/require-extension": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1
}
}


I moved my .eslintrc.json from node_modules\.bin\ to the project's root and added in it:

"root": true

After this I re-opened project's workspace in VSCode and ESLint didn't report any errors and worked with no issues.

Introduction to ESLint


What is ESLint?


JavaScript linting utility

What is linting?


Code linting is a type of static analysis that is frequently used to find problematic patterns or code that doesn’t adhere to certain style guidelines.

Why do we need linting in JavaScript?


JavaScript, being a dynamic and loosely-typed language, is especially prone to developer error. Without the benefit of a compilation process, JavaScript code is typically executed in order to find syntax or other errors. Linting tools like ESLint allow developers to discover problems with their JavaScript code without executing it.

Installation


ESLint community is advocating local installation although a global installation is used in the guide on ESLint's webpage [source].

"If you want to include ESLint as part of your project’s build system, we recommend installing it locally." [Local Installation and Usage]

To install ESLint locally (after running npm init):

> npm install eslint --save-dev

package.json now contains:

"devDependencies": {
  "eslint": "^5.6.0"
}

Configuration


To set up configuration file use:

C:\dev\github\demo-html-css-js\node_modules\.bin>eslint --init
? How would you like to configure ESLint? (Use arrow keys)
  Use a popular style guide
> Answer questions about your style
  Inspect your JavaScript file(s)

NOTE: Read here why it might be better to run this command and create config file in the root directory of your project. If you choose to do so, navigate to root directory and run npx eslint --init.

This option makes eslint asking a set of questions:

? How would you like to configure ESLint? Answer questions about your style
? Which version of ECMAScript do you use? ES2018
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? No
? Do you use JSX? No
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Windows
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON
Successfully created .eslintrc.json file in C:\dev\github\demo-html-css-js\node_modules\.bin

This config file looks like this:

..\node_modules\.bin>type .eslintrc.json
{
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "windows"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
}


Usage


We can now call ESLint from command line:

..\node_modules\.bin> eslint <path_to_JavaScript_file>

References:


ESLint
How to disable “unexpected console statement” in Node.js?
disallow the use of console (no-console)

Introduction to Node Package Manager (npm)

What is npm?

  • package manager
  • task runner that can serve as a replacement for Gulp

How to install npm?


Upon installation user variable Path (in environment variables) gets a new entry:
C:\Users\user\AppData\Roaming\npm


How to see command line arguments?




How to check its version?




How to update npm?




To verify it:



Packages


See here a complete definition of the package.

Node applications usually use (or depend on) multiple packages. There are three types of packages and each of them is listed within the object with the same name in packages.json:

  • regular (dependencies) - used in development in production
  • development (devDependencies) - packages are used only during application development and testing; we don't want to include them in the production and make users of our app unnecessarily download and build them 
  • optional (optionalDependencies) - dependencies which are used optionally - if they are found; their absence does not make application to fail

Configuration


npm gets its config settings from:

  • command line
  • environment variables
  • npmrc files
  • package.json file (in some cases)

.npmrc 


Global (per machine) 

Location: C:\Users\user\AppData\Roaming\npm\etc\npmrc

To find its location use:

>npm config get globalconfig
C:\Users\user\AppData\Roaming\npm\etc\npmrc

User-specific

Location:  %USERPROFILE%\.npmrc

To find its location use:

>npm config get userconfig
C:\Users\user\.npmrc

Local (per project) 

Location: in project's root directory.

It defines where can npm look for and fetch packages - package registries by listing their URLs:

# use npmjs registry by default
registry=https://registry.npmjs.org/

# use xyz registry for packages in @xyz scope
@xyz:registry=https://xyz.com/npm

package.json


It lets npm know what the name of your package is as well as what dependencies it uses.

It is created and initialized via npm init command. This can be done retroactively - npm init can be executed for the already existing project...it will only add to it package.json file.

It is a manifest of your project that includes the packages and applications it depends on, information about its unique source control, and specific metadata like the project's name, description, and author. [source]

The biggest reason for using package.json to specify a project’s dependencies is portability. For example, when you clone someone else’s code, all you have to do is run npm i in the project root and npm will resolve and fetch all of the necessary packages for you to run the app. [source]

All modules from package.json are installed to ./node_modules/. [source] This is npm install's default behavior. [source]

npm opens and reads package.json from the current directory. If it can't find it, it issues an error like:


Working with package.json
package.json - Specifics of npm's package.json handling
What's the difference between tilde(~) and caret(^) in package.json?


Properties


scripts


If within the project we have some tool we want to call frequently e.g. after every code change we don't want to type it every time but want to automate the process by adding a command within scripts object. See Babel example.



-w instructs npm to watch for changes in the src folder. Every time you make a change to a file in src, this command is automatically executed.

browser

Specifies alternative files to load in case bundling is done for the browser.

It is provided by a module author as a hint to javascript bundlers or component tools when packaging modules for client side use. [source]

Example:

"browser": {
 "vue": "vue/dist/vue.min.js"
},

package-lock.json

package-lock.json is automatically generated for any operations where npm modifies either the node_modules tree, or package.json. [package-lock.json - A manifestation of the manifest]

Is there a way to force npm to generate package-lock.json?
Shall package-lock.json be added to version control?


CLI commands

adduser
alias: login

>npm login
Username: bojan
Password:
Email: (this IS public) bojan@example.com

Logged in as bojan on https://registry.npmjs.org/.

If logging for the first time and if %USERPROFILE%/.npmrc does not already exist, it will be created with the content similar to this one:

//registry.npmjs.org/:_authToken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx


cache
Used to add, list, or clean the npm cache folder.[npm-cache]
Try clearing the npm cache

cache clean

Cleans the npm cache.  Deletes all data out of the cache folder.

config
used to update and edit the contents of the user and global npmrc files.
Sub-commands:
config get


init
Creates package.json. It is used when creating/initializing a package (not when installing it).

install
Installs in the local node_modules folder all dependencies listed in package.json.

install --production
npm will not install modules listed in devDependencies. Same applies when --production is omitted but the NODE_ENV environment variable is set to production.

install -g npm
Updates npm package itself.

install --save-dev package1 package2...
Installs packages locally (in  project's devDependencies)


list
Lists all packages installed locally (for the current project) and also their dependencies.

list --depth=0
Lists all packages installed locally but without their dependencies.

Example:



list -g
Lists all packages installed globally and also full path to the installation directory.
On Windows, that directory is: C:\Users\user\AppData\Roaming\npm.

Example:



list -g --depth=0
Lists globally installed packages but not their dependencies.

Example:


login
see adduser

run script_name
run-script script_name
Runs an arbitrary command (script) from a package's scripts key (scripts object in package.json). Optional argument: command. If no command is provided, it will list the available scripts (all properties of "scripts" object). [npm-run-script]

search search_terms...
Searches the registry for packages matching the search terms. [npm-search]

start
(short for run start)
npm runs the start script which is a command defined under scripts key in packages.json. This command usually starts the application with special configuration options (all listed in packages.json).

uninstall
uninstalls a package, completely removing everything npm installed on its behalf.
-S, --save: Package will be removed from your dependencies.
-D, --save-dev: Package will be removed from your devDependencies.
-O, --save-optional: Package will be removed from your optionalDependencies.

If all packages are uninstalled package.json contains empty dependency list:

"dependencies": {}



uninstall -g package_name
uninstall --global package_name
Uninstalls globally installed package

update
Updates local packages to their latest versions allowed by the version specified with syntax ^ ("compatible with") or ~ ("reasonably close to") (see here for explanation in detail)  in package.json. Installs missing packages. Changes package.json and package-lock.json.
npm install vs. update - what's the difference?

update -g 
Updates global packages.

view package_name
Shows data about a package. This can be used to check if some package actually exists as if package doesn't exist a message "404 Not found : package_name" is printed out. [npm-view]

    Packages


    Products (tools, apps) used to come with a single package which contained both the core functionality and CLI.  Nowadays products are moving away from coupling the CLI and library together. The CLI now usually lives in the xxx-cli package.

    Installing package locally vs globally


    Sometimes it is better to install a package locally (project by project) then globally. There are couple of reasons for this:
    • Different projects on the same machine can depend on different versions of the package allowing you to update them individually.
    • Not having an implicit dependency on the environment you are working in makes your project far more portable and easier to setup.

    Where to install development dependencies?


    Shall development dependencies (e.g. Rollup, Webpack, Browserify, Gulp...) be installed globally or locally? Looking at reasons listed in the previous paragraph it makes more sense to install them locally. Benefits:
    • if required, different versions of these tools can be used in different projects 
    • all required dependencies can be installed in one go with npm install. There is no need for extra steps for installing packages globally (which, in turn, might also require root or admin privileges opening potential security holes...)

    How to run locally installed packages?


    • use npx to run their binaries directly (usually for some quick demonstrations, not in the real-world projects)
    • add them to scripts section of package.json and call them via npm run
    • add them to gulpfile and then Gulp takes care of executing them

    How to find quickly the version of some package installed on the local machine?


    >npm list dtrace-provider
    my-app@0.1.0 C:\dev\my-app
    `-- web-ext@2.9.1
    `-- bunyan@1.8.12
    `-- dtrace-provider@0.8.7

    Use -g to look for package installed globally on the machine.

    Resources

    https://stackoverflow.com/questions/18412129/how-can-i-update-npm-on-windows
    https://www.npmjs.com/package/npm-windows-upgrade
    http://thecodebarbarian.com/3-neat-tricks-with-npm-run
    Common npm mistakes

    Introduction to Browserify

    Friday, 21 September 2018

    How to use "Insert JIRA Issue/Filter" macro in Confluence

    To add Insert JIRA Issue/Filter macro in Confluence follow the steps as in this image:



    When using this macro in Confluence, it is better to use filter ID rather than JQL. JQL con contain various values which can change in time e.g. the name of the fixVersion field or project name etc...If we change JQL string in JIRA we have to change it everywhere we used it in Confluence. If we use in Confluence filter ID (which is actually JQL query ID), the only place we have to make a change is JQL in JIRA - filter ID remains the same.


    Make sure that filter in JIRA is set to be shared e.g. with anyone logged-in. This will allow Confluence to execute filter in JIRA and display all JIRA tasks from it.

    Thursday, 20 September 2018

    Introduction to Gulp


    What is Gulp?


    Gulp is a:

    • toolkit for automating & enhancing JavaScript development workflow
    • Node.js based build system
    • JavaScript task runner which can automate common tasks (e.g. minification, error checking, bundling etc...)

    How to install Gulp?


    Two packages have to be installed: CLI and the core one.

    CLI package has to be installed globally:

    > npm install --global gulp-cli

    The local installation guarantees that the project build will not break even if system install is upgraded. The global gulp install (CLI) is there only to provide a binary command in the path. Gulp is designed in a way that a specific project can depend on a specific version of the build system so it will still build no matter the state or installed version of the global package.

    How to check if gulp CLI is installed?

    C:\dev\github\gulp-demo>npm list -g gulp-cli
    C:\Users\user\AppData\Roaming\npm
    `-- gulp-cli@2.0.1

    Gulp core package has to be installed locally. To check if it's already been installed we can use:

    C:\dev\github\gulp-demo>npm list gulp
    gulp-demo@1.0.0 C:\dev\github\gulp-demo
    `-- (empty)

    Let's install it (but if package.json hasn't been created yet, execute npm init):

    C:\dev\github\gulp-demo>npm install --save-dev gulp
    npm WARN deprecated gulp-util@3.0.8: gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5
    npm WARN deprecated graceful-fs@3.0.11: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js
    npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
    npm WARN deprecated minimatch@0.2.14: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
    npm WARN deprecated graceful-fs@1.2.3: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js
    npm notice created a lockfile as package-lock.json. You should commit this file.
    + gulp@3.9.1
    added 253 packages from 162 contributors and audited 1112 packages in 9.569s
    found 5 vulnerabilities (1 low, 4 high)
      run `npm audit fix` to fix them, or `npm audit` for details

    Let's verify installation:

    C:\dev\github\gulp-demo>npm list gulp
    gulp-demo@1.0.0 C:\dev\github\gulp-demo
    `-- gulp@3.9.1

    gulp package does not represent Gulp's latest version. [read here and here]
    In order to get the latest version we should use gulp@next.

    So let's uninstall version 3.9.1 and install the latest Gulp:

    C:\dev\github\gulp-demo>npm uninstall gulp
    removed 253 packages in 3.447s
    found 0 vulnerabilities

    C:\dev\github\gulp-demo>npm install --save-dev gulp@next
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    + gulp@4.0.0
    added 318 packages from 212 contributors and audited 5472 packages in 15.089s
    found 0 vulnerabilities

    How to check Gulp version?


    C:\dev\github\gulp-demo>gulp -v
    [13:49:01] CLI version 2.0.1
    [13:49:01] Local version 4.0.0

    How to configure Gulp?


    Let's try to run gulp now:

    \gulp-demo>gulp
    [13:49:32] No gulpfile found

    Gulp requires a gulpfile.js file at the root of the project. Gulpfile contains tasks it can run from the command line. Let's define a default task which prints Hello world! to the console:

    \gulp-demo\gulpfile.js:

    var gulp = require('gulp');

    gulp.task('default', function() {
      // place code for your default task here
      console.log('Hello, world!');
    });

    When we run gulp with no arguments, it will run the task named as default:

    C:\dev\github\gulp-demo>gulp
    [13:55:13] Using gulpfile C:\dev\github\gulp-demo\gulpfile.js
    [13:55:13] Starting 'default'...
    Hello, world!
    [13:55:13] The following tasks did not complete: default
    [13:55:13] Did you forget to signal async completion?

    We could have explicitly named the task we want to run:

    C:\dev\github\gulp-demo>gulp default
    [13:55:24] Using gulpfile C:\dev\github\gulp-demo\gulpfile.js
    [13:55:24] Starting 'default'...
    Hello, world!
    [13:55:24] The following tasks did not complete: default
    [13:55:24] Did you forget to signal async completion?

    Gulp CLI


    These are the Gulp CLI command line arguments:
    >gulp --help

    Usage: gulp [options] tasks

    Options:
      --help, -h              Show this help.                              [boolean]
      --version, -v           Print the global and local gulp versions.    [boolean]
      --require               Will require a module before running the gulpfile.
                              This is useful for transpilers but also has other
                              applications.                                 [string]
      --gulpfile, -f          Manually set path of gulpfile. Useful if you have
                              multiple gulpfiles. This will set the CWD to the
                              gulpfile directory as well.                   [string]
      --cwd                   Manually set the CWD. The search for the gulpfile, as
                              well as the relativity of all requires will be from
                              here.                                         [string]
      --verify                Will verify plugins referenced in project's
                              package.json against the plugins blacklist.
      --tasks, -T             Print the task dependency tree for the loaded
                              gulpfile.                                    [boolean]
      --tasks-simple          Print a plaintext list of tasks for the loaded
                              gulpfile.                                    [boolean]
      --tasks-json            Print the task dependency tree, in JSON format, for
                              the loaded gulpfile.
      --tasks-depth, --depth  Specify the depth of the task dependency tree.[number]
      --compact-tasks         Reduce the output of task dependency tree by printing
                              only top tasks and their child tasks.        [boolean]
      --sort-tasks            Will sort top tasks of task dependency tree. [boolean]
      --color                 Will force gulp and gulp plugins to display colors,
                              even when no color support is detected.      [boolean]
      --no-color              Will force gulp and gulp plugins to not display
                              colors, even when color support is detected. [boolean]
      --silent, -S            Suppress all gulp logging.                   [boolean]
      --continue              Continue execution of tasks upon failure.    [boolean]
      --series                Run tasks given on the CLI in series (the default is
                              parallel).                                   [boolean]
      --log-level, -L         Set the loglevel. -L for least verbose and -LLLL for
                              most verbose. -LLL is default.                 [count]

    --tasks
    Lists all tasks defined in gulpfile.
    Example:

    >gulp  --tasks
    [17:11:17] Tasks for C:\dev\gulp-demo\gulpfile.js
    [17:11:17] ├── build_brandA_environmentA
    [17:11:17] ├─┬ pack_brandA_environmentA
    [17:11:17] │ └─┬ <series>
    [17:11:17] │   ├── build_brandA_environmentA
    [17:11:17] │   └── <anonymous>
    [17:11:17] ├── build_brandB_environmentA
    [17:11:17] ├─┬ pack_brandB_environmentA
    [17:11:17] │ └─┬ <series>
    [17:11:17] │   ├── build_brandB_environmentA
    [17:11:17] │   └── <anonymous>
    [17:11:17] ├─┬ build
    [17:11:17] │ └─┬ <parallel>
    [17:11:17] │   ├── build_brandA_environmentA
    [17:11:17] │   └── build_brandB_environmentA
    [17:11:17] ├─┬ pack
    [17:11:17] │ └─┬ <parallel>
    [17:11:17] │   ├─┬ pack_brandA_environmentA
    [17:11:17] │   │ └─┬ <series>
    [17:11:17] │   │   ├── build_brandA_environmentA
    [17:11:17] │   │   └── <anonymous>
    [17:11:17] │   └─┬ pack_brandB_environmentA
    [17:11:17] │     └─┬ <series>
    [17:11:17] │       ├── build_brandB_environmentA
    [17:11:17] │       └── <anonymous>
    [17:11:17] ├─┬ watch
    [17:11:17] │ └─┬ <series>
    [17:11:17] │   ├─┬ build
    [17:11:17] │   │ └─┬ <parallel>
    [17:11:17] │   │   ├── build_brandA_environmentA
    [17:11:17] │   │   └── build_brandB_environmentA
    [17:11:17] │   └── watch
    [17:11:17] ├─┬ default
    [17:11:17] │ └─┬ <series>
    [17:11:17] │   └─┬ build
    [17:11:17] │     └─┬ <parallel>
    [17:11:17] │       ├── build_brandA_environmentA
    [17:11:17] │       └── build_brandB_environmentA
    [17:11:17] └── help

    Gulp API


    All function arguments can take arbitrary arguments.

    gulp.task


    Objective:
       define a task
    Format:
       gulp.task(task_name, function () {...})
       gulp.task(task_name,  gulp.series(task1_name, gulp.parallel(task2_name, task3_name)))
       ...

    gulp.series


    Objective:
       Define which tasks or task and a function have to be executed in serial fashion (one after another). [source]
    Format:
       gulp.series(task1_name, task2_name)
       gulp.series(task1_name, function() {...})
       gulp.series(task1_name, gulp.parallel(task2_name, task3_name))
       ...

    gulp.parallel


    Objective:
       Define which tasks or task and a function have to be executed in parallel. [source]
    Format:
       gulp.parallel(task1_name, task2_name)
       gulp.parallel(task1_name, function() {...})
       ...


    gulp.watch


    Objective:
       Define a function or task(s) to be executed each time any of files that matches the provided pattern change.
    Format:
       gulp.watch(file_matching_pattern, function(){...})
       gulp.watch(file_matching_pattern, gulp.series(task1_name, task2_name))
       gulp.watch(file_matching_pattern, gulp.parallel(task1_name, task2_name))
       ...

    How is Gulp actually used in projects?


    Projects are usually npm-based so contain packages.json. Its "scripts" section contains one or more scripts which are basically executions of Gulp tasks (in form gulp [options] tasks). Gulp tasks are defined in gulpfile.js. Gulp is indirectly executed via npm.

    Further reading:

    Gulp - Getting Started
    Gulp js interview questions for beginners
    Gulp recipes
    How do I update to Gulp 4?
    A quick guide for switching to gulp 4

    Wednesday, 19 September 2018

    Introduction to Babel

    What is Babel?

    • tool for transpiling (compiling) ES6/ES7 code to ECMAScript 5 code, which can be used today in any modern browser [source]
    • has extensions for transpiling JSX for React and Flow syntax for static type checking. [source]
    • babeljs.io: What is Babel?

    What is a Transpiler?

    Transpiler is a type of compiler that takes the source code of a program written in one programming language as its input and produces the equivalent source code in another programming language. (Wikipedia)

    Why do we need Babel?

    • Allows us to write future JavaScript syntax today [source]
    • Current browsers don’t support all the new ECMAScript 6 (aka ECMAScript 2015) features yet (see compatibility table). You need to use a compiler (transpiler) to transform your ECMAScript 6 code to ECMAScript 5 compatible code. Although there are other options, Babel has become the de-facto standard to compile ECMAScript 6 applications to a version of ECMAScript that can run in current browsers. Babel can also compile other versions of ECMAScript as well as React’s JSX. [source]
    • Why Do We Need Babel (Youtube video)

    How does it work?


    It analyzes code and applies any of the transformation rules that are defined in its configuration and installed. 


    Transformers


    A transformer is a module with a specific goal that is run against your code to transform it. For example, the es6.arrowFunctions transformer has the very specific goal of transforming ES6 Arrow Functions to the equivalent ES3. This allows transformers to be completely unaware of the other transformations happening so that you can easily chain them together. [source]

    Transformations come in the form of plugins.[source]

    Presets


    Babel can be configured to transpile specific JavaScript features. You can add much more plugins, but you can’t add to the configuration features one by one, it’s not practical. This is why Babel offers presets. [source] Presets define sets of plugins. They are collections of pre-configured plugins.

    See the full list of available presets here.

    If preset includes a single plugin, its name does not have to match the plugin's name e.g. preset @babel/preset-typescript includes @babel/plugin-transform-typescript plugin.

    env preset


    The env preset is very popular as you tell it which environments you want to support, and it does everything for you, supporting all modern JavaScript features. It includes all plugins to support modern JavaScript (ES2015, ES2016, etc.). [source]

    Configuration


    Babel has to be configured in order to know what it has to do. Configuration file comprises of explicitly listed:
    • presets - predefined sets of plugins
    • plugins - manually added plugins
    If we want to use es2015 preset (which transpiles ES6 code to ES5) and e.g. React preset we'll install both plugins first:

    > npm install babel-preset-es2015 --save-dev
    > npm install babel-preset-react --save-dev

    ...and then list presets in Babel configuration file (.babelrc):

    {
       "presets": [
          "es2015",
          "react"
       ],
       "plugins": []
    }

    Obviously, if we uninstall some preset/package, we have to remove it from .babelrc.

    Babel modules (packages)

    All Babel modules/packages (libraries, plugins and presets) are scoped under @babel name. Just like any other npm package, they can be installed either as a development or production dependencies.

    @babel/cli (babel-cli)
    Babel command line - a tool for transpiling the code through the command line.
    To transpile ES6/ES7 file into ECMAScript 5 file run: 
    babel input.js --out-file compiled.js 
    [@babel/cli]

    @babel/core (babel-core)
    Babel core functionality. It can be used directly in JS code via require.

    @babel/preset-env
    Preset that allows using the latest JavaScript without needing to micromanage which syntax transforms and (optionally) browser polyfills are needed by target environment(s). It replaces many presets that were previously used including: babel-preset-es2015 (transpiles ECMAScript 2015/ES2015/ES6 code into ES5 [source]), babel-preset-es2016, babel-preset-es2017, babel-preset-latest, babel-preset-node5, babel-preset-es2015-node etc...If targets option is not used, @babel/preset-env behaves exactly the same as @babel/preset-es2015, @babel/preset-es2016 and @babel/preset-es2017 together (or the deprecated babel-preset-latest).

    @babel/polyfill
    Emulates a full ES2015+ environment (e.g. allows using Promise type)

    @babel/plugin-transform-typescript
    Plugin which transforms TypeScript into ES.next.

    @babel/plugin-transform-arrow-functions
    Plugin that transforms arrow functions into ES5 compatible function expressions.

    @babel/plugin-transform-runtime
    A plugin that enables the re-use of Babel's injected helper code to save on codesize.
    But it's also required (together with @babel/runtime) when using Promise type or async-await idiom (otherwise an error like Uncaught ReferenceError: regeneratorRuntime is not defined might appear; regenrator is one of options of this plugin).

    @babel/runtime
    Contains Babel's modular runtime helpers and a version of regenerator-runtime.
    Required when using Promise type and/or async-await syntax.

    @babel/plugin-proposal-object-rest-spread
    Allows using:
    rest argument for functions; introduced in ECMA2015 (ES6)
    spread syntax; introduced in ECMA2015 (ES6)

    etc...

    How to install Babel?


    Create package.json:

    > npm init

    Install Babel locally (read here why):

    > npm install --save-dev @babel/core @babel/cli

    We also added them to dev dependencies as they are used during app development only (they are not needed in the runtime). [Do you put Babel and Webpack in devDependencies or Dependencies?]

    If you try to install @babel/cli without @babel/core, npm will tell you that cli requires npm as a peer dependency. CLI acts as a plugin for Core library which makes sense.

    >npm install --save-dev @babel/cli
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN @babel/cli@7.1.0 requires a peer of @babel/core@^7.0.0-0 but none is installed. You must install peer dependencies yourself.
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    + @babel/cli@7.1.0
    added 153 packages from 108 contributors and audited 2145 packages in 10.359s
    found 0 vulnerabilities

    Furthermore, if we try to run babel CL executable, it will throw an error complaining it can't find the core:

    C:\dev\github\babel-demo\node_modules\.bin>babel
    internal/modules/cjs/loader.js:583
        throw err;
        ^

    Error: Cannot find module '@babel/core'
        at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
        at Function.Module._load (internal/modules/cjs/loader.js:507:25)
        at Module.require (internal/modules/cjs/loader.js:637:17)
        at require (internal/modules/cjs/helpers.js:20:18)
        at _core (C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\options.js:29:16)
        at Object.<anonymous> (C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\options.js:132:76)
        at Module._compile (internal/modules/cjs/loader.js:689:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
        at Module.load (internal/modules/cjs/loader.js:599:32)
        at tryModuleLoad (internal/modules/cjs/loader.js:538:12)

    So let's install the core:

    C:\dev\github\babel-demo>npm install --save-dev @babel/core
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    + @babel/core@7.1.0
    added 33 packages from 16 contributors and audited 2344 packages in 7.878s
    found 0 vulnerabilities

    package.json now contains:

      "devDependencies": {
        "@babel/cli": "^7.1.0",
        "@babel/core": "^7.1.0"
      }

    Babel CLI executable does not complain anymore:

    C:\dev\github\babel-demo\node_modules\.bin>babel
    babel:
      stdin compilation requires either -f/--filename [filename] or --no-babelrc


    How to use Babel 



    Let's now add a JS file which uses some of the recent JS features (e.g. arrow-style function):

    C:\dev\github\babel-demo\src\scripts\index.js:

    var log = x => console.log(x);
    log('Hello, world!');

    Now let's see what would Babel do with original JS code at this stage:

    C:\dev\github\babel-demo\node_modules\.bin>babel ../../src/scripts --out-dir lib
    Successfully compiled 1 file with Babel.

    We can see that it didn't change anything apart from inserting an empty line between code lines:

    C:\dev\github\babel-demo\node_modules\.bin>type lib\index.js
    var log = x => console.log(x);

    log('Hello, world!');

    In the previous example we instructed Babel to compile the entire src directory and output it to the lib directory. Instead of that, as we're observing a single file, we could have specify a single input file and (single) output file:

    C:\dev\github\babel-demo\node_modules\.bin>babel ../../src/scripts/index.js --out-file lib/index-transpiled.js

    C:\dev\github\babel-demo\node_modules\.bin>type lib\index-transpiled.js
    var log = x => console.log(x);

    log('Hello, world!');


    npx


    Instead of going to node_modules\.bin in order to run babel executable (as it's installed locally) we can use a handy npm tool called npx:

    C:\dev\github\babel-demo>npx babel
    babel:
      stdin compilation requires either -f/--filename [filename] or --no-babelrc

    Babel does not create directories in the output path:

    C:\dev\github\babel-demo>npx babel src/scripts/index.js --out-file build/index-transpiled.js
    { Error: ENOENT: no such file or directory, open 'build/index-transpiled.js'
        at Object.openSync (fs.js:443:3)
        at Object.writeFileSync (fs.js:1163:35)
        at output (C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\file.js:148:23)
        at C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\file.js:234:9
        at Generator.next (<anonymous>)
        at asyncGeneratorStep (C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\file.js:74:103)
        at _next (C:\dev\github\babel-demo\node_modules\@babel\cli\lib\babel\file.js:76:194)
      errno: -4058,
      syscall: 'open',
      code: 'ENOENT',
      path: 'build/index-transpiled.js' }

    ...so we have to do it ourselves:

    C:\dev\github\babel-demo>mkdir build

    After this Babel runs successfully:

    C:\dev\github\babel-demo>npx babel src/scripts/index.js --out-file build/index-transpiled.js

    ...and we can see the output file:

    C:\dev\github\babel-demo>type build\index-transpiled.js
    var log = x => console.log(x);

    log('Hello, world!');

    Output file being (almost) the same as the input file is not a surprise as we didn't specify any transformer (rules for transpilation). As in our JS file we used arrow functions, let's see how can we instruct Babel to transpile them into ES5 code. We have to install a plugin which contains rules for this transformation. We mentioned it earlier, it's @babel/plugin-transform-arrow-functions.

    We'll install it locally:

    C:\dev\github\babel-demo>npm install --save-dev @babel/plugin-transform-arrow-functions
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    + @babel/plugin-transform-arrow-functions@7.0.0
    added 2 packages from 1 contributor and audited 2346 packages in 6.998s
    found 0 vulnerabilities

    We can now instruct Babel to use this plugin via --plugins argument:

    C:\dev\github\babel-demo>npx babel src/scripts/index.js --out-file build/index-transpiled.js --plugins=@babel/plugin-transform-arrow-functions

    Output file now contains ES5-compatible code:

    C:\dev\github\babel-demo>type build\index-transpiled.js
    var log = function (x) {
       return console.log(x);
    };

    log('Hello, world!');

    We can instruct Babel to automatically run each time we change input files by adding option --watch to its command line arguments.

    To instruct Babel to transform all ES2015+ features in our code we would need to install a long list of plugins. Instead of this, we can install env preset which contains them all:

    C:\dev\github\babel-demo>npm install --save-dev @babel/preset-env
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

    + @babel/preset-env@7.1.0
    added 70 packages from 13 contributors and audited 3492 packages in 19.331s
    found 0 vulnerabilities

    To instruct Babel to use a preset we'll use --presets option in command line arguments:

    C:\dev\github\babel-demo>npx babel src/scripts/index.js --out-file build/index-transpiled.js --presets=@babel/env

    We can see that output now has an extra line ("use strict";):

    C:\dev\github\babel-demo>type build\index-transpiled.js
    "use strict";

    var log = function log(x) {
      return console.log(x);
    };

    log('Hello, world!');

    Source Code


    Source code used in this article can be found on my GitHub account: babel-demo.

    References &  Future Read:

    Everything you need to know about BabelJS

    Tuesday, 18 September 2018

    Introduction to Rollup.js

    rollup.js is a module bundler which compiles JavaScript  modules into a library or application.


    In this article I want to go through Rollup Tutorial and:

    • install Rolllup.js
    • create an application which consists of modules 
    • demonstrate how Rollup bundles multiple files into a single one


    Rollup Installation 



    Instead of installing Rollup.js globally as in the tutorial, I'll install it locally as I want to make it portable - all clones of this project will use the same Rollup version.

    Let's see how package.json looks after executing npm init:

    C:\wherever\rollup-js-demo>type package.json
    {
      "name": "rollup-js-demo",
      "version": "1.0.0",
      "description": "Application which demoes Rollup.JS basic features.",
      "main": "main.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "repository": {
        "type": "git",
        "url": "git+https://github.com/BojanKomazec/rollup-js-demo.git"
      },
      "keywords": [
        "rollup.js",
        "demo"
      ],
      "author": "Bojan Komazec",
      "license": "ISC",
      "bugs": {
        "url": "https://github.com/BojanKomazec/rollup-js-demo/issues"
      },
      "homepage": "https://github.com/BojanKomazec/rollup-js-demo#readme"
    }

    To install Rollout.js locally (per project) we'll omit -g in npm install. By default npm install rollout would add Rollout to dependencies but as it is a dependency used in development we'll use --save-dev option:

    C:\wherever\rollup-js-demo>npm install --save-dev rollup
    + rollup@0.66.0
    added 3 packages from 33 contributors and audited 3 packages in 0.903s
    found 0 vulnerabilities

    If we now check package.json, Rollout will be listed in devDependencies:

    C:\wherever\rollup-js-demo>type package.json
    {
      "name": "rollup-js-demo",
      "version": "1.0.0",
      "description": "Application which demoes Rollup.JS basic features.",
      "main": "main.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "repository": {
        "type": "git",
        "url": "git+https://github.com/BojanKomazec/rollup-js-demo.git"
      },
      "keywords": [
        "rollup.js",
        "demo"
      ],
      "author": "Bojan Komazec",
      "license": "ISC",
      "bugs": {
        "url": "https://github.com/BojanKomazec/rollup-js-demo/issues"
      },
      "homepage": "https://github.com/BojanKomazec/rollup-js-demo#readme",
      "devDependencies": {
        "rollup": "^0.66.0"
      }
    }

    As this package is installed locally we won't be able to access its executable from anywhere - we have to go to its directory. We can execute it and for example check Rollout.js version first:

    C:\wherever\rollup-js-demo\node_modules\.bin>rollup --version
    rollup v0.66.0

    Or, from the root directory:

    C:\wherever\rollup-js-demo>npx rollup --version
    rollup v0.66.0

    Let's check command line arguments of Rollup CLI:

    C:\wherever\rollup-js-demo>npx rollup

    rollup version 0.66.0
    =====================================

    Usage: rollup [options] <entry file>

    Basic options:

    -v, --version               Show version number
    -h, --help                  Show this help message
    -c, --config                Use this config file (if argument is used but value
                                  is unspecified, defaults to rollup.config.js)
    -w, --watch                 Watch files in bundle and rebuild on changes
    -i, --input                 Input (alternative to <entry file>)
    -o, --file <output>         Output (if absent, prints to stdout)
    -f, --format [es]           Type of output (amd, cjs, es, iife, umd)
    -e, --external              Comma-separate list of module IDs to exclude
    -g, --globals               Comma-separate list of `module ID:Global` pairs
                                  Any module IDs defined here are added to external
    -n, --name                  Name for UMD export
    -m, --sourcemap             Generate sourcemap (`-m inline` for inline map)
    --amd.id                    ID for AMD module (default is anonymous)
    --amd.define                Function to use in place of `define`
    --no-strict                 Don't emit a `"use strict";` in the generated modules.
    --no-indent                 Don't indent result
    --environment <values>      Settings passed to config file (see example)
    --no-conflict               Generate a noConflict method for UMD globals
    --no-treeshake              Disable tree-shaking
    --silent                    Don't print warnings
    --intro                     Content to insert at top of bundle (inside wrapper)
    --outro                     Content to insert at end of bundle (inside wrapper)
    --banner                    Content to insert at top of bundle (outside wrapper)
    --footer                    Content to insert at end of bundle (outside wrapper)
    --no-interop                Do not include interop block

    Examples:

    # use settings in config file
    rollup -c

    # in config file, process.env.INCLUDE_DEPS === 'true'
    # and process.env.BUILD === 'production'
    rollup -c --environment INCLUDE_DEPS,BUILD:production

    # create CommonJS bundle.js from src/main.js
    rollup --format=cjs --file=bundle.js -- src/main.js

    # create self-executing IIFE using `window.jQuery`
    # and `window._` as external globals
    rollup -f iife --globals jquery:jQuery,lodash:_ \
      -i src/app.js -o build/app.js -m build/app.js.map

    Notes:

    * When piping to stdout, only inline sourcemaps are permitted

    For more information visit https://rollupjs.org



    Writing an Application in a Modular Way



    Now when we have Rollup installed, we can create our project which consist of an entry point script which imports a module:

    C:\wherever\rollup-js-demo\src\scripts\modules\foo.js:

    export default 'hello world!';

    C:\wherever\rollup-js-demo\src\scripts\main.js:

    // application entry point
    import foo from "./modules/foo.js";
    export default function() {
        console.log(foo);
    }


    Bundling Modules With Rollup



    Let's create a bundle and push it onto stdout:

    C:\wherever\rollup-js-demo\node_modules\.bin>rollup ../../src/scripts/main.js -f cjs

    ../../src/scripts/main.js → stdout...
    'use strict';

    // a module

    // export a primitive (string in this case)
    var foo = 'hello world!';

    // application entry point
    function main() {
        console.log(foo);
    }

    module.exports = main;
    created stdout in 39ms

    Rollup took couple of parameters:
    • path to the JS script which is application's entry point
    • -f cjs - bundle format specification; in this case cjs - CommonJS runnable by Node.js


    Running the Bundle in Node Console



    To run this bundle with Node.js we have to save it to the file:

    C:\wherever\rollup-js-demo\node_modules\.bin>rollup ../../src/scripts/main.js -f cjs -o ../../build/js/bundle.js

    ../../src/scripts/main.js → ../../build/js/bundle.js...
    created ../../build/js/bundle.js in 40ms

    Let's run the bundle now with Node.js:

    C:\wherever\rollup-js-demo\node_modules\.bin>node
    > let bundle = require('../../build/js/bundle.js');
    undefined
    > bundle()
    hello world!
    undefined

    For each function Node outputs its return value. If function has only side effects (like printing something on the stdout) Node will output undefined as its return value. [source]


    Using Configuration File



    Instead of passing a (potentially long) list of parameters and options to rollup executable, we can place them into a configuration file.

    C:\wherever\rollup-js-demo\rollup.config.js:

    export default {
        input: '../../src/scripts/main.js',
        output: {
            file: '../../build/js/bundle.js',
            format: 'cjs'
        }
    }

    input: entry point - a file that imports other modules. Only single file is allowed by default. rollup-plugin-multi-entry can be used to use multiple entry points in the rollup bundle.
    output: a bundle file; defaults to stdout if not specified
    format: specifies what kind of bundle is to be created:
    • cjsCommonJS (runs in Node.js)
    • iife - Immediately-Invoked Function Expression (runs in the browser)
    sourceMap: adds a sourcemap inside the generated file (helpful for debugging)



      Paths in configuration file have to be relative to the path of the Rollup executable if we call it directly.

      Rollup now only needs to be passed the path to configuration file:

      C:\wherever\rollup-js-demo\node_modules\.bin>rollup -c ../../rollup.config.js

      ../../src/scripts/main.js → ../../build/js/bundle.js...
      created ../../build/js/bundle.js in 18ms

      Rollup creates all directories in specified path to the output file. In this case that is: build/js.

      If we use npx rollup paths in the config file can be relative to the project root directory (where npx is usually called from):

      C:\wherever\rollup-js-demo\rollup.config.js:

      export default {
          input: 'src/scripts/main.js',
          output: {
              file: 'build/js/bundle.js',
              format: 'cjs'
          }
      }

      We can now call it like this:

      C:\wherever\rollup-js-demo>npx rollup -c

      If we try to call rollup -c when rollup.config.js does not exist, we'll get an error:

      C:\wherever\rollup-js-demo>npx rollup -c
      ENOENT: no such file or directory, lstat 'C:\wherever\rollup-js-demo\rollup.config.js'

      In the real life web apps are usually created as Node projects so we'd have package.json with the script which runs rollup e.g.:

        "scripts": {
          ...
          "build": "rollup -c --environment BUILD:production"
        }

      In this case we'd run npm run build and npm knows how to call locally installed dependencies (it's probably using npx under the bonnet).

      Plugins configuration


      If we install some of the Rollup's plugins, their configuration can be embedded into rollup.config.js. E.g. if we install rollup-plugin-node-resolve, it and its configuration will be listed under plugins section:

      import resolve from 'rollup-plugin-node-resolve';

      export default {
        input: 'main.js',
        output: {
          file: 'bundle.js',
          format: 'iife'
        },
        name: 'MyModule',
        plugins: [
          resolve({
             ...
             extensions: ['.js', '.json'],
             browser: true,
             ...
          })
        ]
      };

      Creating a Bundle Loadable Into Web Page



      Let's now create a bundle script that can be loaded into an HTML file. Let's say that now instead of printing foo value into console we want to print it in HTML page. Let's define first HTML page.

      C:\wherever\rollup-js-demo\test\index.html:

      <!DOCTYPE html>
        <head>
          <meta charset="utf-8">
          <title>Rollup.js Demo</title>
        </head>
        <body>
          <p id="log"></p>
          <script src="../build/js/bundle-browser.js" async defer></script>
        </body>
      </html>

      bundle-browser.js is a bundle output that will be created for this test.

      Let's create another entry point JS file which is adjusted to the web page it will be loaded into.

      C:\wherever\rollup-js-demo\src\scripts\main-browser.js:

      // application entry point
      import foo from "./modules/foo.js";

      const log = document.getElementById('log');
      log.innerText = 'foo value (loaded from a module) is: \n';
      log.innerText += foo;

      As we now have different input and output files, we can create another Rollup config file.

      C:\wherever\rollup-js-demo\rollup.config.browser.js:

      export default {
          input: '../../src/scripts/main-browser.js',
          output: {
              file: '../../build/js/bundle-browser.js',
              format: 'iife',
              name: 'main'
          }
      }

      The content of the JS file aimed to be loaded into web page should be wrapped in the auto-executed function - therefore we have to use iife output format.

      If output.name is not specified Rollup gives the following error:
      Error: You must supply output.name for IIFE bundles

      Let's now create a bundle:

      C:\wherever\rollup-js-demo\node_modules\.bin>rollup -c ../../rollup.config.browser.js

      ../../src/scripts/main-browser.js → ../../build/js/bundle-browser.js...
      created ../../build/js/bundle-browser.js in 25ms

      C:\wherever\rollup-js-demo\build\js\bundle-browser.js:

      (function () {
      'use strict';

      // a module

      // export a primitive (string in this case)
      var foo = 'hello world!';

      // application entry point

      const log = document.getElementById('log');
      log.innerText = `Demo app: \n\n`;
      log.innerText += foo;

      }());

      If we open index.html in browser, we can see:


      Check out the source code for this demo (on my GitHub).

      What about TypeScript source files?


      We can have TypeScript file specified as an entry point. To learn how to achieve that read in this article on this blog.


      Further reading:

      Rollup.js Tutorial, Part 1: How to Set Up Smaller, More Efficient JavaScript Bundling Using Rollup

      Article ToDo:


      • introduce using npx (so can be called as npx rollup) or at least adding rollup : rollup within scripts section of package.json so it can be run as npm rollup - all this is to avoid calling a locally installed package from .bin directory and passing relative paths which is very messy...