Installation

Requirements

You need to have:

  • Node.js LTS (with npm)
  • Angular CLI

Initialize

The initialization of this library is inspired by The Best Way To Architect Your Angular Libraries from Thomas Trajan

Init library

Init Angular

ng new ng-base --createApplication false --prefix lt
ng g library ng-base --prefix lt

Change package name in projects/ng-base/src

"name": "@lenne.tech/ng-base"

Replace path configuration in tsconfig.json:

{
  "paths": {
    "@lenne.tech/ng-base/*": ["projects/ng-base/*", "projects/ng-base"],
    "@lenne.tech/ng-base": ["dist/ng-base/*", "dist/ng-base"]
  }
}

Delete the content of the projects/some-lib/src/lib/folder and remove content of the root public-api.ts file so that it's empty.

Install ng-samurai to extend the Angular CLI for creating Sub-entries:

npm i -D ng-samurai

Create new Sub-entry (without component --gc falseand module --gm false):

ng g ng-samurai:generate-subentry core --gc false --gm false

Linting

Migration from TSLint (depracted since 2019) to ESLint (see Migrationsanleitung):

ng add @angular-eslint/schematics
ng g @angular-eslint/schematics:convert-tslint-to-eslint ng-base
rm tslint.json
npm uninstall tslint
npm uninstall codelyzer

Additional rules in .eslintrc.json:

{
  "overrides": [
    {
      "rules": {
        "no-underscore-dangle": "off"
      }
    }
  ]
}

Install Prettier and prettier-quick:

npm install --save-dev --save-exact prettier
npm install --save-dev pretty-quick

Add file .prettierrc:

{
  "arrowParens": "always",
  "plugins": ["./extras/prettier-imports"],
  "printWidth": 120,
  "singleQuote": true
}

Add file extras/prettier-imports.js:

const { parsers: typescriptParsers } = require('prettier/parser-typescript');
const ts = require('typescript');

// =============================================================================
// Prettier plugin to optimize and sort imports
// see https://github.com/prettier/prettier/issues/6260
// =============================================================================

class SingleLanguageServiceHost {
  constructor(name, content) {
    this.name = name;
    this.content = content;
    this.getCompilationSettings = ts.getDefaultCompilerOptions;
    this.getDefaultLibFileName = ts.getDefaultLibFilePath;
  }
  getScriptFileNames() {
    return [this.name];
  }
  getScriptVersion() {
    return ts.version;
  }
  getScriptSnapshot() {
    return ts.ScriptSnapshot.fromString(this.content);
  }
  getCurrentDirectory() {
    return '';
  }
}

function applyChanges(text, changes) {
  return changes.reduceRight((text, change) => {
    const head = text.slice(0, change.span.start);
    const tail = text.slice(change.span.start + change.span.length);
    return `${head}${change.newText}${tail}`;
  }, text);
}

function organizeImports(text) {
  const fileName = 'file.ts';
  const host = new SingleLanguageServiceHost(fileName, text);
  const languageService = ts.createLanguageService(host);
  const formatOptions = ts.getDefaultFormatCodeSettings();
  const fileChanges = languageService.organizeImports({ type: 'file', fileName }, formatOptions, {});
  const textChanges = [...fileChanges.map((change) => change.textChanges)];
  return applyChanges(text, textChanges);
}

const parsers = {
  typescript: {
    ...typescriptParsers.typescript,
    preprocess(text) {
      text = organizeImports(text);
      return text;
    },
  },
};

// Uses module.export because of 'Unexpected token export' error
module.exports = parsers;

Add scripts in package.json:

"scripts": {
  ...
  "format:check": "prettier --config ./.prettierrc --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.scss}\"",
  "format:fix": "pretty-quick --staged",
  "format:fixAll": "prettier --write src",
  ...
}

format:check: Check only format:fix: Optimize stage files format:fixAll: Optimize all files

Automatic optimizations and checks

Install husky:

npm install --save-dev husky

Add scripts in package.json:

"scripts": {
  "check": "npm run format:fix && npm run lint",
  "postinstall": "husky install .husky",
}

Add pre-commit hook:

mkdir .husky
npx husky add .husky/pre-commit "cd $(dirname "$0") && npm run check"

Init husky via npm i.

Optimize TypeScript config

To get a little more leeway in dealing with TypeScript's strict typing, the following rules should be disabled in compilerOptions of tsconfig.json:

{
  "compilerOptions": {
    "strictNullChecks": false,
    "strictPropertyInitialization": false,
    "noImplicitAny": false
  }
}

Thanks

Many thanks to the developers of Angular, Apollo Angular and all the developers whose packages are use here.