Skip to content

Instantly share code, notes, and snippets.

@SHaTRO
Last active February 19, 2024 05:42
Show Gist options
  • Save SHaTRO/a2396deb3424478b6e805549e3de37da to your computer and use it in GitHub Desktop.
Save SHaTRO/a2396deb3424478b6e805549e3de37da to your computer and use it in GitHub Desktop.
Setting up TS (fp-ts, io-ts) Project with NodeJS
##
## Script to setup a Node TS project with Jest, ESLint, and fp-ts/io-ts
##
## If you want to skip SonarQube support do:
## SKIP_SONAR=true node-typescript-fp-setup.sh
##
# tested with npm 10.2.3, Node v20.10.0; 2023-12-02
testForJQ() {
if ! command -v jq &> /dev/null
then
echo "command 'jq' could not be found and is required"
exit 1
fi
JQ_VERSION=$(jq --version | cut -d '-' -f 2)
echo "Using jq version: ${JQ_VERSION}"
}
initPackageNPM() {
echo "Initializing NPM project..."
npm init
ENGINES_APPEND_FILE="/tmp/engines-$$.json" && \
(cat <<EOT >> "${ENGINES_APPEND_FILE}"
{
"engines": {
"npm": ">=10.2.4",
"node": ">=20.11.1"
}
}
EOT
) && \
jq -s add package.json "${ENGINES_APPEND_FILE}" > package.json-new && \
mv package.json-new package.json && \
PROJECT_NAME=$(jq -r '.name' package.json) && \
echo "NPM Project '${PROJECT_NAME}' initialized."
}
initProjectNPM() {
if [ -f "package.json" ]; then
echo "NPM project already initialized."
INIT_PROJECT=exists
else
initPackageNPM
INIT_PROJECT=initialized
fi
}
checkEngine() {
echo "Checking engine versions..."
npx check-engine || exit 1
CHECK_ENGINE=true
echo "Engine check is good."
}
installDevDeps() {
echo "Installing dev dependencies..." \
&& \
npm install --save-dev \
@tsconfig/node20 @types/node typescript ts-node jest @types/jest ts-jest \
@typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-jest \
@typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest \
eslint-plugin-unused-imports \
&& \
echo "Dev dependencies installed." \
|| exit 1
}
installOptionalDeps() {
echo "Installing 'optional' dependencies..." \
&& \
npm install --save-optional fsevents@latest \
&& \
echo "Optional dependencies installed." \
|| exit 1
}
installMainDeps() {
echo "Installing main dependencies..." \
&& \
npm install --save tslib fp-ts io-ts io-ts-types monocle-ts newtype-ts \
&& \
echo "Main dependencies installed." \
|| exit 1
}
setupConfigTS() {
(cat <<EOT >> tsconfig.json
{
"extends": "@tsconfig/node20/tsconfig.json",
"\$schema": "https://json.schemastore.org/tsconfig",
"display": "Node 20",
"compilerOptions": {
"lib": ["es2022"],
"target": "es2022",
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
EOT
) \
&& \
echo "TSConfig setup." \
|| exit 1
}
setupConfigESLint() {
(cat <<EOT >> .eslintrc.js
module.exports = {
'env': {
'browser': true,
'es2021': true,
'jest': true,
'node': true
},
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 12,
'sourceType': 'module'
},
'plugins': [
'@typescript-eslint',
'unused-imports'
],
'rules': {
'indent': [
'error',
2
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
'@typescript-eslint/no-unused-vars': [
'warn',
{
'vars': 'all',
'varsIgnorePattern': '^_',
'args': 'after-used',
'argsIgnorePattern': '^_'
}
],
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
'vars': 'all',
'varsIgnorePattern': '^_',
'args': 'after-used',
'argsIgnorePattern': '^_'
}
]
}
};
EOT
) \
&& \
echo "ESLint configured." \
|| exit 1
}
setupConfigJest() {
JEST_APPEND_FILE="/tmp/jest-$$.json" && \
(cat <<EOT >> "${JEST_APPEND_FILE}"
{
"jest": {
"transform": {
"^.+\\\\.tsx?\$": "ts-jest"
},
"testPathIgnorePatterns": [
"/node_modules/",
"/dist/"
]
}
}
EOT
) && \
jq -s add package.json "${JEST_APPEND_FILE}" > package.json-new \
&& \
jq '.scripts |= . + { "test": "jest" }' package.json-new > package.json-new2 \
&& \
mv package.json-new2 package.json && \
echo "Jest configured." \
|| exit 1
}
setupSourceCode() {
echo "Setting up source code..."
mkdir src && \
(cat <<EOT >> "src/hello.ts"
export const hello = () => 'Hello, world!';
console.log(hello());
EOT
) && \
(cat <<EOT >> "src/hello.test.ts"
import { hello } from './hello';
describe('hello', () => {
it('should return "Hello, world!"', () => {
expect(hello()).toBe('Hello, world!');
});
});
EOT
)
}
testCompilation() {
echo "Testing compilation..."
npx tsc && echo "good."
}
testExecution() {
echo "Testing execution..."
node ./dist/src/hello.js && npm test && echo "good."
}
setupSonar() {
if [[ "${SKIP_SONAR}" = '' ]]; then
echo "Doing sonar setup..."
( \
npm install --save-dev sonar-project-properties && \
jq '.scripts |= . + { "pre-sonar": "update-sonar-properties -v --sp" }' package.json > package.json-new && \
mv package.json-new package.json && \
if [ ! -f "sonar-project.properties" ]; then
(cat <<EOT >> sonar-project.properties
# PROJECT KEY WILL BE REPLACED WITH "name" from package.json
sonar.projectKey=some-foo-project-key
## OPTIONAL
# --- optional properties ---
# defaults to project key
#sonar.projectName="Your Project Here"
# defaults to 'not provided'
sonar.projectVersion=1.0.0
# Path is relative to the sonar-project.properties file. Defaults to .
sonar.sources=./src
sonar.coverage.exclusions=**/*.test.ts,**/*.spec.ts
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8
EOT
) \
fi && \
npm run pre-sonar && \
echo "Done with sonar setup"
) \
|| \
echo "Failed to do sonar setup"
fi
}
## main
testForJQ && \
initProjectNPM && \
checkEngine && \
installDevDeps && \
installMainDeps && \
installOptionalDeps && \
setupConfigTS && \
setupConfigESLint && \
setupConfigJest && \
setupSourceCode && \
testCompilation && \
testExecution && \
setupSonar \
|| exit 1
echo "Done with project configuration."
@SHaTRO
Copy link
Author

SHaTRO commented Mar 17, 2022

For the latest revision:

  • updated to latest npm and node 16 and es2021
  • added engine check for lowest tested versions
  • added package check for 'jq'

@SHaTRO
Copy link
Author

SHaTRO commented Jul 3, 2022

  • added workaround optional dependency for fsevents so ci actions work correctly
  • added unused imports plugin for eslint
  • update eslint rules

@SHaTRO
Copy link
Author

SHaTRO commented Jul 6, 2022

  • add SonarQube (properties) setup

@SHaTRO
Copy link
Author

SHaTRO commented Feb 19, 2024

  • updated to latest npm and node 20
  • turned into functions
  • added eslint
  • more dynamics

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment