Modern JavaScript Apps with Neutrino

By  on  

Mozilla Neutrino

Utilize tooling that harnesses the power of Webpack with ease of simple presets to quickly start JavaScript projects, all without upfront configuration.

Taking the plunge into starting a new JS project often brings along a significant effort into preparing your environment prior to starting development on the project. Many developers have a preference for using cutting-edge features and modern tooling workflows to make the process enjoyable. Unfortunately, this approach can often have quite a bit of learning curve as people spend time researching best practices, following complex configuration tutorials, and experimenting with boilerplates. What if we could take the work that the community has put into configuring their own projects, and capture that into shareable packages that anyone can use? This is why we created Neutrino.

Neutrino is a tool that combines the build and configuration power of Webpack and bolts on the capability to build JavaScript-based projects with presets. A preset is the fundamental building block of Neutrino. With it you can supplement a project with a number of features including how it is compiled, ensuring code quality, and even quickly add testing. By breaking up configuration into composable units we can foster an ecosystem where customizing a project becomes plug and play.

To get an idea of how easy it is to get started with Neutrino, I'll walk through creating a couple simple projects.

Note: In the upcoming examples I am using the Yarn package manager for installing dependencies and creating scripts. This is only my personal preference, and you are free to use the npm client if you desire.

Node.js Quickstart

Wait, you can use Neutrino to build Node.js apps?

To get started with our first Neutrino-based Node.js project, we are going to be using neutrino-preset-node. According to its documentation it enables:

  • No upfront configuration, and easy overrides if necessary
  • Compiles to support Node.js v6.9+, ES Modules, Async Functions
  • Auto-wired sourcemaps

Cool, let's get started!

First up, we need a directory to start working from. From your terminal, create a new directory and change into it. Then we are going to install neutrino and neutrino-preset-node as development dependencies.

❯ mkdir project && cd project
❯ yarn add --dev neutrino neutrino-preset-node

By default the Node.js preset looks for source code in a src directory, with the main entry point being index.js. Let's create this file and edit it, just with a simple HTTP server that echoes whatever we send to it.

import { createServer } from 'http';

const port = process.env.PORT || 3000;

createServer(({ url }, response) => {
  console.log(`Received message at ${url}`);
  response.end(url.slice(1));
})
.listen(port, () => console.log(`Running on :${port}`));

Next, let's add a couple scripts to our package.json which will give us some easy commands to start and build our app:

{
  "scripts": {
    "start": "neutrino start --presets neutrino-preset-node",
    "build": "neutrino build --presets neutrino-preset-node",
    "serve": "yarn start && node build"

  },
  "devDependencies": {
    "neutrino": "^4.0.1",
    "neutrino-preset-node": "^4.0.1"
  }
}

We are ready to start our app! Using yarn serve in one terminal, and curl in another, we can see our code in action:

❯ yarn serve
Warning: This preset does not support watch compilation. Falling back to a one-time build.
Hash: 8fa3faf9cbe8ca235884
Version: webpack 2.2.1
Time: 632ms
       Asset     Size  Chunks             Chunk Names
    index.js   3.6 kB       0  [emitted]  index
index.js.map  3.53 kB       0  [emitted]  index

Running on :3000

---

❯ curl http://localhost:3000/Hello\!
Hello!
That's it?!

Yep. That's it.

No upfront cost needed to start your project with a completely modern toolchain.

React Quickstart

Okay, I'll admit that was simple enough. But certainly a complex environment like React needs more than this, right?

For fun, let's just change this project from Node.js to React. According to the Neutrino documentation, the React preset features:

  • JSX syntax, ES Modules, support for last 2 browser versions, and Async Functions
  • Support for import CSS, HTML, images, fonts, and icons directly from JavaScript
  • Hot module replacement, no HTML templating, and much more

Let's swap presets and install some React dependencies.

❯ yarn remove neutrino-preset-node && yarn add --dev neutrino-preset-react
❯ yarn add react react-dom

Our package.json commands need to be changed to use the React preset now:

{
  "scripts": {
    "start": "neutrino start --presets neutrino-preset-react",
    "build": "neutrino build --presets neutrino-preset-react"
  },
}

Instead of creating a Node.js server, let's render some content to a web app. By convention our preset allows us to mount our application at ID “root”:

import React from 'react';
import { render } from 'react-dom';

render((
  <main>
    <h1>Hello! 😎</h1>
  </main>
), document.getElementById('root'));

Back to the terminal, and we can start up our app, and load it up in the browser:

❯ yarn start
✔ Development server running on: http://localhost:5000
✔ Build completed

Hopefully that demonstrates how simple it is to get up and running with a new React project! If you aren't working with React for your web project, consider using neutrino-preset-web for other libraries or generic web applications.

Multiple presets

Neutrino makes it simple to compose multiple presets together. To demonstrate, let's add a linting preset which will conform our project to the Airbnb style guide. Install neutrino-preset-airbnb-base:

❯ yarn add --dev neutrino-preset-airbnb-base

To reduce some repetition, we are going to take advantage of a Neutrino feature which will pull from an array of presets in our package.json. This saves us from having to name all the presets we want to use for every command. Remove the presets from the script commands and move them to config.presets.

{
  "config": {
    "presets": [
      "neutrino-preset-airbnb-base",
      "neutrino-preset-react"
    ]
  },
  "scripts": {
    "start": "neutrino start",
    "build": "neutrino build"
  }
}

Note: neutrino-preset-airbnb-base needs to be loaded before our React preset according to the documentation.

If we modify our code and introduce something in violation of the preset, we are clearly notified in the console:

❯ yarn start
✔ Development server running on: http://localhost:5000
✔ Build completed

ERROR in ./src/index.js

/node-project/src/index.js
  6:10  error  Strings must use singlequote  quotes

✖ 1 problem (1 error, 0 warnings)
I'm starting to sense a pattern here…

Testing, too!

Let's quickly add a simple Jest test, because why not? The Neutrino preset neutrino-preset-jest uses a convention of a test directory, with some expectations on file extensions:

❯ yarn add --dev neutrino-preset-jest
❯ mkdir test && touch test/add.test.js
❯ touch src/add.js

Let's write a quick test which verifies a function correctly performs simple addition, which we will shortly create:

import add from '../src/add';

describe('addition', () => {
  it('adds 2 numbers', () => {
    expect(add(3, 5)).toBe(8);
  });
});

Now our addition module in src/add.js:

export default (x, y) => x + y;

Edit the package.json once more, adding the Jest preset to our list, along with a command to run tests:

{
  "config": {
    "presets": [
      "neutrino-preset-airbnb-base",
      "neutrino-preset-react",
      "neutrino-preset-jest"
    ]
  },
  "scripts": {
    "start": "neutrino start",
    "build": "neutrino build",
    "test": "neutrino test"
  }
}

Let's run the test!

❯ yarn test

 PASS  test/add.test.js
  addition
    ✓ adds 2 numbers (3ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.228s
Ran all test suites.

If we had made a mistake writing our addition module and accidentally used multiplication:

export default (x, y) => x * y;

This would have caused the test to fail:

❯ yarn test

 FAIL  test/add.test.js
  ● addition › adds 2 numbers

expect(received).toBe(expected)

Expected value to be (using ===):
      8
    Received:
      15

at Object.<anonymous> (test/add.test.js:5:38)
      at process._tickCallback (internal/process/next_tick.js:103:7)

addition
    ✕ adds 2 numbers (5ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.221s
Ran all test suites.

Modifying the build

One of the great features of Neutrino is that you don't have to trade simplicity for lock-in or lack of extensibility. By following the documentation you can supplement your project build process with additional features that don't come with your preset. If you find you use these features or changes across multiple projects, you can roll that into your own preset, and share it with your team and the community!

Conclusion

Making Neutrino into the tool it is today has been a lot of hard work, but we hope you enjoy it. Try it in your projects, give feedback, create your own presets, and share with others. We want to see everyone be successful with Neutrino.

If you would like to get involved with development or documentation, please visit the contributing section of the docs for complete details, or visit our GitHub repo.

Neutrino Documentation: https://neutrino.js.org/

Neutrino GitHub: https://github.com/mozilla-neutrino/neutrino-dev

Thanks!
Eli Perelman & Hassan Ali — Mozilla

Eli Perelman

About Eli Perelman

Eli Perelman is a JavaScript and Node.js Obsessionalist™, currently working on web tooling for Mozilla's Release & Productivity team. In his spare time he enjoys working on open source and experimenting with electronic music production. Child of the '90s web. Skeptic.

Recent Features

  • By
    Vibration API

    Many of the new APIs provided to us by browser vendors are more targeted toward the mobile user than the desktop user.  One of those simple APIs the Vibration API.  The Vibration API allows developers to direct the device, using JavaScript, to vibrate in...

  • By
    Page Visibility API

    One event that's always been lacking within the document is a signal for when the user is looking at a given tab, or another tab. When does the user switch off our site to look at something else? When do they come back?

Incredible Demos

Discussion

  1. Node, we get access to sharing the language between the back end and the front end and as well,sharing the code between the back end and the front end.Another difference and one that I like is that JavaScript is a dynamic language,meaning the type is determined by value,not when the variable is declared.thanks for sharing great article

  2. I really like Neutrino. Coming from Webpack, it’s a nice concept that can be a real time saver.

    That said, it’s growing fast and undergoing many changes.. can’t wait to see the final form!

  3. gingerchris

    Just FYI – it seems in the latest versions of Neutrino the start and build scripts should use the argument –use rather than –preset

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!