dev environment

Documenting how I do things

node

Use nvm to install the latest version of node.

I use npm as a package manager (vs yarn). I started using npm when I started with node, and I've never found a reason to switch package managers.

npm is also what I use to organize/orchestrate different scripts. I remember reading this article about task automation back when it still existed in blog format. This is an example of scripts in package.json:

  "scripts": {
    "start": "npm run build && concurrently --kill-others \"npm run watch-sass\" \"npm run watch-js\" \"netlify dev --port=8888\"",
    "build": "npm run copy && npm run build-js && npm run build-sass",
    "cypress-test": "NODE_ENV=test env $(cat .env | xargs) concurrently --kill-others \"npm start\" \"cypress open\"",
    "test": "NODE_ENV=test tape test/*.js | tap-spec"
  },

Note the script cypress-test. Run it with $ npm run cypress-test. This uses another tool, concurrently, to run multiple other scripts at the same time. It starts our localhost server with npm start, and opens the cypress GUI with cypress open.

tools

For unit tests use tape. For testing in a browser enironment tape-run. Here is an example of a test script in package.json. It will start an electron process (for a browser enironment), but log standard tap output to stdout in the terminal, and return a pass/fail exit code.

For end-to-end testing I like cypress. It will start a GUI application for visually running tests, which is good because this is an e2e test -- part of it is that you want to test the GUI.

You will probably have a script like this in package.json:

"cypress-test": "concurrently --kill-others \"npm start\" \"cypress open\""

fork something on github.

There is more to this than you might think.

Now clone the repo

You want to clone the forked version, not the original. hub by default will clone from your github username.

$ hub clone repo-name .

dev how to

Notes on a javascript development process

It’s crucial to have a setup, so that, at any given moment, when you get an idea, you have the place and the tools to make it happen.

– David Lynch, Catching the Big Fish

npm

You can use npm to privately host our internal dependencies. This allows you to use the same workflow with both private and public libraries.

publish a private package

  1. Use your org name as the scope in the name field:
    {
     "name": "@org/project-name"
    }
    

If you use npm init to initialize your packages, you can pass in the scope like this:

npm init --scope=<your_scope>
  1. Publish to npm. By default, scoped packages are published as private.
    npm publish
    

If you want to make the package public, publish it with --access=public

npm publish --access=public

read more

use private npm modules with a CI server

An automated build service will need a token to authenticate with npm. Create a .npmrc file that exposes the token as a variable. An overview:

See https://docs.npmjs.com/private-modules/ci-server-config


publish a compiled library

We want to ignore the compiled code in git, but publish the built code to npm so that users don't have to build it themselves. Add the build script as a prepare hook in package.json. This will run the build step before a new version is published to npm.

.gitignore:

dist

package.json:

{
    "main": "dist/index.js",
    "scripts": {
        "prepare": "npm run build > dist/index.js"
    }
}

If the build depends on the host environment, the correct place to build the code is in the postinstall hook, because it will run on the consumer's machine.

Read more https://docs.npmjs.com/misc/scripts


semantic versions

Version numbers are meaningful to npm. By default npm will install the latest non-breaking change, which means it's important to increment the major version number on any change that breaks the API.

A caveat is version numbers starting with 0, which are considered to be experimental. Any version number change to an experimental package is potentially a breaking change. When you install a package with an experimental version number, npm will pin it to an exact number, instead of using the default ^ operator.

read more


environment variables

dotenv-safe

Create a .env.example file that is commited with the code, and also a .env file that is ignored.


env-cmd Use both CLI env vars, and also a .env file:

{
    "build": "env-cmd --no-override .env browserify -t envify src/index.js > dist/bundle.js"
}

cypress

A quick start with cypress

install

$ npm i -D cypress

This will add a folder cypress and a file cypress.json to the project.

add a script to package.json

This is how you open the gui test runner, cypress open.

{
    "cypress": "cypress open"
}

configure eslint

Add a .eslintrc into the cypress folder. See https://github.com/cypress-io/eslint-plugin-cypress

write tests

Tests go in cypress/integration. They look like this

describe('My First Test', function() {
    it('clicking "type" navigates to a new url', function() {
        cy.visit('https://example.cypress.io')
        cy.contains('type').click()
        cy.url().should('include', '/commands/actions')
    })
})

See https://docs.cypress.io/guides/getting-started/writing-your-first-test.html

CI

We want to be able to automate these tests, not just run them in a gui. We need a script that will start our dev server, then run the tests, then close both.

Create a wrapper that runs budo from CLI, then runs the tests and stops the server when finished:

// cypress/scripts/ci.js
var cypress = require('cypress')
var Budo = require('budo')

var budo = Budo.cli(process.argv.slice(2), {
    port: process.env.PORT || 8000
}).on('connect', function () {
    cypress.run()
        .then(res => {
            budo.close()
        })
})

This should be called as an npm script like the others, but using our file instead of budo:

{
    "cypress-ci": "node ./cypress/scripts/ci.js src/index.js:bundle.js --pushstate --dir=public -- -t babelify -g aliasify -t [ envify --NODE_ENV development ] -dv"
}