Creando app de NestJS

💡Este template esta basado en el repositorio de nestjs template de Inlaze. Se debe generar una estructura parecida o igual a la siguiente para tener un proyecto configurado de acuerdo a los estándares.

├───.husky/
│   └───pre-commit
├───src/
│   ├───app/
│   │   ├───app.controller.spec.ts
│   │   ├───app.controller.ts
│   │   ├───app.module.ts
│   │   └───app.service.ts
│   └───main.ts
├───test/
│   └───jest-e2e.json
├───.eslintignore
├───.eslintrc.js
├───.lintstagedrc
├───.prettierignore
├───.prettierrc
├───nest-cli.json
├───package.json
├───pnpm-lock.yaml
├───README.md
├───tsconfig.build.json
└───tsconfig.json

Husky Precommit

Se trata de un paquete que permite ejecutar precommits para hacer validaciones de código. Se complementa con el paquete de lint-staged para poder validar sólo lo que el desarrollador tiene el stage, para tener esto configurado debemos instalar los paquetes como devDependencies, así:

pnpm i -D husky lint-staged

El husky debe tener un bash que es el que ejecuta antes de cada commit, este debe ser el contenido:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

Como la ejecución es del lint-staged, entonces toca crear un .lintstagedrc que es un json con la configuración de la ejecución que debe ejecutar el precommit:

{
  "**/{src,test}/**/*.{ts,tsx}": ["eslint", "prettier --write"],
  "**/*.{ts,tsx}": "bash -c tsc -p tsconfig.json --noEmit"
}

Eslint

La configuración que debe haber de eslint es:

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "tsconfig.json",
    tsconfigRootDir : __dirname, 
    sourceType: "module",
  },
  plugins: [
    "@typescript-eslint/eslint-plugin",
    "unused-imports"
  ],
  extends: [
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
    "plugin:@typescript-eslint/strict",
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: [".eslintrc.js", "global.d.ts"],
  rules: {
    "unused-imports/no-unused-imports": "error",
    "@typescript-eslint/triple-slash-reference": "off",
    "@typescript-eslint/interface-name-prefix": "off",
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-inferrable-types": "off",
    "@typescript-eslint/ban-types": "off",
    "@typescript-eslint/no-unsafe-assignment": "off",
    "@typescript-eslint/adjacent-overload-signatures": "error",
    "@typescript-eslint/await-thenable": "error",
    "@typescript-eslint/ban-ts-comment": "error",
    "@typescript-eslint/ban-tslint-comment": "error",
    "@typescript-eslint/class-literal-property-style": "error",
    "@typescript-eslint/consistent-generic-constructors": "error",
    "@typescript-eslint/consistent-indexed-object-style": "error",
    "@typescript-eslint/consistent-type-assertions": "error",
    "@typescript-eslint/consistent-type-definitions": "error",
    "@typescript-eslint/consistent-type-exports": "error",
    "@typescript-eslint/consistent-type-imports": "error",
    "@typescript-eslint/explicit-function-return-type": "error",
    "@typescript-eslint/explicit-member-accessibility": "error",
    "@typescript-eslint/explicit-module-boundary-types": "error",
    "@typescript-eslint/method-signature-style": "error",
    "@typescript-eslint/no-confusing-non-null-assertion": "error",
    "@typescript-eslint/no-duplicate-enum-values": "error",
    "@typescript-eslint/no-duplicate-type-constituents": "error",
    "@typescript-eslint/no-extra-non-null-assertion": "error",
    "@typescript-eslint/no-extraneous-class": "off",
    "@typescript-eslint/no-for-in-array": "error",
    "@typescript-eslint/no-import-type-side-effects": "error",
    "@typescript-eslint/no-require-imports": "error",
    "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
    "@typescript-eslint/no-unnecessary-condition": "error",
    "@typescript-eslint/no-unnecessary-type-assertion": "off",
    "@typescript-eslint/no-unsafe-call": "off",
    "@typescript-eslint/no-unsafe-member-access": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    "@typescript-eslint/prefer-reduce-type-parameter": "error",
    "@typescript-eslint/prefer-string-starts-ends-with": "error",
    "@typescript-eslint/restrict-template-expressions": "off",
    "@typescript-eslint/prefer-readonly": "error",
    "no-return-await": "off",
    "@typescript-eslint/return-await": "error",
    "@typescript-eslint/no-misused-promises": "off",
    "eqeqeq": ["error", "always"],
    "no-console": "error",
    "@typescript-eslint/unified-signatures": "off",
    "@typescript-eslint/naming-convention": ["error",
      {
        selector: 'default',
        format: ['camelCase'],
      },
      {
        selector: 'variable',
        format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
        leadingUnderscore: 'allow',
      },
      {
        selector: 'parameter',
        format: ['camelCase'],
        leadingUnderscore: 'allow',
      },
      {
        selector: 'memberLike',
        modifiers: ['private'],
        format: ['camelCase', "UPPER_CASE", "snake_case"],
        leadingUnderscore: 'allow',
      },
      {
        selector: 'memberLike',
        modifiers: ['public'],
        format: ['camelCase', "UPPER_CASE", "snake_case", "PascalCase"],
      },
      {
        selector: 'typeLike',
        format: ['PascalCase'],
      },
      {
        selector: "enumMember",
        format: ["UPPER_CASE"]
      },
      {
        selector: "objectLiteralMethod",
        format: ["camelCase", "PascalCase"]
      },
      {
        selector: "objectLiteralProperty",
        format: ["camelCase", "UPPER_CASE", "PascalCase", "snake_case"]
      }
    ]
  },
};

Prettier

El prettier debe tener lo siguiente configurado:

{
  "singleQuote": false,
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "bracketSpacing": true,
  "bracketSameLine": true,
  "printWidth": 100,
  "proseWrap": "always",
  "endOfLine": "crlf",
  "useTabs": false
}

TS Config

La configuración de typescript es estricta, por lo que debe tener las siguientes normas para que sea de acuerdo a los estándares planteados:

{
  "compilerOptions": {
    "strict": true,
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "resolveJsonModule": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useUnknownInCatchVariables": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noImplicitOverride": true,
  }
}

Last updated