Merge pull request #7 from IT4Change/lint-locales

feat(frontend): lint locales
This commit is contained in:
Ulf Gebhardt 2023-11-21 00:22:54 +01:00 committed by GitHub
commit 5cc5db223c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 111 additions and 47 deletions

View File

@ -13,8 +13,7 @@
"plugin:promise/recommended", "plugin:promise/recommended",
"plugin:security/recommended", "plugin:security/recommended",
"plugin:vue/vue3-recommended", "plugin:vue/vue3-recommended",
"plugin:storybook/recommended", "plugin:storybook/recommended"
"plugin:prettier/recommended"
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": "latest", "ecmaVersion": "latest",
@ -23,7 +22,6 @@
}, },
"plugins": [ "plugins": [
"@typescript-eslint", "@typescript-eslint",
"prettier",
"import", "import",
"promise", "promise",
"security", "security",
@ -40,7 +38,6 @@
"no-console": "error", "no-console": "error",
"no-debugger": "error", "no-debugger": "error",
"camelcase": "error", "camelcase": "error",
"prettier/prettier": "error",
"indent": ["error", 2], "indent": ["error", 2],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],
"semi": ["error", "never"], "semi": ["error", "never"],
@ -120,6 +117,14 @@
"promise/no-multiple-resolved": "error" "promise/no-multiple-resolved": "error"
}, },
"overrides": [ "overrides": [
{
"files": ["!*.json"],
"plugins": ["prettier"],
"extends": ["plugin:prettier/recommended"],
"rules": {
"prettier/prettier": "error"
}
},
{ {
"files": ["*.json"], "files": ["*.json"],
"plugins": ["json"], "plugins": ["json"],
@ -131,7 +136,7 @@
"extends": ["plugin:vuetify/recommended"] "extends": ["plugin:vuetify/recommended"]
}, },
{ {
"files": ["**/*.test.[tj]s"], "files": ["*.[test,spec].[tj]s"],
"plugins": ["vitest"], "plugins": ["vitest"],
"extends": ["plugin:vitest/all"] "extends": ["plugin:vitest/all"]
}, },

View File

@ -2,6 +2,7 @@
[![nodejs][badge-nodejs-img]][badge-nodejs-href] [![nodejs][badge-nodejs-img]][badge-nodejs-href]
[![npm][badge-npm-img]][badge-npm-href] [![npm][badge-npm-img]][badge-npm-href]
[![docker][badge-docker-img]][badge-docker-href] [![docker][badge-docker-img]][badge-docker-href]
[![jq][badge-jq-img]][badge-jq-href]
[![vue][badge-vue-img]][badge-vue-href] [![vue][badge-vue-img]][badge-vue-href]
[![vike][badge-vike-img]][badge-vike-href] [![vike][badge-vike-img]][badge-vike-href]
[![vuetify][badge-vuetify-img]][badge-vuetify-href] [![vuetify][badge-vuetify-img]][badge-vuetify-href]
@ -18,7 +19,7 @@ The IT4C Boilerplate for frontends
## Requirements & Technology ## Requirements & Technology
To be able to build this project you need `nodejs`, `npm` and optional `docker`. To be able to build this project you need `nodejs`, `npm` and optional `docker` and `jq`.
The project uses `vite` as builder, `vike` to do the SSR. The design framework is `vuetify` which requires the frontend framework `vue3`. For localization `vue-i18n` is used; Session storage is handled with `pinia`. The project uses `vite` as builder, `vike` to do the SSR. The design framework is `vuetify` which requires the frontend framework `vue3`. For localization `vue-i18n` is used; Session storage is handled with `pinia`.
@ -30,29 +31,30 @@ This projects utilizes `storybook` to develop frontend components and `vuepress`
The following commands are available: The following commands are available:
| Command | Description | | Command | Description |
|----------------------------|-------------------------------------------------| |-----------------------------|-------------------------------------------------|
| `npm install` | Project setup | | `npm install` | Project setup |
| `npm run build` | Compiles and minifies for production | | `npm run build` | Compiles and minifies for production |
| `npm run server:prod` | Runs productions server | | `npm run server:prod` | Runs productions server |
| **Develop** | | | **Develop** | |
| `npm run dev` | Compiles and hot-reloads for development | | `npm run dev` | Compiles and hot-reloads for development |
| `npm run server:dev` | Run development server | | `npm run server:dev` | Run development server |
| **Test** | | | **Test** | |
| `npm run test:lint` | Run all linters | | `npm run test:lint` | Run all linters |
| `npm run test:lint:eslint` | Run linter eslint | | `npm run test:lint:eslint` | Run linter eslint |
| `npm run test:lint:remark` | Run linter remark | | `npm run test:lint:locales` | Run linter locales |
| `npm run test:lint:style` | Run linter stylelint | | `npm run test:lint:remark` | Run linter remark |
| `npm run test:unit` | Run all unit tests and generate coverage report | | `npm run test:lint:style` | Run linter stylelint |
| `npm run test:unit:dev` | Run all unit tests in watch mode | | `npm run test:unit` | Run all unit tests and generate coverage report |
| `npm test` | Run all tests & linters | | `npm run test:unit:dev` | Run all unit tests in watch mode |
| **Storybook** | | | `npm test` | Run all tests & linters |
| `npm run storybook` | Run Storybook | | **Storybook** | |
| `npm run storybook:build` | Build static storybook | | `npm run storybook` | Run Storybook |
| `npm run storybook:test` | Run tests against all storybook stories | | `npm run storybook:build` | Build static storybook |
| **Documentation** | | | `npm run storybook:test` | Run tests against all storybook stories |
| `npm run docs:dev` | Run Documentation in development mode | | **Documentation** | |
| `npm run docs:build` | Build static documentation | | `npm run docs:dev` | Run Documentation in development mode |
| `npm run docs:build` | Build static documentation |
### Docker ### Docker
@ -81,9 +83,7 @@ The following endpoints are provided given the right command is executed or all
- [ ] figma - [ ] figma
- [ ] chromatic - [ ] chromatic
- [ ] github actions - [ ] github actions
- [ ] lint locales
- [ ] feature zähler -> pinia tore - [ ] feature zähler -> pinia tore
- [ ] lint style (inline vue) stylelint?
## Known Problems ## Known Problems
@ -103,6 +103,9 @@ Currently none
[badge-docker-img]: https://img.shields.io/badge/docker-latest-blue [badge-docker-img]: https://img.shields.io/badge/docker-latest-blue
[badge-docker-href]: https://www.docker.com/ [badge-docker-href]: https://www.docker.com/
[badge-jq-img]: https://img.shields.io/badge/jq-latest-blue
[badge-jq-href]: https://jqlang.github.io/jq/
[badge-vue-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.vue&label=vue&color=green [badge-vue-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.vue&label=vue&color=green
[badge-vue-href]: https://vuejs.org/ [badge-vue-href]: https://vuejs.org/

View File

@ -40,8 +40,9 @@
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"storybook:build": "storybook build -o build/storybook", "storybook:build": "storybook build -o build/storybook",
"storybook:test": "test-storybook", "storybook:test": "test-storybook",
"test:lint": "npm run test:lint:eslint && npm run test:lint:remark && npm run test:lint:style", "test:lint": "npm run test:lint:eslint && npm run test:lint:remark && npm run test:lint:style && npm run test:lint:locales",
"test:lint:eslint": "eslint --ext .vue,.ts,.tsx,.js,.jsx,.json,.yml,.yaml --max-warnings 0 --ignore-path .gitignore .", "test:lint:eslint": "eslint --ext .vue,.ts,.tsx,.js,.jsx,.json,.yml,.yaml --max-warnings 0 --ignore-path .gitignore .",
"test:lint:locales": "scripts/locales/locales.sh src/locales",
"test:lint:remark": "remark . --quiet --frail", "test:lint:remark": "remark . --quiet --frail",
"test:lint:style": "stylelint --max-warnings 0 --ignore-path .gitignore \"**/*.{css,scss,vue,vuex}\"", "test:lint:style": "stylelint --max-warnings 0 --ignore-path .gitignore \"**/*.{css,scss,vue,vuex}\"",
"test:unit": "npm run test:unit:dev -- run --coverage", "test:unit": "npm run test:unit:dev -- run --coverage",

View File

@ -2,8 +2,10 @@ import { createI18n } from 'vue-i18n'
// eslint-disable-next-line import/no-relative-parent-imports // eslint-disable-next-line import/no-relative-parent-imports
import de from '../src/locales/de' import de from '../src/locales/de'
// import { de as $vuetify } from 'vuetify/locale'
// eslint-disable-next-line import/no-relative-parent-imports // eslint-disable-next-line import/no-relative-parent-imports
import en from '../src/locales/en' import en from '../src/locales/en'
// import { en as $vuetify } from 'vuetify/locale'
export default createI18n({ export default createI18n({
legacy: false, // Vuetify does not support the legacy mode of vue-i18n legacy: false, // Vuetify does not support the legacy mode of vue-i18n

1
scripts/locales/keys.jq Normal file
View File

@ -0,0 +1 @@
path(..)|[.[]|tostring]|join(".")

44
scripts/locales/locales.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
if [ $# -eq 0 ]
then
echo "You have to supply at least one argument specifying the folder to lint"
fi
FILES="$1"
tmp=$(mktemp)
exit_code=0
for locale_file in $FILES/*.json
do
jq -f $(dirname "$0")/sort.jq $locale_file > "$tmp"
# check sort order and fix it if required
if [ "$2" == "--fix" ]
then
mv "$tmp" $locale_file
else
if diff -q "$tmp" $locale_file > /dev/null ;
then
: # all good
else
exit_code=$?
echo "$(basename -- $locale_file) is not sorted by keys"
fi
fi
# check keys
if [ -n "$LAST_FILE" ]; then
listPaths="jq -f $(dirname "$0")/keys.jq"
diffString="<( cat $LAST_FILE | $listPaths ) <( cat $locale_file | $listPaths )"
if eval "diff -q $diffString";
then
: # all good
else
eval "diff -y $diffString | grep '[|<>]'";
printf "\n$LAST_FILE\" and $locale_file translation keys do not match, see diff above.\n"
exit_code=1
fi
fi
LAST_FILE=$locale_file
done
exit $exit_code

13
scripts/locales/sort.jq Normal file
View File

@ -0,0 +1,13 @@
def walk(f):
. as $in
| if type == "object" then
reduce keys_unsorted[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
def keys_sort_by(f):
to_entries | sort_by(.key|f ) | from_entries;
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)

3
src/locales/de.json Normal file
View File

@ -0,0 +1,3 @@
{
"counter": "Zähler"
}

View File

@ -1,6 +0,0 @@
import { de as $vuetify } from 'vuetify/locale'
export default {
counter: 'Zähler',
$vuetify,
}

3
src/locales/en.json Normal file
View File

@ -0,0 +1,3 @@
{
"counter": "Counter"
}

View File

@ -1,6 +0,0 @@
import { en as $vuetify } from 'vuetify/locale'
export default {
counter: 'Counter',
$vuetify,
}

View File

@ -8,7 +8,8 @@
"lib": ["DOM", "DOM.Iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vite/client"], "types": ["vite/client"],
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true "esModuleInterop": true,
"resolveJsonModule": true
}, },
"ts-node": { "ts-node": {
"transpileOnly": true, "transpileOnly": true,

View File

@ -12,10 +12,10 @@ export default mergeConfig(
coverage: { coverage: {
all: true, all: true,
include: ['src/**/*.{js,jsx,ts,tsx,vue}'], include: ['src/**/*.{js,jsx,ts,tsx,vue}'],
lines: 5, lines: 2,
functions: 0, functions: 0,
branches: 10, branches: 10,
statements: 5, statements: 2,
// 100: true, // 100: true,
}, },
}, },