From 25456edc101b292259994e407dfec5e17488d2b9 Mon Sep 17 00:00:00 2001 From: Aliakseyenka Ihar Date: Mon, 25 Nov 2024 21:33:43 +0300 Subject: [PATCH] chore: initial setup --- .editorconfig | 15 +++ .github/workflows/tests.yml | 42 ++++++++ .gitignore | 175 +++++++++++++++++++++++++++++++ .husky/install.mjs | 9 ++ .husky/pre-commit | 1 + CHANGELOG.md | 1 + LICENSE | 21 ++++ README.md | 49 +++++++++ bun.lockb | Bin 0 -> 109683 bytes eslint.config.mjs | 31 ++++++ package.json | 36 +++++++ src/index.ts | 5 + src/pluralizers/ru/index.test.ts | 97 +++++++++++++++++ src/pluralizers/ru/index.ts | 32 ++++++ src/types/options.d.ts | 11 ++ tsconfig.json | 31 ++++++ vite.config.ts | 29 +++++ 17 files changed, 585 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 .husky/install.mjs create mode 100644 .husky/pre-commit create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100755 bun.lockb create mode 100644 eslint.config.mjs create mode 100644 package.json create mode 100644 src/index.ts create mode 100644 src/pluralizers/ru/index.test.ts create mode 100644 src/pluralizers/ru/index.ts create mode 100644 src/types/options.d.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..653466d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true + +[*.yml] +indent_size = 2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..ceb015e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,42 @@ +name: Run tests + +on: + pull_request: + types: + - opened + push: + branches: + - master + paths: + - '.github/workflows/tests.yml' + - 'bun.lockb' + - '**.ts' + +env: + HUSKY: 0 + +jobs: + unit-tests: + runs-on: ubuntu-22.04 + strategy: + fail-fast: true + matrix: + node-version: [18, 20, 22, 23] + + name: NodeJS ${{ matrix.node-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - uses: oven-sh/setup-bun@v1 + + - name: Install dependencies + run: bun install + + - name: Running tests + run: bun test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/.husky/install.mjs b/.husky/install.mjs new file mode 100644 index 0000000..ee1d715 --- /dev/null +++ b/.husky/install.mjs @@ -0,0 +1,9 @@ +/* eslint-env node */ + +// Skip Husky install in CI +// @see https://typicode.github.io/husky/how-to.html +if (process.env.CI === 'true') { + process.exit(0) +} + +(await import('husky')).default diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..ea5a55b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +bunx lint-staged diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ac239ca --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# Releases diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3889eb1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Ihar Aliakseyenka + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab3e8ae --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Pluralizer + +TODO + +## Examples + +```js +const options = { + single: 'яблоко', + some: 'яблока', + many: 'яблок', +} + +pluralizeRu(0, options) // '0 яблок' +pluralizeRu(1, options) // '1 яблоко' +pluralizeRu(2, options) // '2 яблока' +pluralizeRu(11, options) // '11 яблок' +pluralizeRu(33, options) // '11 яблока' +pluralizeRu(50, options) // '50 яблок' +pluralizeRu(51, options) // '51 яблоко' +pluralizeRu(1022, options) // '1022 яблока' + +const options = { + single: 'яблоко', + some: 'яблока', + many: 'яблок', + none: 'яблочищ', // zero items +} + +pluralizeRu(0, options) // '0 яблочищ' + +const options = { + single: 'яблоко', + some: 'яблока', + many: 'яблок', + none: 'яблок', +} + +pluralizeRu(0, options, 'Нет') // 'Нет яблок' + +const options = { + single: 'яблоко', + some: 'яблока', + many: 'яблок', + none: 'яблок', +} + +pluralizeRu(0, options, 0, false) // 'яблок' // do not show amount of items +``` diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..f90b9be50957b33f68ebe9b71a9155cd19666072 GIT binary patch literal 109683 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 z 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 literal 0 HcmV?d00001 diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..9fd8e7e --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,31 @@ +// @ts-check + +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' +import stylisticTs from '@stylistic/eslint-plugin-ts' + +export default tseslint.config( + { + ignores: [ + '**/node_modules/**', + '**/dist/**', + ], + }, + eslint.configs.recommended, + tseslint.configs.stylistic, + { + plugins: { + '@stylistic/ts': stylisticTs, + }, + + // @see https://typescript-eslint.io/rules/ + rules: { + '@stylistic/ts/semi': ['warn', 'never'], + '@stylistic/ts/quotes': ['warn', 'single'], + '@stylistic/ts/comma-dangle': ['warn', 'always-multiline'], + 'no-console': ['error', { + allow: ['warn', 'error'], + }], + }, + }, +) diff --git a/package.json b/package.json new file mode 100644 index 0000000..6a45266 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "pluralizer", + "description": "pluralize words depends on amount of items", + "license": "MIT", + "version": "0.1.0", + "module": "./dist/pluralizer.js", + "types": "./dist/index.d.ts", + "type": "module", + "files": [ + "dist" + ], + "scripts": { + "build": "vite build", + "lint": "eslint ./src", + "lint:fix": "eslint ./src --fix", + "prepare": "node .husky/install.mjs" + }, + "lint-staged": { + "*.ts": "eslint --fix" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@stylistic/eslint-plugin-ts": "^2.11.0", + "@types/bun": "latest", + "ajv": "^8.17.1", + "eslint": "^9.15.0", + "husky": "^9.1.7", + "lint-staged": "^15.2.10", + "typescript-eslint": "^8.15.0", + "vite": "^5.4.11", + "vite-plugin-dts": "^4.3.0" + }, + "peerDependencies": { + "typescript": "^5.7.2" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8eb9310 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +import { pluralize as pluralizeRu } from '@/pluralizers/ru' + +export { + pluralizeRu, +} diff --git a/src/pluralizers/ru/index.test.ts b/src/pluralizers/ru/index.test.ts new file mode 100644 index 0000000..a4bcf15 --- /dev/null +++ b/src/pluralizers/ru/index.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it, test } from 'bun:test' +import { pluralizeRu } from '@/index' +import type { PluralizeOptionsWithNoneKey, PluralizeOptionsWithoutNoneKey } from '@/types/options' + +const OPTIONS_WITH_NONE_KEY: PluralizeOptionsWithNoneKey = { + none: 'нет яблок', + single: 'яблоко', + some: 'яблока', + many: 'яблок', +} + +const OPTIONS_WITHOUT_NONE_KEY: PluralizeOptionsWithoutNoneKey = { + single: 'яблоко', + some: 'яблока', + many: 'яблок', +} + +describe('Russian pluralization', () => { + describe('options has `none` key', () => { + test.each([ + { count: 0, options: OPTIONS_WITH_NONE_KEY, expected: 'нет яблок' }, + { count: 1, options: OPTIONS_WITH_NONE_KEY, expected: 'яблоко' }, + { count: 2, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 3, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 4, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 5, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 6, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 7, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 8, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 9, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 10, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 11, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 12, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 13, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 14, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 15, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 20, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 21, options: OPTIONS_WITH_NONE_KEY, expected: 'яблоко' }, + { count: 22, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 23, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 24, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + { count: 25, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 69, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, // nice + { count: 100, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 101, options: OPTIONS_WITH_NONE_KEY, expected: 'яблоко' }, + { count: 111, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 1001, options: OPTIONS_WITH_NONE_KEY, expected: 'яблоко' }, + { count: 1011, options: OPTIONS_WITH_NONE_KEY, expected: 'яблок' }, + { count: 1022, options: OPTIONS_WITH_NONE_KEY, expected: 'яблока' }, + ])('should return correct value', ({ count, options, expected }) => { + expect(pluralizeRu(count, options, 0, false)).toBe(expected) + }) + }) + + describe('options has not `none` key', () => { + test.each([ + { count: 0, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 1, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблоко' }, + { count: 2, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 3, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 4, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 5, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 6, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 7, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 8, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 9, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 10, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 11, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 12, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 13, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 14, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 15, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 20, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 21, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблоко' }, + { count: 22, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 23, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 24, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + { count: 25, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 100, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 101, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблоко' }, + { count: 111, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 1001, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблоко' }, + { count: 1011, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблок' }, + { count: 1022, options: OPTIONS_WITHOUT_NONE_KEY, expected: 'яблока' }, + ])('should return correct value', ({ count, options, expected }) => { + expect(pluralizeRu(count, options, 0, false)).toBe(expected) + }) + }) + + it('should show value', () => { + expect(pluralizeRu(10, OPTIONS_WITH_NONE_KEY)).toBe('10 яблок') + }) + + it('should show placeholder', () => { + expect(pluralizeRu(0, OPTIONS_WITH_NONE_KEY, 'Совсем')).toBe('Совсем нет яблок') + }) +}) diff --git a/src/pluralizers/ru/index.ts b/src/pluralizers/ru/index.ts new file mode 100644 index 0000000..047d793 --- /dev/null +++ b/src/pluralizers/ru/index.ts @@ -0,0 +1,32 @@ +import type { PluralizeOptions, PluralizeOptionsWithoutNoneKey } from '@/types/options' + +const resolveKey = (val: number): keyof PluralizeOptionsWithoutNoneKey => { + if (val % 10 === 1 && val % 100 !== 11) { + return 'single' + } + + if (val % 10 >= 2 && val % 10 <= 4 && (val % 100 < 10 || val % 100 >= 20)) { + return 'some' + } + + return 'many' +} + +export const pluralize = ( + amount: number, + options: PluralizeOptions, + emptyPlaceholder: string | number = 0, + showAmount = true, +): string => { + const amountDisplayValue = amount === 0 ? emptyPlaceholder : amount + + const key = resolveKey(amount) + if ('none' in options) { + const noneKey = amount === 0 ? 'none' : key + const pluralized = options[noneKey] + return showAmount ? `${amountDisplayValue} ${pluralized}` : pluralized + } + + const pluralized = options[key] + return showAmount ? `${amountDisplayValue} ${pluralized}` : pluralized +} diff --git a/src/types/options.d.ts b/src/types/options.d.ts new file mode 100644 index 0000000..dad752d --- /dev/null +++ b/src/types/options.d.ts @@ -0,0 +1,11 @@ +export interface PluralizeOptionsWithoutNoneKey { + single: string + some: string + many: string +} + +export interface PluralizeOptionsWithNoneKey extends PluralizeOptionsWithoutNoneKey { + none: string +} + +export type PluralizeOptions = PluralizeOptionsWithoutNoneKey | PluralizeOptionsWithNoneKey diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8b16103 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + "baseUrl": "./", + "paths": { + "@/*": ["src/*"], + }, + }, + "exclude": ["node_modules", "dist"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..4f8f450 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from 'vite' +import { fileURLToPath, URL } from 'url' +import dts from 'vite-plugin-dts' + +export default defineConfig({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + build: { + lib: { + entry: './src/index.ts', + name: 'pluralizer', + formats: ['es'], + }, + }, + plugins: [ + dts({ + copyDtsFiles: true, + include: [ + 'src/', + ], + exclude: [ + '**/*.test.ts', + ], + }), + ], +})