feat: new project from template
This commit is contained in:
commit
33c2936275
|
@ -0,0 +1,35 @@
|
||||||
|
version: "2"
|
||||||
|
|
||||||
|
checks:
|
||||||
|
method-complexity:
|
||||||
|
config:
|
||||||
|
threshold: 6 # 5 is *just* too low and flags a number of otherwise readable methods
|
||||||
|
method-lines:
|
||||||
|
config:
|
||||||
|
threshold: 30 # with object literals, lines are not a great measure
|
||||||
|
|
||||||
|
exclude_patterns:
|
||||||
|
- CHANGELOG.md
|
||||||
|
- config/
|
||||||
|
- docs/api/
|
||||||
|
- docs/dev/style.md
|
||||||
|
- src/migration/
|
||||||
|
- vendor/
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
duplication:
|
||||||
|
enabled: true
|
||||||
|
config:
|
||||||
|
languages:
|
||||||
|
typescript:
|
||||||
|
mass_threshold: 225
|
||||||
|
eslint:
|
||||||
|
enabled: true
|
||||||
|
config:
|
||||||
|
config: config/eslint.json
|
||||||
|
fixme:
|
||||||
|
enabled: true
|
||||||
|
markdownlint:
|
||||||
|
enabled: true
|
||||||
|
shellcheck:
|
||||||
|
enabled: true
|
|
@ -0,0 +1,7 @@
|
||||||
|
.nyc_output/
|
||||||
|
docs/api/
|
||||||
|
node_modules/
|
||||||
|
out/api
|
||||||
|
out/cache/
|
||||||
|
out/coverage/
|
||||||
|
out/tmp/
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: report a problem
|
||||||
|
title: ''
|
||||||
|
labels: status/new, type/bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug
|
||||||
|
|
||||||
|
Something is broken!
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
(quick summary of the issue)
|
||||||
|
|
||||||
|
## Steps to Reproduce
|
||||||
|
|
||||||
|
- install
|
||||||
|
- configure
|
||||||
|
- run
|
||||||
|
- ...
|
||||||
|
- :(
|
||||||
|
|
||||||
|
## Actual Behavior
|
||||||
|
|
||||||
|
```none
|
||||||
|
example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
```none
|
||||||
|
example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
(detailed description of the expected behavior and what goes wrong)
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
name: Feature Suggestion
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: status/new, type/feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Feature
|
||||||
|
|
||||||
|
Something is missing!
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
An elevator pitch of the feature and why it will be useful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- [ ] items to be implemented
|
||||||
|
- [ ] additional details
|
||||||
|
|
||||||
|
## Use Case
|
||||||
|
|
||||||
|
At least one concrete use case for the feature, ideally with step-by-step example.
|
||||||
|
|
||||||
|
Bonus points if you can refer to another issue here.
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Outstanding questions, potential problems, and further research that might be needed.
|
||||||
|
|
||||||
|
New or updated dependencies should be noted here.
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Merge
|
||||||
|
|
||||||
|
Something is ready to be merged!
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
List the issue(s) resolved or addressed by this change.
|
||||||
|
|
||||||
|
Changes MUST be related to an issue. If one does not exist,
|
||||||
|
please create one with the appropriate `type/` label.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
Please ensure all required status checks have succeeded:
|
||||||
|
|
||||||
|
- [ ] Gitlab pipeline has passed
|
||||||
|
- [ ] lint warnings have not increased
|
||||||
|
- [ ] documentation has been written
|
||||||
|
- [ ] tests have been added and consistently pass
|
|
@ -0,0 +1,15 @@
|
||||||
|
.awcache/
|
||||||
|
.licenses/
|
||||||
|
.nyc_output/
|
||||||
|
node_modules/
|
||||||
|
out/
|
||||||
|
temp/
|
||||||
|
|
||||||
|
# types
|
||||||
|
*.bak
|
||||||
|
*.pid
|
||||||
|
*.swp
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
yarn-error.log
|
|
@ -0,0 +1,159 @@
|
||||||
|
include:
|
||||||
|
- local: /config/gitlab/ci-tools.yml
|
||||||
|
- local: /config/gitlab/ci-rules.yml
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- status-pre
|
||||||
|
- build
|
||||||
|
- image
|
||||||
|
- publish
|
||||||
|
- status-post
|
||||||
|
|
||||||
|
# build jobs
|
||||||
|
build-node:
|
||||||
|
stage: build
|
||||||
|
extends:
|
||||||
|
- .build-node
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make ci
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
expire_in: 1 day
|
||||||
|
paths:
|
||||||
|
- out/
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: "${CI_COMMIT_REF_SLUG}"
|
||||||
|
policy: pull-push
|
||||||
|
paths:
|
||||||
|
- node_modules/
|
||||||
|
- out/api
|
||||||
|
- out/cache
|
||||||
|
- out/tmp
|
||||||
|
|
||||||
|
build-image-alpine-branch:
|
||||||
|
extends:
|
||||||
|
- .build-docker
|
||||||
|
stage: image
|
||||||
|
except:
|
||||||
|
- tags
|
||||||
|
script:
|
||||||
|
- ./scripts/docker-build.sh --push --default
|
||||||
|
variables:
|
||||||
|
IMAGE_ARCH: alpine
|
||||||
|
|
||||||
|
build-image-alpine-tag:
|
||||||
|
extends:
|
||||||
|
- .build-docker
|
||||||
|
stage: image
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
script:
|
||||||
|
- ./scripts/docker-build.sh --push --default
|
||||||
|
variables:
|
||||||
|
IMAGE_ARCH: alpine
|
||||||
|
|
||||||
|
build-image-stretch-branch:
|
||||||
|
stage: image
|
||||||
|
extends: [.build-docker]
|
||||||
|
except:
|
||||||
|
- tags
|
||||||
|
script:
|
||||||
|
- ./scripts/docker-build.sh --push
|
||||||
|
variables:
|
||||||
|
IMAGE_ARCH: stretch
|
||||||
|
|
||||||
|
build-image-stretch-tag:
|
||||||
|
stage: image
|
||||||
|
extends:
|
||||||
|
- .build-docker
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
script:
|
||||||
|
- ./scripts/docker-build.sh --push
|
||||||
|
variables:
|
||||||
|
IMAGE_ARCH: stretch
|
||||||
|
|
||||||
|
# publish jobs
|
||||||
|
publish-npm:
|
||||||
|
stage: publish
|
||||||
|
extends:
|
||||||
|
- .build-node
|
||||||
|
- .deploy-tags
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- build-node
|
||||||
|
script:
|
||||||
|
- npm publish
|
||||||
|
|
||||||
|
# commit status
|
||||||
|
climate-pending:
|
||||||
|
stage: status-pre
|
||||||
|
extends:
|
||||||
|
- .build-climate
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cc-test-reporter before-build
|
||||||
|
|
||||||
|
climate-success:
|
||||||
|
stage: status-post
|
||||||
|
extends:
|
||||||
|
- .build-climate
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- build-node
|
||||||
|
script:
|
||||||
|
- make upload-climate
|
||||||
|
|
||||||
|
codecov-success:
|
||||||
|
stage: status-post
|
||||||
|
extends:
|
||||||
|
- .build-codecov
|
||||||
|
|
||||||
|
when: on_success
|
||||||
|
dependencies:
|
||||||
|
- build-node
|
||||||
|
script:
|
||||||
|
- make upload-codecov
|
||||||
|
|
||||||
|
github-pending:
|
||||||
|
stage: status-pre
|
||||||
|
extends:
|
||||||
|
- .build-curl
|
||||||
|
script:
|
||||||
|
- ./scripts/github-status.sh pending
|
||||||
|
|
||||||
|
github-failure:
|
||||||
|
stage: status-post
|
||||||
|
extends:
|
||||||
|
- .build-curl
|
||||||
|
when: on_failure
|
||||||
|
script:
|
||||||
|
- ./scripts/github-status.sh failure
|
||||||
|
|
||||||
|
github-success:
|
||||||
|
stage: status-post
|
||||||
|
extends:
|
||||||
|
- .build-curl
|
||||||
|
when: on_success
|
||||||
|
script:
|
||||||
|
- ./scripts/github-status.sh success
|
||||||
|
|
||||||
|
sonar-success:
|
||||||
|
stage: status-post
|
||||||
|
extends:
|
||||||
|
- .build-sonar
|
||||||
|
|
||||||
|
when: on_success
|
||||||
|
script:
|
||||||
|
- make node_modules
|
||||||
|
- sonar-scanner
|
||||||
|
-Dsonar.projectKey=ssube_rollup-template
|
||||||
|
-Dsonar.projectVersion=${CI_COMMIT_REF_SLUG}
|
||||||
|
-Dsonar.organization=ssube-github
|
||||||
|
-Dsonar.sources=src/,test/
|
||||||
|
-Dsonar.host.url=https://sonarcloud.io
|
||||||
|
-Dsonar.login=${SONAR_SECRET}
|
||||||
|
-Dsonar.typescript.lcov.reportPaths=out/coverage/lcov.info
|
|
@ -0,0 +1,36 @@
|
||||||
|
.awcache/
|
||||||
|
.git/
|
||||||
|
.github/
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
config/
|
||||||
|
deploy/
|
||||||
|
node_modules/
|
||||||
|
out/cache/
|
||||||
|
out/coverage/
|
||||||
|
out/coverage-*
|
||||||
|
out/docs/
|
||||||
|
out/typings/
|
||||||
|
out/*.db
|
||||||
|
out/*.html
|
||||||
|
out/*.json
|
||||||
|
out/test-*
|
||||||
|
out/tmp/
|
||||||
|
scripts/
|
||||||
|
src/
|
||||||
|
temp/
|
||||||
|
test/
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
.codeclimate.yml
|
||||||
|
.dockerignore
|
||||||
|
.gitlab-ci.yml
|
||||||
|
.gitmodules
|
||||||
|
.mdlrc
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
licensed.yml
|
||||||
|
Makefile
|
||||||
|
renovate.json
|
||||||
|
tsconfig.json
|
||||||
|
yarn-*
|
|
@ -0,0 +1 @@
|
||||||
|
registry=https://artifacts.apextoaster.com/repository/group-npm/
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM node:12-alpine
|
||||||
|
|
||||||
|
COPY package.json /app/package.json
|
||||||
|
COPY yarn.lock /app/yarn.lock
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN yarn install --production
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
RUN yarn global add file:$(pwd)
|
||||||
|
ENV PATH="${PATH}:$(yarn global bin)"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "node", "/app/out/index.js" ]
|
||||||
|
CMD [ "--help" ]
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM node:12-stretch
|
||||||
|
|
||||||
|
COPY package.json /app/package.json
|
||||||
|
COPY yarn.lock /app/yarn.lock
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN yarn install --production
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
RUN yarn global add file:$(pwd)
|
||||||
|
ENV PATH="${PATH}:$(yarn global bin)"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "node", "/app/out/index.js" ]
|
||||||
|
CMD [ "--help" ]
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2018 Sean Sube
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,166 @@
|
||||||
|
# Git
|
||||||
|
export GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
export GIT_COMMIT ?= $(shell git rev-parse HEAD)
|
||||||
|
export GIT_OPTIONS ?=
|
||||||
|
export GIT_REMOTES ?= $(shell git remote -v | awk '{ print $1; }' | sort | uniq)
|
||||||
|
export GIT_TAG ?= $(shell git tag -l --points-at HEAD | head -1)
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
# resolve the makefile's path and directory, from https://stackoverflow.com/a/18137056
|
||||||
|
export MAKE_PATH ?= $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||||
|
export ROOT_PATH ?= $(dir $(MAKE_PATH))
|
||||||
|
export CONFIG_PATH ?= $(ROOT_PATH)/config
|
||||||
|
export DOCS_PATH ?= $(ROOT_PATH)/docs
|
||||||
|
export SCRIPT_PATH ?= $(ROOT_PATH)/scripts
|
||||||
|
export SOURCE_PATH ?= $(ROOT_PATH)/src
|
||||||
|
export TARGET_PATH ?= $(ROOT_PATH)/out
|
||||||
|
export TARGET_LOG ?= $(TARGET_PATH)/make.log
|
||||||
|
export TARGET_MAIN ?= $(TARGET_PATH)/index.js
|
||||||
|
export TEST_PATH ?= $(ROOT_PATH)/test
|
||||||
|
export VENDOR_PATH ?= $(ROOT_PATH)/vendor
|
||||||
|
|
||||||
|
# CI
|
||||||
|
export CI_COMMIT_REF_SLUG ?= $(GIT_BRANCH)
|
||||||
|
export CI_COMMIT_SHA ?= $(GIT_COMMIT)
|
||||||
|
export CI_COMMIT_TAG ?= $(GIT_TAG)
|
||||||
|
export CI_ENVIRONMENT_SLUG ?= local
|
||||||
|
export CI_JOB_ID ?= 0
|
||||||
|
export CI_PROJECT_PATH ?= $(shell ROOT_PATH=$(ROOT_PATH) ${SCRIPT_PATH}/ci-project-path.sh)
|
||||||
|
export CI_RUNNER_DESCRIPTION ?= $(shell hostname)
|
||||||
|
export CI_RUNNER_ID ?= $(shell hostname)
|
||||||
|
export CI_RUNNER_VERSION ?= 0.0.0
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
export DEBUG_BIND ?= 127.0.0.1
|
||||||
|
export DEBUG_PORT ?= 9229
|
||||||
|
|
||||||
|
# Versions
|
||||||
|
export NODE_VERSION := $(shell node -v || echo "none")
|
||||||
|
export RUNNER_VERSION := $(CI_RUNNER_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
# Node options
|
||||||
|
NODE_BIN := $(ROOT_PATH)/node_modules/.bin
|
||||||
|
NODE_CMD ?= $(shell env node)
|
||||||
|
NODE_DEBUG ?= --inspect-brk=$(DEBUG_BIND):$(DEBUG_PORT) --nolazy
|
||||||
|
NODE_INFO := $(shell node -v)
|
||||||
|
|
||||||
|
# Tool options
|
||||||
|
COVER_OPTS ?= --reporter=lcov --reporter=text-summary --reporter=html --report-dir="$(TARGET_PATH)/coverage" --exclude-after-remap
|
||||||
|
MOCHA_OPTS ?= --check-leaks --colors --sort --ui bdd
|
||||||
|
RELEASE_OPTS ?= --commit-all
|
||||||
|
|
||||||
|
.PHONY: all clean clean-deps clean-target configure help todo
|
||||||
|
.PHONY: build build-bundle build-docs build-image test test-check test-cover test-watch
|
||||||
|
.PHONY: yarn-install yarn-upgrade git-push git-stats license-check release release-dry upload-climate upload-codecov
|
||||||
|
|
||||||
|
all: build test ## builds, bundles, and tests the application
|
||||||
|
@echo Success!
|
||||||
|
|
||||||
|
clean: ## clean up everything added by the default target
|
||||||
|
clean: clean-deps clean-target
|
||||||
|
|
||||||
|
clean-deps: ## clean up the node_modules directory
|
||||||
|
rm -rf node_modules
|
||||||
|
|
||||||
|
clean-target: ## clean up the target directory
|
||||||
|
rm -rf $(TARGET_PATH)
|
||||||
|
|
||||||
|
configure: ## create the target directory and other files not in git
|
||||||
|
mkdir -p $(TARGET_PATH)
|
||||||
|
|
||||||
|
node_modules: yarn-install
|
||||||
|
|
||||||
|
# from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
||||||
|
help: ## print this help
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort \
|
||||||
|
| sed 's/^.*\/\(.*\)/\1/' \
|
||||||
|
| awk 'BEGIN {FS = ":[^:]*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
todo:
|
||||||
|
@echo "Remaining tasks:"
|
||||||
|
@echo ""
|
||||||
|
@grep -i "todo" -r docs/ src/ test/ || true
|
||||||
|
@echo ""
|
||||||
|
@echo "Pending tests:"
|
||||||
|
@echo ""
|
||||||
|
@grep "[[:space:]]xit" -r test/ || true
|
||||||
|
@echo "Casts to any:"
|
||||||
|
@echo ""
|
||||||
|
@grep "as any" -r src/ test/ || true
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# Build targets
|
||||||
|
build: ## builds, bundles, and tests the application
|
||||||
|
build: build-bundle build-docs
|
||||||
|
|
||||||
|
build-bundle: node_modules
|
||||||
|
$(NODE_BIN)/rollup --config $(CONFIG_PATH)/rollup.js
|
||||||
|
sed -i '1s;^;#! /usr/bin/env node\n\n;' $(TARGET_PATH)/index.js
|
||||||
|
|
||||||
|
build-docs: ## generate html docs
|
||||||
|
$(NODE_BIN)/api-extractor run --config $(CONFIG_PATH)/api-extractor.json --local -v
|
||||||
|
$(NODE_BIN)/api-documenter markdown -i $(TARGET_PATH)/api -o $(DOCS_PATH)/api
|
||||||
|
|
||||||
|
build-image: ## build a docker image
|
||||||
|
$(SCRIPT_PATH)/docker-build.sh --push
|
||||||
|
|
||||||
|
test: ## run mocha unit tests
|
||||||
|
test: test-cover
|
||||||
|
|
||||||
|
test-check: ## run mocha unit tests with coverage reports
|
||||||
|
$(NODE_BIN)/nyc $(COVER_OPTS) $(NODE_BIN)/mocha $(MOCHA_OPTS) $(TARGET_PATH)/test.js
|
||||||
|
|
||||||
|
test-cover: ## run mocha unit tests with coverage reports
|
||||||
|
test-cover: test-check
|
||||||
|
sed -i $(TARGET_PATH)/coverage/lcov.info \
|
||||||
|
-e '/external ".*"$$/,/end_of_record/d' \
|
||||||
|
-e '/ sync$$/,/end_of_record/d' \
|
||||||
|
-e '/test sync/,/end_of_record/d' \
|
||||||
|
-e '/node_modules/,/end_of_record/d' \
|
||||||
|
-e '/bootstrap$$/,/end_of_record/d' \
|
||||||
|
-e '/universalModuleDefinition/,/end_of_record/d'
|
||||||
|
sed -n '/^SF/,$$p' -i $(TARGET_PATH)/coverage/lcov.info
|
||||||
|
sed '1s;^;TN:\n;' -i $(TARGET_PATH)/coverage/lcov.info
|
||||||
|
|
||||||
|
test-watch:
|
||||||
|
$(NODE_BIN)/nyc $(COVER_OPTS) $(NODE_BIN)/mocha $(MOCHA_OPTS) --watch $(TARGET_PATH)/test-bundle.js
|
||||||
|
|
||||||
|
yarn-install: ## install dependencies from package and lock file
|
||||||
|
yarn
|
||||||
|
|
||||||
|
yarn-global: ## install bundle as a global tool
|
||||||
|
yarn global add file:$(ROOT_PATH)
|
||||||
|
|
||||||
|
yarn-update: ## check yarn for outdated packages
|
||||||
|
yarn upgrade-interactive --latest
|
||||||
|
|
||||||
|
# release targets
|
||||||
|
git-push: ## push to both gitlab and github (this assumes you have both remotes set up)
|
||||||
|
git push $(GIT_OPTIONS) github $(GIT_BRANCH)
|
||||||
|
git push $(GIT_OPTIONS) gitlab $(GIT_BRANCH)
|
||||||
|
|
||||||
|
# from https://gist.github.com/amitchhajer/4461043#gistcomment-2349917
|
||||||
|
git-stats: ## print git contributor line counts (approx, for fun)
|
||||||
|
git ls-files | while read f; do git blame -w -M -C -C --line-porcelain "$$f" |\
|
||||||
|
grep -I '^author '; done | sort -f | uniq -ic | sort -n
|
||||||
|
|
||||||
|
license-check: ## check license status
|
||||||
|
licensed cache
|
||||||
|
licensed status
|
||||||
|
|
||||||
|
release: ## create a release
|
||||||
|
$(NODE_BIN)/standard-version --sign $(RELEASE_OPTS)
|
||||||
|
GIT_OPTIONS=--tags $(MAKE) git-push
|
||||||
|
|
||||||
|
release-dry: ## test creating a release
|
||||||
|
$(NODE_BIN)/standard-version --sign $(RELEASE_OPTS) --dry-run
|
||||||
|
|
||||||
|
upload-climate:
|
||||||
|
cc-test-reporter format-coverage -t lcov -o $(TARGET_PATH)/coverage/codeclimate.json -p $(ROOT_PATH) $(TARGET_PATH)/coverage/lcov.info
|
||||||
|
cc-test-reporter upload-coverage --debug -i $(TARGET_PATH)/coverage/codeclimate.json -r "$(shell echo "${CODECLIMATE_SECRET}" | base64 -d)"
|
||||||
|
|
||||||
|
upload-codecov:
|
||||||
|
codecov --disable=gcov --file=$(TARGET_PATH)/coverage/lcov.info --token=$(shell echo "${CODECOV_SECRET}" | base64 -d)
|
||||||
|
|
||||||
|
include $(shell find $(ROOT_PATH) -name '*.mk' | grep -v node_modules)
|
|
@ -0,0 +1,149 @@
|
||||||
|
# Rollup Template
|
||||||
|
|
||||||
|
This project contains the configuration and build scripts for most of
|
||||||
|
my Typescript projects, with scripts to create a new project and keep
|
||||||
|
existing ones up-to-date. Even this readme is a template for others.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- build scripts with `make`
|
||||||
|
- build pipeline with `gitlab`
|
||||||
|
- update github commit status
|
||||||
|
- publish docker images from branches & tags
|
||||||
|
- publish npm packages from tags
|
||||||
|
- bundled with `rollup`
|
||||||
|
- type checked with `typescript`
|
||||||
|
- style checked with `eslint` (with `tslint` rules and other plugins)
|
||||||
|
- tested with `mocha` (with source map support and helpers for async leak tracking)
|
||||||
|
- code coverage measured with `nyc`
|
||||||
|
- changelog generated with `standard-release`
|
||||||
|
|
||||||
|
### Intentionally Omitted Features
|
||||||
|
|
||||||
|
- everything frontend: React, CSS, etc
|
||||||
|
- heavy backend libraries: ORMs, etc
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Rollup Template](#rollup-template)
|
||||||
|
- [Features](#features)
|
||||||
|
- [Intentionally Omitted Features](#intentionally-omitted-features)
|
||||||
|
- [Contents](#contents)
|
||||||
|
- [Status](#status)
|
||||||
|
- [Releases](#releases)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [To Setup](#to-setup)
|
||||||
|
- [To Build](#to-build)
|
||||||
|
- [To Release](#to-release)
|
||||||
|
- [External Services](#external-services)
|
||||||
|
- [Maintenance Bots](#maintenance-bots)
|
||||||
|
- [External Secrets](#external-secrets)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
[![Pipeline status](https://img.shields.io/gitlab/pipeline/ssube/rollup-template.svg?gitlab_url=https%3A%2F%2Fgit.apextoaster.com&logo=gitlab)](https://git.apextoaster.com/ssube/rollup-template/commits/master)
|
||||||
|
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=ssube_rollup-template&metric=ncloc)](https://sonarcloud.io/dashboard?id=ssube_rollup-template)
|
||||||
|
[![Test coverage](https://codecov.io/gh/ssube/rollup-template/branch/master/graph/badge.svg)](https://codecov.io/gh/ssube/rollup-template)
|
||||||
|
[![MIT license](https://img.shields.io/github/license/ssube/rollup-template.svg)](https://github.com/ssube/rollup-template/blob/master/LICENSE.md)
|
||||||
|
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fssube%2Frollup-template.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fssube%2Frollup-template?ref=badge_shield)
|
||||||
|
|
||||||
|
[![Open bug count](https://img.shields.io/github/issues-raw/ssube/rollup-template/type-bug.svg)](https://github.com/ssube/rollup-template/issues?q=is%3Aopen+is%3Aissue+label%3Atype%2Fbug)
|
||||||
|
[![Open issue count](https://img.shields.io/github/issues-raw/ssube/rollup-template.svg)](https://github.com/ssube/rollup-template/issues?q=is%3Aopen+is%3Aissue)
|
||||||
|
[![Closed issue count](https://img.shields.io/github/issues-closed-raw/ssube/rollup-template.svg)](https://github.com/ssube/rollup-template/issues?q=is%3Aissue+is%3Aclosed)
|
||||||
|
|
||||||
|
[![Renovate badge](https://badges.renovateapi.com/github/ssube/rollup-template)](https://renovatebot.com)
|
||||||
|
[![Dependency status](https://img.shields.io/david/ssube/rollup-template.svg)](https://david-dm.org/ssube/rollup-template)
|
||||||
|
[![Dev dependency status](https://img.shields.io/david/dev/ssube/rollup-template.svg)](https://david-dm.org/ssube/rollup-template?type=dev)
|
||||||
|
[![Known vulnerabilities](https://snyk.io/test/github/ssube/rollup-template/badge.svg)](https://snyk.io/test/github/ssube/rollup-template)
|
||||||
|
|
||||||
|
[![Maintainability score](https://api.codeclimate.com/v1/badges/0ca333e0379bda050d84/maintainability)](https://codeclimate.com/github/ssube/rollup-template/maintainability)
|
||||||
|
[![Technical debt ratio](https://img.shields.io/codeclimate/tech-debt/ssube/rollup-template.svg)](https://codeclimate.com/github/ssube/rollup-template/trends/technical_debt)
|
||||||
|
[![Quality issues](https://img.shields.io/codeclimate/issues/ssube/rollup-template.svg)](https://codeclimate.com/github/ssube/rollup-template/issues)
|
||||||
|
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/ssube/rollup-template.svg?logo=lgtm)](https://lgtm.com/projects/g/ssube/rollup-template/context:javascript)
|
||||||
|
[![Total alerts](https://img.shields.io/lgtm/alerts/g/ssube/rollup-template.svg)](https://lgtm.com/projects/g/ssube/rollup-template/alerts/)
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
[![github release link](https://img.shields.io/badge/github-release-blue?logo=github)](https://github.com/ssube/rollup-template/releases)
|
||||||
|
[![github release version](https://img.shields.io/github/tag/ssube/rollup-template.svg)](https://github.com/ssube/rollup-template/releases)
|
||||||
|
[![github commits since release](https://img.shields.io/github/commits-since/ssube/rollup-template/v0.2.4.svg)](https://github.com/ssube/rollup-template/compare/v0.2.4...master)
|
||||||
|
|
||||||
|
[![npm package link](https://img.shields.io/badge/npm-package-blue?logo=npm)](https://www.npmjs.com/package/@apextoaster/rollup-template)
|
||||||
|
[![npm release version](https://img.shields.io/npm/v/@apextoaster/rollup-template.svg)](https://www.npmjs.com/package/@apextoaster/rollup-template)
|
||||||
|
[![Typescript definitions](https://img.shields.io/npm/types/@apextoaster/rollup-template.svg)](https://www.npmjs.com/package/@apextoaster/rollup-template)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### To Setup
|
||||||
|
|
||||||
|
To create a new repository from this template:
|
||||||
|
|
||||||
|
- create your new repo on Github & Gitlab (your server or Gitlab.com)
|
||||||
|
- `git clone git@github.com:ssube/rollup-template.git your-project`
|
||||||
|
- `cd your-project`
|
||||||
|
- `git remote add github git@github.com:yourname/your-project.git`
|
||||||
|
- `git remote add gitlab git@gitlab.com:yourname/your-project.git`
|
||||||
|
- set up repository mirroring in Gitlab
|
||||||
|
- set up [some maintenance bots](#maintenance-bots)
|
||||||
|
- `make git-push`
|
||||||
|
- install your dependencies
|
||||||
|
- write some code
|
||||||
|
|
||||||
|
### To Build
|
||||||
|
|
||||||
|
Once your project is set up:
|
||||||
|
|
||||||
|
- `make` to bundle and test
|
||||||
|
- commit
|
||||||
|
- `make git-push`
|
||||||
|
|
||||||
|
The `git-push` target pushes to Github first, to avoid conflicts with changes
|
||||||
|
from bots and other contributors.
|
||||||
|
|
||||||
|
### To Release
|
||||||
|
|
||||||
|
When your project is ready to release:
|
||||||
|
|
||||||
|
- `make release-dry` to make sure your changelog and options look right
|
||||||
|
- `make release`
|
||||||
|
|
||||||
|
Additional options can be passed with the `RELEASE_OPTS` variable. Frequently
|
||||||
|
used options include `--release-as minor` and `--prerelease`.
|
||||||
|
|
||||||
|
## External Services
|
||||||
|
|
||||||
|
This template works with or expects a few external services, namely a Gitlab
|
||||||
|
CI server (self-hosted or using Gitlab.com).
|
||||||
|
|
||||||
|
### Maintenance Bots
|
||||||
|
|
||||||
|
Good tests and clever bots can eliminate the most painful parts of project
|
||||||
|
maintenance. This repository is configured to work with:
|
||||||
|
|
||||||
|
- [CodeCov](https://codecov.io/)
|
||||||
|
- [Code Climate](https://codeclimate.com/)
|
||||||
|
- [LGTM](https://lgtm.com/)
|
||||||
|
- [Renovate](https://renovatebot.com/)
|
||||||
|
- [Snyk](https://snyk.io/)
|
||||||
|
- [SonarCloud](https://sonarcloud.io/)
|
||||||
|
|
||||||
|
None of these are required, but Renovate and Snyk can be very helpful when
|
||||||
|
dependencies release a security patch.
|
||||||
|
|
||||||
|
## External Secrets
|
||||||
|
|
||||||
|
This template expects a few secrets to exist in the environment, including
|
||||||
|
tokens for the [external services](#external-services).
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------------------- | ----------------------------------------------------- |
|
||||||
|
| CC_TEST_REPORTER_ID | code climate ID |
|
||||||
|
| CODECLIMATE_SECRET | code climate token |
|
||||||
|
| CODECOV_SECRET | codecov token |
|
||||||
|
| DOCKER_SECRET | docker config, required for publishing images |
|
||||||
|
| GITHUB_SECRET | github.com token, required for publishing status |
|
||||||
|
| NPM_SECRET | npmjs.com token, required for publishing npm packages |
|
||||||
|
| SONAR_SECRET | sonarcloud token |
|
||||||
|
|
||||||
|
Secrets should be provided as environment variables, with the secret value
|
||||||
|
`base64`-encoded.
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||||
|
"projectFolder": "..",
|
||||||
|
"mainEntryPointFilePath": "<projectFolder>/out/src/index.d.ts",
|
||||||
|
"apiReport": {
|
||||||
|
"enabled": true,
|
||||||
|
"reportFolder": "<projectFolder>/out/",
|
||||||
|
"reportTempFolder": "<projectFolder>/out/tmp/"
|
||||||
|
},
|
||||||
|
"docModel": {
|
||||||
|
"enabled": true,
|
||||||
|
"apiJsonFilePath": "<projectFolder>/out/api/<unscopedPackageName>.api.json"
|
||||||
|
},
|
||||||
|
|
||||||
|
"dtsRollup": {
|
||||||
|
"enabled": true,
|
||||||
|
"untrimmedFilePath": "<projectFolder>/out/index.d.ts",
|
||||||
|
"betaTrimmedFilePath": "<projectFolder>/out/index-beta.d.ts",
|
||||||
|
"publicTrimmedFilePath": "<projectFolder>/out/index-public.d.ts"
|
||||||
|
},
|
||||||
|
|
||||||
|
"tsdocMetadata": {
|
||||||
|
},
|
||||||
|
|
||||||
|
"messages": {
|
||||||
|
"compilerMessageReporting": {
|
||||||
|
"default": {
|
||||||
|
"logLevel": "warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"extractorMessageReporting": {
|
||||||
|
"default": {
|
||||||
|
"logLevel": "warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tsdocMessageReporting": {
|
||||||
|
"default": {
|
||||||
|
"logLevel": "warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,389 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "tsconfig.json",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"eslint-plugin-chai",
|
||||||
|
"eslint-plugin-chai-expect",
|
||||||
|
"eslint-plugin-chai-expect-keywords",
|
||||||
|
"eslint-plugin-import",
|
||||||
|
"eslint-plugin-mocha",
|
||||||
|
"eslint-plugin-no-null",
|
||||||
|
"eslint-plugin-sonarjs",
|
||||||
|
"@typescript-eslint",
|
||||||
|
"@typescript-eslint/tslint"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||||
|
"@typescript-eslint/array-type": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"default": "generic"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/await-thenable": "error",
|
||||||
|
"@typescript-eslint/ban-types": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"types": {
|
||||||
|
"null": "Use 'undefined' instead of 'null'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/class-name-casing": "error",
|
||||||
|
"@typescript-eslint/consistent-type-assertions": "error",
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "error",
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"accessibility": "explicit",
|
||||||
|
"overrides": {
|
||||||
|
"constructors": "no-public"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/indent": [
|
||||||
|
"error",
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
"ObjectExpression": "first",
|
||||||
|
"FunctionDeclaration": {
|
||||||
|
"parameters": "first"
|
||||||
|
},
|
||||||
|
"FunctionExpression": {
|
||||||
|
"parameters": "first"
|
||||||
|
},
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/interface-name-prefix": "error",
|
||||||
|
"@typescript-eslint/member-delimiter-style": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"delimiter": "semi",
|
||||||
|
"requireLast": true
|
||||||
|
},
|
||||||
|
"singleline": {
|
||||||
|
"delimiter": "semi",
|
||||||
|
"requireLast": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/member-ordering": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"default": [
|
||||||
|
"public-static-method",
|
||||||
|
"public-static-field",
|
||||||
|
"public-instance-field",
|
||||||
|
"protected-instance-field",
|
||||||
|
"public-constructor",
|
||||||
|
"public-instance-method",
|
||||||
|
"protected-instance-method"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-empty-function": "error",
|
||||||
|
"@typescript-eslint/no-empty-interface": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
|
"@typescript-eslint/no-for-in-array": "error",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "error",
|
||||||
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
|
"@typescript-eslint/no-namespace": "error",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "error",
|
||||||
|
"no-param-reassign": "error",
|
||||||
|
"@typescript-eslint/no-parameter-properties": "error",
|
||||||
|
"@typescript-eslint/no-this-alias": "error",
|
||||||
|
"@typescript-eslint/no-unnecessary-type-arguments": "error",
|
||||||
|
"@typescript-eslint/no-use-before-declare": "off",
|
||||||
|
"@typescript-eslint/no-var-requires": "error",
|
||||||
|
"@typescript-eslint/prefer-for-of": "error",
|
||||||
|
"@typescript-eslint/prefer-function-type": "error",
|
||||||
|
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||||
|
"@typescript-eslint/quotes": [
|
||||||
|
"error",
|
||||||
|
"single",
|
||||||
|
{
|
||||||
|
"avoidEscape": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/restrict-plus-operands": "error",
|
||||||
|
"@typescript-eslint/semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"space-in-parens": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": "error",
|
||||||
|
"@typescript-eslint/triple-slash-reference": "error",
|
||||||
|
"@typescript-eslint/type-annotation-spacing": "error",
|
||||||
|
"@typescript-eslint/unbound-method": "error",
|
||||||
|
"@typescript-eslint/unified-signatures": "error",
|
||||||
|
"arrow-body-style": "error",
|
||||||
|
"arrow-parens": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"camelcase": "error",
|
||||||
|
"complexity": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"max": 12
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constructor-super": "error",
|
||||||
|
"curly": "error",
|
||||||
|
"default-case": "error",
|
||||||
|
"dot-notation": "error",
|
||||||
|
"eol-last": "error",
|
||||||
|
"eqeqeq": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"guard-for-in": "error",
|
||||||
|
"id-blacklist": [
|
||||||
|
"error",
|
||||||
|
"any",
|
||||||
|
"Number",
|
||||||
|
"String",
|
||||||
|
"Boolean",
|
||||||
|
"Undefined"
|
||||||
|
],
|
||||||
|
"id-match": "error",
|
||||||
|
"import/no-default-export": "error",
|
||||||
|
"import/no-deprecated": "error",
|
||||||
|
"import/no-extraneous-dependencies": "off",
|
||||||
|
"import/no-internal-modules": "off",
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
[
|
||||||
|
"builtin",
|
||||||
|
"external"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"index",
|
||||||
|
"parent",
|
||||||
|
"sibling",
|
||||||
|
"unknown"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max-classes-per-file": [
|
||||||
|
"off",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"max-len": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"code": 180
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max-lines": [
|
||||||
|
"error",
|
||||||
|
500
|
||||||
|
],
|
||||||
|
"new-parens": "error",
|
||||||
|
"no-bitwise": "off",
|
||||||
|
"no-caller": "error",
|
||||||
|
"no-cond-assign": "error",
|
||||||
|
"no-console": "error",
|
||||||
|
"no-debugger": "error",
|
||||||
|
"no-duplicate-case": "error",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"no-empty": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-extra-bind": "error",
|
||||||
|
"no-fallthrough": "off",
|
||||||
|
"no-invalid-this": "error",
|
||||||
|
"no-irregular-whitespace": "error",
|
||||||
|
"no-magic-numbers": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
-3,
|
||||||
|
-2,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
20,
|
||||||
|
30,
|
||||||
|
40,
|
||||||
|
50,
|
||||||
|
60,
|
||||||
|
70,
|
||||||
|
80,
|
||||||
|
90,
|
||||||
|
100
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-multiple-empty-lines": "error",
|
||||||
|
"no-new-func": "error",
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
"no-null/no-null": "error",
|
||||||
|
"no-plusplus": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowForLoopAfterthoughts": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-redeclare": "error",
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
"ForInStatement"
|
||||||
|
],
|
||||||
|
"no-return-await": "error",
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-shadow": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"hoist": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-sparse-arrays": "error",
|
||||||
|
"no-template-curly-in-string": "error",
|
||||||
|
"no-throw-literal": "error",
|
||||||
|
"no-trailing-spaces": "error",
|
||||||
|
"no-undef-init": "error",
|
||||||
|
"no-underscore-dangle": "error",
|
||||||
|
"no-unsafe-finally": "error",
|
||||||
|
"no-unused-expressions": "error",
|
||||||
|
"no-unused-labels": "error",
|
||||||
|
"no-useless-constructor": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"no-void": "error",
|
||||||
|
"max-params": [
|
||||||
|
"error",
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"object-shorthand": "error",
|
||||||
|
"one-var": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"prefer-const": "error",
|
||||||
|
"prefer-object-spread": "error",
|
||||||
|
"@typescript-eslint/prefer-readonly": "error",
|
||||||
|
"quote-props": [
|
||||||
|
"error",
|
||||||
|
"consistent-as-needed"
|
||||||
|
],
|
||||||
|
"radix": "error",
|
||||||
|
"space-before-function-paren": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"anonymous": "never",
|
||||||
|
"asyncArrow": "always",
|
||||||
|
"named": "never"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spaced-comment": "error",
|
||||||
|
"use-isnan": "error",
|
||||||
|
"valid-typeof": "off",
|
||||||
|
"sonarjs/max-switch-cases": "error",
|
||||||
|
"sonarjs/cognitive-complexity": "error",
|
||||||
|
"sonarjs/no-all-duplicated-branches": "error",
|
||||||
|
"sonarjs/no-collapsible-if": "error",
|
||||||
|
"sonarjs/no-collection-size-mischeck": "error",
|
||||||
|
"sonarjs/no-duplicate-string": "error",
|
||||||
|
"sonarjs/no-duplicated-branches": "error",
|
||||||
|
"sonarjs/no-element-overwrite": "error",
|
||||||
|
"sonarjs/no-identical-conditions": "error",
|
||||||
|
"sonarjs/no-identical-expressions": "error",
|
||||||
|
"sonarjs/no-identical-functions": "error",
|
||||||
|
"sonarjs/no-inverted-boolean-check": "error",
|
||||||
|
"sonarjs/no-redundant-boolean": "error",
|
||||||
|
"sonarjs/no-redundant-jump": "error",
|
||||||
|
"sonarjs/no-same-line-conditional": "error",
|
||||||
|
"sonarjs/no-useless-catch": "error",
|
||||||
|
"sonarjs/prefer-immediate-return": "error",
|
||||||
|
"@typescript-eslint/tslint/config": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"ban": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"message": "use lodash isString",
|
||||||
|
"name": [
|
||||||
|
"util",
|
||||||
|
"isString"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "use lodash isNil",
|
||||||
|
"name": [
|
||||||
|
"util",
|
||||||
|
"isNullOrUndefined"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import-spacing": true,
|
||||||
|
"jsdoc-format": [
|
||||||
|
true,
|
||||||
|
"check-multiline-start"
|
||||||
|
],
|
||||||
|
"no-boolean-literal-compare": true,
|
||||||
|
"no-dynamic-delete": true,
|
||||||
|
"no-inferred-empty-object-type": true,
|
||||||
|
"no-reference-import": true,
|
||||||
|
"object-literal-sort-keys": true,
|
||||||
|
"one-line": [
|
||||||
|
true,
|
||||||
|
"check-catch",
|
||||||
|
"check-else",
|
||||||
|
"check-finally",
|
||||||
|
"check-open-brace",
|
||||||
|
"check-whitespace"
|
||||||
|
],
|
||||||
|
"prefer-switch": true,
|
||||||
|
"strict-type-predicates": true,
|
||||||
|
"trailing-comma": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"esSpecCompliant": true,
|
||||||
|
"multiline": {
|
||||||
|
"arrays": "always",
|
||||||
|
"functions": "never",
|
||||||
|
"object": "always"
|
||||||
|
},
|
||||||
|
"singleline": "never"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"whitespace": [
|
||||||
|
true,
|
||||||
|
"check-branch",
|
||||||
|
"check-decl",
|
||||||
|
"check-operator",
|
||||||
|
"check-separator",
|
||||||
|
"check-type",
|
||||||
|
"check-typecast",
|
||||||
|
"check-type-operator",
|
||||||
|
"check-rest-spread"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.deploy-branches:
|
||||||
|
except:
|
||||||
|
- tags
|
||||||
|
|
||||||
|
.deploy-tags:
|
||||||
|
only:
|
||||||
|
- tags
|
|
@ -0,0 +1,61 @@
|
||||||
|
.build-curl:
|
||||||
|
image: apextoaster/base:1.2
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
|
||||||
|
.build-climate:
|
||||||
|
image: apextoaster/code-climate:0.6
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
allow_failure: false
|
||||||
|
variables:
|
||||||
|
CI_BRANCH: "${CI_COMMIT_REF_NAME}"
|
||||||
|
GIT_BRANCH: "${CI_COMMIT_REF_NAME}"
|
||||||
|
GIT_COMMIT_SHA: "${CI_COMMIT_SHA}"
|
||||||
|
|
||||||
|
.build-codecov:
|
||||||
|
image: apextoaster/codecov:3.1
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
allow_failure: false
|
||||||
|
|
||||||
|
.build-docker:
|
||||||
|
image: apextoaster/docker:18.09
|
||||||
|
services:
|
||||||
|
- apextoaster/docker-dind:18.09
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
allow_failure: false
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- mkdir ${HOME}/.docker
|
||||||
|
- echo "${DOCKER_SECRET}" | base64 -d > ${HOME}/.docker/config.json
|
||||||
|
script:
|
||||||
|
- ${CI_PROJECT_DIR}/scripts/docker-build.sh --push
|
||||||
|
after_script:
|
||||||
|
- rm -rfv ${HOME}/.docker
|
||||||
|
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_HOST: tcp://localhost:2375
|
||||||
|
|
||||||
|
.build-node:
|
||||||
|
image: apextoaster/node:10.1
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
allow_failure: false
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- echo "${NPM_SECRET}" | base64 -d > ${HOME}/.npmrc
|
||||||
|
|
||||||
|
.build-sonar:
|
||||||
|
image: apextoaster/sonar-scanner:3.3
|
||||||
|
tags:
|
||||||
|
- platform:k8s
|
||||||
|
- runner:shared
|
||||||
|
allow_failure: false
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"reporter": ["json"],
|
||||||
|
"ui": ["bdd"]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"async_hooks",
|
||||||
|
"chai",
|
||||||
|
"sinon"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"node_modules/chai/index.js": [
|
||||||
|
"expect",
|
||||||
|
"use"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"names": []
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
import { join, sep } from 'path';
|
||||||
|
import commonjs from 'rollup-plugin-commonjs';
|
||||||
|
import { eslint } from 'rollup-plugin-eslint';
|
||||||
|
import json from 'rollup-plugin-json';
|
||||||
|
import multiEntry from 'rollup-plugin-multi-entry';
|
||||||
|
import externals from 'rollup-plugin-node-externals';
|
||||||
|
import resolve from 'rollup-plugin-node-resolve';
|
||||||
|
import replace from 'rollup-plugin-replace';
|
||||||
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
|
import yaml from 'rollup-plugin-yaml';
|
||||||
|
|
||||||
|
const debug = process.env['DEBUG'] === 'TRUE';
|
||||||
|
const metadata = require('../package.json');
|
||||||
|
|
||||||
|
const external = require('./rollup-external.json').names;
|
||||||
|
const globals = require('./rollup-globals.json');
|
||||||
|
const namedExports = require('./rollup-named.json');
|
||||||
|
const stubNames = require('./rollup-stub.json').names;
|
||||||
|
|
||||||
|
const passStub = 'require("pass-stub")';
|
||||||
|
const stubs = stubNames.reduce((p, c) => (p[c] = passStub, p), {});
|
||||||
|
|
||||||
|
const rootPath = process.env['ROOT_PATH'];
|
||||||
|
const targetPath = process.env['TARGET_PATH'];
|
||||||
|
|
||||||
|
const bundle = {
|
||||||
|
external,
|
||||||
|
input: {
|
||||||
|
include: [
|
||||||
|
join(rootPath, 'src', 'index.ts'),
|
||||||
|
join(rootPath, 'test', 'harness.ts'),
|
||||||
|
join(rootPath, 'test', '**', 'Test*.ts'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
manualChunks(id) {
|
||||||
|
if (id.includes(`${sep}test${sep}`)) {
|
||||||
|
return 'test';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.match(/commonjs-external/i) || id.match(/commonjsHelpers/)) {
|
||||||
|
return 'vendor';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes(`${sep}node_modules${sep}`)) {
|
||||||
|
return 'vendor';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes(`${sep}src${sep}index`)) {
|
||||||
|
return 'index';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes(`${sep}src${sep}`)) {
|
||||||
|
return 'main';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
console.log('file belongs to no chunk', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'nochunk';
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
dir: targetPath,
|
||||||
|
chunkFileNames: '[name].js',
|
||||||
|
entryFileNames: 'entry-[name].js',
|
||||||
|
format: 'cjs',
|
||||||
|
globals,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
multiEntry(),
|
||||||
|
json(),
|
||||||
|
yaml(),
|
||||||
|
externals({
|
||||||
|
builtins: true,
|
||||||
|
deps: true,
|
||||||
|
devDeps: false,
|
||||||
|
peerDeps: false,
|
||||||
|
}),
|
||||||
|
replace({
|
||||||
|
delimiters: ['require("', '")'],
|
||||||
|
values: stubs,
|
||||||
|
}),
|
||||||
|
replace({
|
||||||
|
delimiters: ['require(\'', '\')'],
|
||||||
|
values: stubs,
|
||||||
|
}),
|
||||||
|
replace({
|
||||||
|
delimiters: ['{{ ', ' }}'],
|
||||||
|
values: {
|
||||||
|
BUILD_JOB: process.env['CI_JOB_ID'],
|
||||||
|
BUILD_RUNNER: process.env['CI_RUNNER_DESCRIPTION'],
|
||||||
|
GIT_BRANCH: process.env['CI_COMMIT_REF_SLUG'],
|
||||||
|
GIT_COMMIT: process.env['CI_COMMIT_SHA'],
|
||||||
|
NODE_VERSION: process.env['NODE_VERSION'],
|
||||||
|
PACKAGE_NAME: metadata.name,
|
||||||
|
PACKAGE_VERSION: metadata.version,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
resolve({
|
||||||
|
preferBuiltins: true,
|
||||||
|
}),
|
||||||
|
commonjs({
|
||||||
|
namedExports,
|
||||||
|
}),
|
||||||
|
eslint({
|
||||||
|
configFile: join('.', 'config', 'eslint.json'),
|
||||||
|
exclude: [
|
||||||
|
join('node_modules', '**'),
|
||||||
|
join('src', 'resource'),
|
||||||
|
join('src', '**', '*.json'),
|
||||||
|
join('src', '**', '*.yml'),
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
join('**', '*.ts'),
|
||||||
|
],
|
||||||
|
throwOnError: true,
|
||||||
|
}),
|
||||||
|
typescript({
|
||||||
|
cacheRoot: join(targetPath, 'cache', 'rts2'),
|
||||||
|
rollupCommonJSResolveHack: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default [
|
||||||
|
bundle,
|
||||||
|
];
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es2017"
|
||||||
|
],
|
||||||
|
"module": "es6",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"outDir": "../out",
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"types": [
|
||||||
|
"chai-as-promised",
|
||||||
|
"mocha",
|
||||||
|
"node",
|
||||||
|
"rollup-resources",
|
||||||
|
"sinon-chai"
|
||||||
|
],
|
||||||
|
"typeRoots": [
|
||||||
|
"../node_modules/@types",
|
||||||
|
"../node_modules",
|
||||||
|
"../vendor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"../node_modules"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"../src/**/*",
|
||||||
|
"../test/**/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@istanbuljs/nyc-config-typescript": "^0.1.3",
|
||||||
|
"@microsoft/api-documenter": "^7.5.8",
|
||||||
|
"@microsoft/api-extractor": "^7.5.6",
|
||||||
|
"@types/chai": "^4.2.5",
|
||||||
|
"@types/chai-as-promised": "^7.1.2",
|
||||||
|
"@types/mocha": "^5.2.7",
|
||||||
|
"@types/sinon-chai": "^3.2.3",
|
||||||
|
"@types/source-map-support": "^0.5.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.7.0",
|
||||||
|
"@typescript-eslint/eslint-plugin-tslint": "^2.7.0",
|
||||||
|
"@typescript-eslint/parser": "^2.7.0",
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"chai-as-promised": "^7.1.1",
|
||||||
|
"eslint-plugin-chai": "^0.0.1",
|
||||||
|
"eslint-plugin-chai-expect": "^2.0.1",
|
||||||
|
"eslint-plugin-chai-expect-keywords": "^1.0.0",
|
||||||
|
"eslint-plugin-import": "^2.18.2",
|
||||||
|
"eslint-plugin-mocha": "^6.2.1",
|
||||||
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
|
"eslint-plugin-sonarjs": "^0.5.0",
|
||||||
|
"mocha": "^6.2.2",
|
||||||
|
"nyc": "^14.1.1",
|
||||||
|
"rollup": "^1.27.0",
|
||||||
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
|
"rollup-plugin-eslint": "^7.0.0",
|
||||||
|
"rollup-plugin-json": "^4.0.0",
|
||||||
|
"rollup-plugin-multi-entry": "^2.1.0",
|
||||||
|
"rollup-plugin-node-externals": "^2.1.2",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"rollup-plugin-replace": "^2.2.0",
|
||||||
|
"rollup-plugin-tslint": "^0.2.2",
|
||||||
|
"rollup-plugin-typescript2": "^0.25.2",
|
||||||
|
"rollup-plugin-yaml": "^2.0.0",
|
||||||
|
"sinon": "^7.5.0",
|
||||||
|
"sinon-chai": "^3.3.0",
|
||||||
|
"source-map-support": "^0.5.16",
|
||||||
|
"standard-version": "^7.0.0",
|
||||||
|
"tslint": "^5.20.1",
|
||||||
|
"tslint-clean-code": "^0.2.10",
|
||||||
|
"tslint-consistent-codestyle": "^1.16.0",
|
||||||
|
"tslint-etc": "^1.9.2",
|
||||||
|
"tslint-microsoft-contrib": "^6.2.0",
|
||||||
|
"tslint-sonarts": "^1.9.0",
|
||||||
|
"typescript": "^3.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"js-yaml": "^3.13.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"config:base",
|
||||||
|
":semanticCommitTypeAll(update)",
|
||||||
|
":semanticCommitScopeDisabled"
|
||||||
|
],
|
||||||
|
"branchPrefix": "update/"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
PROJECT="${ROOT_PATH}"
|
||||||
|
PARENT="$(dirname "${PROJECT}")"
|
||||||
|
PROJECT_PATH="$(basename "${PARENT}")/$(basename "${PROJECT}")"
|
||||||
|
|
||||||
|
echo "${PROJECT_PATH}"
|
|
@ -0,0 +1,27 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
IMAGE_PUSH="${1:---skip}"
|
||||||
|
IMAGE_DEFAULT="${2:---skip}"
|
||||||
|
|
||||||
|
IMAGE_NAME="${CI_PROJECT_PATH}"
|
||||||
|
IMAGE_TAG="$(echo "${CI_COMMIT_TAG:-${CI_COMMIT_REF_SLUG}}" | sed -r 's/[^-_a-zA-Z0-9\\.]/-/g')"
|
||||||
|
|
||||||
|
IMAGE_SHORT="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||||
|
IMAGE_FULL="${IMAGE_NAME}:${IMAGE_TAG}-${IMAGE_ARCH}"
|
||||||
|
|
||||||
|
echo "Building image: ${IMAGE_FULL}"
|
||||||
|
|
||||||
|
docker build -f "Dockerfile.${IMAGE_ARCH}" -t "${IMAGE_FULL}" .
|
||||||
|
|
||||||
|
if [[ "${IMAGE_PUSH}" == "--push" ]];
|
||||||
|
then
|
||||||
|
echo "Pushing image: ${IMAGE_FULL}"
|
||||||
|
docker push "${IMAGE_FULL}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${IMAGE_DEFAULT}" == "--default" ]];
|
||||||
|
then
|
||||||
|
echo "Pushing image (default architecture): ${IMAGE_SHORT}"
|
||||||
|
docker tag "${IMAGE_FULL}" "${IMAGE_SHORT}"
|
||||||
|
docker push "${IMAGE_SHORT}"
|
||||||
|
fi
|
|
@ -0,0 +1,19 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
STATUS="${1}"
|
||||||
|
CI_COMMIT_SHA="${CI_COMMIT_SHA:-$(git rev-parse HEAD)}"
|
||||||
|
|
||||||
|
STATUS_BODY="$(cat <<EOF
|
||||||
|
{
|
||||||
|
"state": "${STATUS}",
|
||||||
|
"target_url": "${CI_PIPELINE_URL}",
|
||||||
|
"description": "CI pipeline ${STATUS}!",
|
||||||
|
"context": "gitlab/build"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf "Reporting status for %s...\n%s" "${CI_COMMIT_SHA}" "${STATUS_BODY}"
|
||||||
|
printf "%s" "${STATUS_BODY}" | curl -d @- \
|
||||||
|
-H "Authorization: token $(printf "%s" "${GITHUB_SECRET}" | base64 -d)" \
|
||||||
|
-i "https://api.github.com/repos/${CI_PROJECT_PATH}/statuses/${CI_COMMIT_SHA}"
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { main } from './app';
|
||||||
|
|
||||||
|
const STATUS_ERROR = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main entry-point to the program and the only file not included in the main bundle.
|
||||||
|
*/
|
||||||
|
main(process.argv).then((status) => process.exit(status)).catch((err: Error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('uncaught error during main:', err);
|
||||||
|
process.exit(STATUS_ERROR);
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { VERSION_INFO } from './version';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
VERSION_INFO,
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"foo": {}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
foo: {}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { DEFAULT_SAFE_SCHEMA, Schema } from 'js-yaml';
|
||||||
|
|
||||||
|
import { envType } from './type/Env';
|
||||||
|
import { includeType } from './type/Include';
|
||||||
|
import { regexpType } from './type/Regexp';
|
||||||
|
import { streamType } from './type/Stream';
|
||||||
|
|
||||||
|
export const CONFIG_ENV = 'SALTY_HOME';
|
||||||
|
export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [
|
||||||
|
envType,
|
||||||
|
includeType,
|
||||||
|
regexpType,
|
||||||
|
streamType,
|
||||||
|
]);
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Type as YamlType } from 'js-yaml';
|
||||||
|
|
||||||
|
import { NotFoundError } from '../../error/NotFoundError';
|
||||||
|
|
||||||
|
export const envType = new YamlType('!env', {
|
||||||
|
kind: 'scalar',
|
||||||
|
resolve(name: string) {
|
||||||
|
if (Reflect.has(process.env, name)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new NotFoundError(`environment variable not found: ${name}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
construct(name: string) {
|
||||||
|
return Reflect.get(process.env, name);
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { existsSync, readFileSync, realpathSync } from 'fs';
|
||||||
|
import { SAFE_SCHEMA, safeLoad, Type as YamlType } from 'js-yaml';
|
||||||
|
import { BaseError } from 'noicejs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { NotFoundError } from '../../error/NotFoundError';
|
||||||
|
|
||||||
|
// work around the circular dependency by setting the schema later
|
||||||
|
export const includeSchema = {
|
||||||
|
schema: SAFE_SCHEMA,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const includeType = new YamlType('!include', {
|
||||||
|
kind: 'scalar',
|
||||||
|
resolve(path: string) {
|
||||||
|
try {
|
||||||
|
const canonical = resolvePath(path);
|
||||||
|
// throws in node 11+
|
||||||
|
if (existsSync(canonical)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new NotFoundError('included file does not exist');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new NotFoundError('included file does not exist', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
construct(path: string): unknown {
|
||||||
|
try {
|
||||||
|
return safeLoad(readFileSync(resolvePath(path), {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
}), {
|
||||||
|
schema: includeSchema.schema,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
throw new BaseError('error including file', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function resolvePath(path: string): string {
|
||||||
|
if (path[0] === '.') {
|
||||||
|
return realpathSync(join(__dirname, path));
|
||||||
|
} else {
|
||||||
|
return realpathSync(path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Type as YamlType } from 'js-yaml';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
|
||||||
|
import { InvalidArgumentError } from '../../error/InvalidArgumentError';
|
||||||
|
|
||||||
|
export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/;
|
||||||
|
|
||||||
|
export const regexpType = new YamlType('!regexp', {
|
||||||
|
kind: 'scalar',
|
||||||
|
resolve(value: string) {
|
||||||
|
return REGEXP_REGEXP.test(value);
|
||||||
|
},
|
||||||
|
construct(value: string): RegExp {
|
||||||
|
const match = REGEXP_REGEXP.exec(value);
|
||||||
|
if (isNil(match)) {
|
||||||
|
throw new InvalidArgumentError('invalid regexp');
|
||||||
|
}
|
||||||
|
const [/* input */, expr, flags] = Array.from(match);
|
||||||
|
return new RegExp(expr, flags);
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Type as YamlType } from 'js-yaml';
|
||||||
|
|
||||||
|
import { NotFoundError } from '../../error/NotFoundError';
|
||||||
|
|
||||||
|
const ALLOWED_STREAMS = new Set([
|
||||||
|
'stdout',
|
||||||
|
'stderr',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const streamType = new YamlType('!stream', {
|
||||||
|
kind: 'scalar',
|
||||||
|
resolve(name: string) {
|
||||||
|
if (ALLOWED_STREAMS.has(name) && Reflect.has(process, name)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new NotFoundError(`process stream not found: ${name}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
construct(name: string) {
|
||||||
|
return Reflect.get(process, name);
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
export const VERSION_INFO = {
|
||||||
|
build: {
|
||||||
|
job: '{{ BUILD_JOB }}',
|
||||||
|
node: '{{ NODE_VERSION }}',
|
||||||
|
runner: '{{ BUILD_RUNNER }}',
|
||||||
|
},
|
||||||
|
git: {
|
||||||
|
branch: '{{ GIT_BRANCH }}',
|
||||||
|
commit: '{{ GIT_COMMIT }}',
|
||||||
|
},
|
||||||
|
package: {
|
||||||
|
name: '{{ PACKAGE_NAME }}',
|
||||||
|
version: '{{ PACKAGE_VERSION }}',
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import chai from 'chai';
|
||||||
|
import chaiAsPromised from 'chai-as-promised';
|
||||||
|
import sinonChai from 'sinon-chai';
|
||||||
|
import sourceMapSupport from 'source-map-support';
|
||||||
|
|
||||||
|
sourceMapSupport.install({
|
||||||
|
environment: 'node',
|
||||||
|
handleUncaughtExceptions: true,
|
||||||
|
hookRequire: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will break the whole test run if any test leaks an unhandled rejection.
|
||||||
|
*/
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('unhandled error during tests', reason);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
chai.use(chaiAsPromised);
|
||||||
|
chai.use(sinonChai);
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { AsyncHook, createHook } from 'async_hooks';
|
||||||
|
|
||||||
|
// this will pull Mocha internals out of the stacks
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
||||||
|
const { stackTraceFilter } = require('mocha/lib/utils');
|
||||||
|
const filterStack = stackTraceFilter();
|
||||||
|
|
||||||
|
type AsyncMochaTest = (this: Mocha.Context | void) => Promise<void>;
|
||||||
|
type AsyncMochaSuite = (this: Mocha.Suite) => Promise<void>;
|
||||||
|
|
||||||
|
function isNil<T>(val: T | null | undefined): val is null | undefined {
|
||||||
|
/* eslint-disable-next-line no-null/no-null */
|
||||||
|
return val === null || val === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrackedResource {
|
||||||
|
source: string;
|
||||||
|
triggerAsyncId: number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugMode() {
|
||||||
|
return Reflect.has(process.env, 'DEBUG');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async resource tracker using node's internal hooks.
|
||||||
|
*
|
||||||
|
* This probably won't work in a browser. It does not hold references to the resource, to avoid leaks.
|
||||||
|
* Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843#file-async-dump-js
|
||||||
|
*/
|
||||||
|
export class Tracker {
|
||||||
|
public static getStack(): string {
|
||||||
|
const err = new Error();
|
||||||
|
if (isNil(err.stack)) {
|
||||||
|
return 'no stack trace available';
|
||||||
|
} else {
|
||||||
|
return filterStack(err.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly hook: AsyncHook;
|
||||||
|
private readonly resources: Map<number, TrackedResource>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.resources = new Map();
|
||||||
|
this.hook = createHook({
|
||||||
|
destroy: (id: number) => {
|
||||||
|
this.resources.delete(id);
|
||||||
|
},
|
||||||
|
init: (id: number, type: string, triggerAsyncId: number) => {
|
||||||
|
const source = Tracker.getStack();
|
||||||
|
// @TODO: exclude async hooks, including this one
|
||||||
|
this.resources.set(id, {
|
||||||
|
source,
|
||||||
|
triggerAsyncId,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
promiseResolve: (id: number) => {
|
||||||
|
this.resources.delete(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.resources.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public disable() {
|
||||||
|
this.hook.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-console, no-invalid-this */
|
||||||
|
public dump() {
|
||||||
|
console.error(`tracking ${this.resources.size} async resources`);
|
||||||
|
this.resources.forEach((res, id) => {
|
||||||
|
console.error(`${id}: ${res.type}`);
|
||||||
|
if (debugMode()) {
|
||||||
|
console.error(res.source);
|
||||||
|
console.error('\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public enable() {
|
||||||
|
this.hook.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get size(): number {
|
||||||
|
return this.resources.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe a suite of async tests. This wraps mocha's describe to track async resources and report leaks.
|
||||||
|
*/
|
||||||
|
export function describeLeaks(description: string, cb: AsyncMochaSuite): Mocha.Suite {
|
||||||
|
return describe(description, function trackSuite(this: Mocha.Suite) {
|
||||||
|
const tracker = new Tracker();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tracker.enable();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
tracker.disable();
|
||||||
|
const leaked = tracker.size;
|
||||||
|
|
||||||
|
// @TODO: this should only exclude the single Immediate set by the Tracker
|
||||||
|
if (leaked > 1) {
|
||||||
|
tracker.dump();
|
||||||
|
const msg = `test leaked ${leaked - 1} async resources`;
|
||||||
|
if (debugMode()) {
|
||||||
|
throw new Error(msg);
|
||||||
|
} else {
|
||||||
|
/* eslint-disable-next-line no-console */
|
||||||
|
console.warn(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const suite: PromiseLike<void> | undefined = cb.call(this);
|
||||||
|
if (isNil(suite) || !Reflect.has(suite, 'then')) {
|
||||||
|
/* eslint-disable-next-line no-console */
|
||||||
|
console.error(`test suite '${description}' did not return a promise`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return suite;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run an asynchronous test with unhandled rejection guards.
|
||||||
|
*
|
||||||
|
* This function may not have any direct test coverage. It is too simple to reasonably mock.
|
||||||
|
*/
|
||||||
|
export function itLeaks(expectation: string, cb?: AsyncMochaTest): Mocha.Test {
|
||||||
|
if (isNil(cb)) {
|
||||||
|
return it(expectation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return it(expectation, function trackTest(this: Mocha.Context) {
|
||||||
|
return new Promise<unknown>((res, rej) => {
|
||||||
|
cb.call(this).then((value: unknown) => {
|
||||||
|
res(value);
|
||||||
|
}, (err: Error) => {
|
||||||
|
rej(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./config/tsconfig.json"
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
declare module "*.json" {
|
||||||
|
export const data: unknown;
|
||||||
|
export default data;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.yml" {
|
||||||
|
export const data: unknown;
|
||||||
|
export default data;
|
||||||
|
}
|
Loading…
Reference in New Issue