Last active
February 19, 2024 05:42
-
-
Save SHaTRO/a2396deb3424478b6e805549e3de37da to your computer and use it in GitHub Desktop.
Setting up TS (fp-ts, io-ts) Project with NodeJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
## 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." | |
- added workaround optional dependency for fsevents so ci actions work correctly
- added unused imports plugin for eslint
- update eslint rules
- add SonarQube (properties) setup
- 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
For the latest revision: