# Part II: Scaffolding a Modern JavaScript Library

# Initialize a project with Vite

Create a new project. [?] (opens new window)

npm create vite@latest
✔ Project name: … collection-js
✔ Select a framework: › vanilla
✔ Select a variant: › vanilla-ts

Install dependencies.

cd collection-js
npm install

Open VS Code editor. [?] (opens new window)

code .

# Initialize a Git repository

Write an initial commit message.

git init
git add .
git commit -m "Initial commit"

Create a repository from GitHub (opens new window).

Push commits to remote.

git remote add origin git@github.com:<username>/collection-js.git
git push -u origin main

# Tidy up the project

Remove sample files.

rm src/*

Write a new commit message.

git add .
git commit -m "Remove sample files"

# Build with library mode

Create a vite.config.js file. [?] (opens new window)

import { resolve } from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      name: 'CollectionJS',
      fileName: 'collection-js',
    },
    rollupOptions: {
      external: [],
      output: {
        globals: {
        },
      },
    },
  },
});

Install dependencies.

npm i @types/node --save-dev

Create a index.ts file in src folder as an entry point.

const hello = () => {
  console.log('Hello');
};

export {
  hello,
};

Update package.json file.

{
  // ...
  "type": "module",
  "files": [
    "dist"
  ],
  "main": "./dist/collection-js.umd.cjs",
  "module": "./dist/collection-js.js",
  "exports": {
    ".": {
      "import": "./dist/collection-js.js",
      "require": "./dist/collection-js.umd.cjs"
    }
  },
}

Run build command.

npm run build

# Try with UMD module

Update index.html file.

<!DOCTYPE html>
<html lang="en">
  <!-- ... -->
  <body>
    <div id="app"></div>
    <script src="/dist/collection-js.umd.cjs"></script>
    <script>
      window.CollectionJS.hello();
    </script>
  </body>
</html>

Start a server.

npm run dev

# Try with ES module

Update index.html file.

<!DOCTYPE html>
<html lang="en">
  <!-- ... -->
  <body>
    <div id="app"></div>
    <script type="module" src="/dist/collection-js"></script>
    <script type="module">
      import { hello } from '/dist/collection-js';
      hello();
    </script>
  </body>
</html>

Start a server.

npm run dev

# Tidy up

Fix index.html file.

<!DOCTYPE html>
<html lang="en">
  <!-- ... -->
  <body>
    <div id="app"></div>
    <script type="module" src="/dist/collection-js"></script>
  </body>
</html>

Write a new commit message.

git add .
git commit -m "Add build config"

# Initialize ESLint and EditorConfig

Create a .editorconfig file. [?] (opens new window)

root = true

[*]
indent_size = 2

Initialize ESLint and install dependencies. [?] (opens new window)

npm init @eslint/config
✔ How would you like to use ESLint? · syntax
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser, node
✔ What format do you want your config file to be in? · JavaScript
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · npm

Install Airbnb ESLint config and install dependencies. [?] (opens new window)

npm install eslint-config-airbnb-typescript \
    eslint-plugin-import \
    --save-dev

Update .eslintrc.cjs file. [?] (opens new window)

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  overrides: [
    {
      files: [
        'src/**/*.ts',
        'src/**/*.tsx',
      ],
      extends: [
        'airbnb-typescript/base',
        'plugin:import/recommended',
      ],
      parserOptions: {
        project: [
          './tsconfig.json',
        ],
      },
    },
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: [
    '@typescript-eslint'
  ],
  rules: {
  },
};

Update package.json file, add a lint command to scripts field.

{
  "scripts": {
    // ...
    "lint": "eslint src"
  }
}

Run lint command.

npm run lint

Write a new commit message.

git add .
git commit -m "Add eslint config"

# Implement a "map" function

Create a modules folder in src folder.

mkdir src/modules

Create a map.ts file in modules folder, and implement a map function.

const map = (items: Array<any>, callable: Function) => {
  const res = [];
  for (let i = 0; i < items.length; i++) {
    res[i] = callable(items[i]);
  }
  return res;
};

export default map;

Create an index.ts file in modules folder, then import and export the module.

import map from './map';

export {
  map,
};

# Unit test with Vitest

Install dependencies. [? (opens new window)]

npm i vitest @vitest/coverage-c8 --save-dev

Update package.json file, add test and coverage commands to scripts field.

{
  "scripts": {
    // ...
    "test": "vitest",
    "coverage": "vitest run --coverage"
  }
}

Create a map.test.ts file in modules folder, and create a test case for the map function.

import { test, expect } from 'vitest';
import { map } from './index';

test('map', () => {
  const actual = map([1, 2, 3, 4, 5], (v: number) => v * 2);
  const expected = [2, 4, 6, 8, 10];

  expect(actual).toStrictEqual(expected);
});

Run test command.

npm run test

> collection-ts@0.0.0 test
> vitest


 DEV  v0.22.1 /Users/memochou/Projects/collection-js

 ✓ src/modules/map.test.ts (1)
 ✓ src/index.test.ts (11)

Test Files  2 passed (2)
     Tests  12 passed (12)
  Start at  23:01:17
  Duration  950ms (transform 541ms, setup 0ms, collect 140ms, tests 18ms)

Run coverage command.

npm run coverage

> collection-ts@0.0.0 coverage
> vitest run --coverage


 RUN  v0.22.1 /Users/memochou/Projects/collection-js
      Coverage enabled with c8

 ✓ src/modules/map.test.ts (1)

Test Files  1 passed (1)
     Tests  1 passed (1)
  Start at  23:03:08
  Duration  1.35s (transform 447ms, setup 0ms, collect 50ms, tests 3ms)

 % Coverage report from c8
----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.ts |     100 |      100 |     100 |     100 |                   
 map.ts   |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------

Update .gitignore file.

# ...
coverage

Write a new commit message.

git add .
git commit -m "Implement map function"