Initial commit

This commit is contained in:
r1oga
2023-01-08 16:57:38 +01:00
committed by GitHub
commit bb72ece850
34 changed files with 6316 additions and 0 deletions

6
.barrelsby.json Normal file
View File

@@ -0,0 +1,6 @@
{
"directory": ["src/mod"],
"delete": true,
"noSemicolon": true,
"singleQuotes": true
}

1
.env-sample Normal file
View File

@@ -0,0 +1 @@
FOO=bar

14
.eslintignore Normal file
View File

@@ -0,0 +1,14 @@
node_modules
package-lock.json
pnpm-lock.yaml
yarn.lock
# build dirs
build
dist
# Jest
coverage
# JetBrains IDE
.idea

1
.eslintrc.yaml Normal file
View File

@@ -0,0 +1 @@
extends: ['@r1oga']

42
.github/workflows/code-quality.yaml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Code Quality
on:
pull_request:
branches: [main]
jobs:
lint-format-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 19
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm i --ignore-scripts --reporter=silent
- name: Validate
run: pnpm run nps v.ci

53
.github/workflows/coveralls.yaml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Coverage Report
on:
# push trigger required to get coveralls monitoring of default branch
# pull_request required to get PR comments
pull_request:
paths-ignore: ['**/*md', '**/*yaml', '**/*yml']
push:
branches: [main]
paths-ignore: ['**/*md', '**/*yaml', '**/*yml']
jobs:
coveralls:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 19
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm i --ignore-scripts --reporter=silent
- name: Test
run: pnpm run nps t.ci
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./test/coverage/lcov.info

46
.github/workflows/snyk.yaml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Snyk Security Checks
on:
push:
branches: [main]
paths-ignore: ['**/*md', '**/*yaml', '**/*yml']
pull_request:
branches: [main]
paths-ignore: ['**/*md', '**/*yaml', '**/*yml']
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 19
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store-
- name: Install
run: pnpm i --ignore-scripts --reporter=silent
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
node_modules
# TS build dir
dist
# Jest
coverage
# JetBrains IDE
.idea
.env

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
node_modules/.bin/nps precommit

4
.lintstagedrc.yaml Normal file
View File

@@ -0,0 +1,4 @@
'*.+(js|jsx|ts|tsx)':
- eslint --fix .
'*.+(js|json|jsx|ts|tsx|yaml|yml)':
- prettier --write .

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
auto-install-peers=true

14
.prettierignore Normal file
View File

@@ -0,0 +1,14 @@
node_modules
package-lock.json
pnpm-lock.yaml
yarn.lock
# TS build dir
build
dist
# Jest
coverage
# JetBrains IDE
.idea

1
.prettierrc.yaml Normal file
View File

@@ -0,0 +1 @@
'@r1oga/prettier-config'

1
.secret-sample Normal file
View File

@@ -0,0 +1 @@
somesecret

1
.tool-versions Normal file
View File

@@ -0,0 +1 @@
nodejs 19.1.0

26
Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
FROM node:19-alpine as build
RUN apk update
RUN apk add bash
RUN npm i -g pnpm
ENV PATH /app/node_modules/.bin:$PATH
WORKDIR app
COPY .npmrc package.json pnpm-lock.yaml ./
RUN pnpm i -P --frozen-lockfile --ignore-scripts --reporter=silent
RUN pnpm i reflect-metadata ts-node-dev tsconfig-paths tslib typescript
COPY .env package-scripts.yaml tsconfig.json tsconfig.compile.json .barrelsby.json ./
COPY src src
COPY scripts/secrets-entrypoint.sh /usr/local/bin/secrets-entrypoint.sh
RUN chmod +x /usr/local/bin/secrets-entrypoint.sh
ENTRYPOINT ["secrets-entrypoint.sh"]
RUN nps build
CMD ["nps", "start.prod"]

40
README.md Normal file
View File

@@ -0,0 +1,40 @@
<div style='display: flex'>
<img alt='ts icon' width='50' src='https://raw.githubusercontent.com/devicons/devicon/master/icons/typescript/typescript-original.svg'/>
<span style='font-weight: bold'>&nbsp;&nbsp<strong>PROJECT TEMPLATE</strong></span>
</div>
<br/>
![Code Quality GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/r1oga/ts-template/code-quality.yaml?branch=main&label=Code%20Quality) ![Security Check GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/r1oga/ts-template/snyk.yaml?branch=main&label=Security%20%28Snyk%29)
[![Coverage Status](https://coveralls.io/repos/github/r1oga/ts-template/badge.svg?branch=main)](https://coveralls.io/github/r1oga/ts-template?branch=main)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com)
[![nps friendly](https://img.shields.io/badge/nps-friendly-blue.svg?style=flat-square)](https://github.com/sezna/nps)
| Feature | With | Configuration File |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Typings | [Typescript](https://www.typescriptlang.org/) | [tsconfig.json](./tsconfig.json) |
| Scripts | [Nps](https://github.com/sezna/nps) | [package-scripts.yaml](./package-scripts.yaml) |
| Testing | [Jest](https://jestjs.io/), [ts-jest](https://kulshekhar.github.io/ts-jest/) | [jest.config.ts](test/jest.config.ts) |
| Coverage reports | [Coveralls](https://coveralls.io/) | [Coveralls GitHub Action](https://github.com/marketplace/actions/coveralls-github-action) |
| Linting | [Eslint](https://eslint.org/) | [.eslintrc.yaml](./.eslintrc.yaml) |
| Formatting | [Prettier](https://prettier.io/) | [.prettierrc.yaml](./.prettierrc.yaml) |
| Continuous Integration | [GitHub Workflow](https://docs.github.com/en/actions/using-workflows) | [.github/workflows](./.github/workflows) |
| Import aliases | [Typescript paths](https://www.typescriptlang.org/tsconfig#paths), [module-alias](https://github.com/ilearnio/module-alias) | [tsconfig.json](https://github.com/r1oga/ts-template/blob/5d6983a6d28429b9dd256edf40bad5ee48c33d9c/tsconfig.json#L26), [package.json](https://github.com/r1oga/ts-template/blob/5d6983a6d28429b9dd256edf40bad5ee48c33d9c/package.json#L9) |
| Rollup exports | [Barrelsby](https://github.com/bencoveney/barrelsby) | [.barrelsby.json](./.barrelsby.json) |
| Containerization | [Docker](https://www.docker.com/) | [Dockerfile](./Dockerfile), [docker-compose.yaml](./docker-compose.yaml) |
| Pre-commit hook (linting, formatting) | [Husky](https://typicode.github.io/husky), [lint-staged](https://github.com/okonet/lint-staged) | [pre-commit](./.husky/pre-commit), [.lintstagedrc.yaml](./.lintstagedrc.yaml) |
| Security Checks | [Snyk](https://snyk.io/) | [snyk.yaml](./.github/workflows/snyk.yaml) |
## Start
Node
```commandline
npm run setup
nps start
```
Docker
```commandline
docker compose up
```

16
docker-compose.yaml Normal file
View File

@@ -0,0 +1,16 @@
version: '3.9'
services:
template:
build: .
environment:
SECRET_FILE: /run/secrets/secret
FOO: ${FOO}
secrets:
- secret
env_file:
- .env-sample
secrets:
secret:
file: .secret-sample

74
package-scripts.yaml Normal file
View File

@@ -0,0 +1,74 @@
scripts:
barrels:
script: barrelsby --config .barrelsby.json
hiddenFromHelp: true
build:
script: nps clean barrels compile
description: Build project
clean:
script: rm -rf dist
hiddenFromHelp: true
compile:
script: tsc -p tsconfig.compile.json
hiddenFromHelp: true
fix:
default:
script: nps lint.fix format.fix
hiddenFromHelp: Fix linting and formatting errors
format:
default:
script: nps 'test.once --selectProjects prettier --coverage false'
description: Check for format errors
fix:
script: prettier --write .
hiddenFromHelp: true
lint:
default:
script: nps 'test.once --selectProjects lint --coverage false'
description: Check for lint errors
fix:
script: eslint . --ext .ts --fix
hiddenFromHelp: true
precommit:
script: tsc && nps barrels && lint-staged
hiddenFromHelp: true
start:
default:
script: nps build start.prod
description: Start in production mode
dev:
script: tsnd --cls --exit-child --ignore-watch node_modules --quiet --respawn --rs --transpile-only -r tsconfig-paths/register src
description: Start in dev mode
prod:
script: NODE_ENV=production node -r module-alias/register dist
hiddenFromHelp: true
test:
default:
script: nps 'test.once --watch --coverage=false'
description: Run tests (watch, no coverage)
ci:
script: jest --config test/jest.config.ts --selectProjects unit --silent --reporters=jest-silent-reporter
hiddenFromHelp: true
debug:
script: node --inspect-brk ./node_modules/jest/bin/jest --runInBand --watch --config test/jest.unit.ts
description: Start tests in debug mode (open in chrome://inspect)
once:
script: jest --config test/jest.config.ts
description: Run tests (once, coverage)
validate:
default:
script: concurrently tsc 'nps test.once'
description: Perform static analysis testing (lint, format...)
ci:
script: concurrently tsc 'jest --config test/jest.config.ts --selectProjects prettier lint --silent --reporters=jest-silent-reporter --coverage false'
hiddenFromHelp: true

57
package.json Normal file
View File

@@ -0,0 +1,57 @@
{
"name": "ts-template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"nps": "nps",
"prepare": "husky install",
"setup": "pnpm 2> /dev/null || npm i -g pnpm && pnpm i"
},
"_moduleAliases": {
"@mod": "dist/mod"
},
"author": "r1oga",
"license": "ISC",
"devDependencies": {
"@r1oga/eslint-config": "^1.1.6",
"@r1oga/prettier-config": "^1.1.8",
"@types/jest": "^29.2.3",
"@types/supertest": "^2.0.12",
"husky": "^8.0.2",
"is-ci": "^3.0.1",
"jest": "^29.3.1",
"jest-chain": "^1.1.6",
"jest-mock-extended": "^3.0.1",
"jest-runner-eslint": "^1.1.0",
"jest-runner-prettier": "^1.0.0",
"jest-silent-reporter": "^0.5.0",
"jest-watch-select-projects": "^2.0.0",
"jest-watch-typeahead": "^2.2.1",
"lint-staged": "^13.1.0",
"reflect-metadata": "^0.1.13",
"supertest": "^6.3.1",
"ts-jest": "^29.0.3",
"ts-node-dev": "^2.0.0",
"tsconfig-paths": "^4.1.0",
"tslib": "^2.4.1",
"typescript": "^4.9.4"
},
"dependencies": {
"barrelsby": "^2.5.1",
"concurrently": "^7.6.0",
"module-alias": "^2.2.2",
"nps": "^5.10.0"
},
"engines": {
"node": ">=19"
},
"jest-runner-eslint": {
"cliOptions": {
"ignorePath": "./.eslintignore"
}
},
"jest-runner-tsc": {
"tsconfigPath": "./tsconfig.json"
}
}

5694
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# https://github.com/adrian-gheorghe/demo-docker-secrets-env-vars
set -e
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
file_env "SECRET"
exec "$@"

13
src/index.ts Normal file
View File

@@ -0,0 +1,13 @@
import { TEST } from '@mod/file'
const A = [1, 2, 3]
interface B {
foo: string
}
const c: B = { foo: 'bar' }
console.log(A, c, TEST, 1)
console.log(process.env.SECRET)
console.log(process.env.FOO)

1
src/mod/file.ts Normal file
View File

@@ -0,0 +1 @@
export const TEST = 'abc'

5
src/mod/index.ts Normal file
View File

@@ -0,0 +1,5 @@
/**
* @file Automatically generated by barrelsby.
*/
export * from './file'

7
test/jest.common.ts Normal file
View File

@@ -0,0 +1,7 @@
import type { JestConfigWithTsJest } from 'ts-jest'
const jestCommonConfig: JestConfigWithTsJest = {
rootDir: '..',
}
export default jestCommonConfig

19
test/jest.config.ts Normal file
View File

@@ -0,0 +1,19 @@
import type { JestConfigWithTsJest } from 'ts-jest'
const jestConfig: JestConfigWithTsJest = {
collectCoverage: true,
collectCoverageFrom: ['<rootDir>/src/**'],
coverageDirectory: 'coverage',
projects: [
'./test/jest.lint.ts',
'./test/jest.prettier.ts',
'./test/jest.unit.ts',
],
watchPlugins: [
'jest-watch-select-projects',
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
}
export default jestConfig

13
test/jest.lint.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { JestConfigWithTsJest } from 'ts-jest'
import common from './jest.common'
const jestLintConfig: JestConfigWithTsJest = {
...common,
displayName: 'lint',
runner: 'jest-runner-eslint',
testMatch: ['<rootDir>/**/*.(cjs|mjs|js|jsx|ts|tsx)'],
testPathIgnorePatterns: ['<rootDir>/test/coverage'],
}
export default jestLintConfig

36
test/jest.prettier.ts Normal file
View File

@@ -0,0 +1,36 @@
import type { JestConfigWithTsJest } from 'ts-jest'
import common from './jest.common'
const jestLintConfig: JestConfigWithTsJest = {
...common,
displayName: 'prettier',
moduleFileExtensions: [
'js',
'mjs',
'jsx',
'ts',
'tsx',
'css',
'less',
'scss',
'html',
'json',
'graphql',
'md',
'markdown',
'mdx',
'yaml',
'yml',
],
runner: 'jest-runner-prettier',
testMatch: [
'<rootDir>/**/*.(cjs|mjs|js|jsx|ts|tsx|css|scss|less|html|md|markdown|json|yaml|yml)',
],
testPathIgnorePatterns: [
'<rootDir>/pnpm-lock.yaml',
'<rootDir>/test/coverage',
],
}
export default jestLintConfig

29
test/jest.unit.ts Normal file
View File

@@ -0,0 +1,29 @@
import type { JestConfigWithTsJest } from 'ts-jest'
import { pathsToModuleNameMapper } from 'ts-jest'
import { compilerOptions } from '../tsconfig.json'
import common from './jest.common'
const jestUnitConfig: JestConfigWithTsJest = {
...common,
clearMocks: true,
coveragePathIgnorePatterns: ['<rootDir>/src/index.ts'],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70,
},
},
displayName: 'unit',
moduleDirectories: ['node_modules', __dirname],
moduleFileExtensions: ['ts', 'js', 'json'],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>/',
}),
preset: 'ts-jest',
setupFilesAfterEnv: ['jest-chain', './test/setup.ts'],
}
export default jestUnitConfig

1
test/setup.ts Normal file
View File

@@ -0,0 +1 @@
import 'reflect-metadata'

8
test/unit/mod.test.ts Normal file
View File

@@ -0,0 +1,8 @@
import { TEST } from '@mod'
describe('Test', () => {
it('some test', () => {
// can chain matchers with 'jest-chain'
expect(TEST).toBeDefined().toEqual('abc')
})
})

16
tsconfig.compile.json Normal file
View File

@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"exclude": ["test"],
"compilerOptions": {
"noEmit": false,
"outDir": "dist",
"moduleResolution": "Node",
"inlineSourceMap": true,
"inlineSources": true,
"declaration": true,
"declarationMap": true,
"declarationDir": "./dist/types",
"removeComments": true,
"preserveConstEnums": true
}
}

33
tsconfig.json Normal file
View File

@@ -0,0 +1,33 @@
{
"include": ["src", "test"],
"exclude": ["test/coverage"],
"files": ["node_modules/jest-chain/types/index.d.ts"],
"compilerOptions": {
"noEmit": true,
"baseUrl": ".",
"sourceRoot": "src",
"target": "ESNext",
"module": "commonjs",
"inlineSourceMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowJs": true,
"checkJs": true,
"declaration": false,
"declarationMap": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"strictPropertyInitialization": true,
"noUnusedLocals": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"importHelpers": true,
"paths": {
"@mod": ["src/mod"],
"@mod/*": ["src/mod/*"]
}
}
}