1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 02:44:45 +00:00

Merge pull request #1 from robonen/repo-update

Repo update
This commit is contained in:
2024-04-10 03:33:51 +07:00
committed by GitHub
14 changed files with 1464 additions and 124 deletions

28
.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# ide
.idea
.vscode
# temp
node_modules
**/*~
**/*.log
**/.DS_Store
**/Thumbs.db
# build
.vitepress/dist
.vitepress/cache
.output
.nuxt
.nitro
.cache
out
build
dist
# test
coverage
# env
.env*

20
.vitepress/config.ts Normal file
View File

@@ -0,0 +1,20 @@
import { defineConfig } from 'vitepress';
export default defineConfig({
lang: 'ru-RU',
title: "Tools",
description: "A set of tools and utilities for web development",
rewrites: {
'packages/:pkg/README.md': 'packages/:pkg/index.md'
},
themeConfig: {
sidebar: [
{
text: 'Пакеты',
items: [
{ text: '@robonen/tsconfig', link: '/packages/tsconfig/' },
],
},
],
},
});

View File

@@ -1,4 +0,0 @@
# toolkit
My most frequently used tools in programming
- [Plural](/plural) (TS, PHP)

132
cli.ts Normal file
View File

@@ -0,0 +1,132 @@
import { mkdir, writeFile } from 'node:fs/promises';
import { defineCommand, runMain } from 'citty';
import { resolve } from 'pathe';
import { splitByCase } from 'scule';
const PACKAGE_MANAGER = 'pnpm@8.15.6';
const NODE_VERSION = '>=18.0.0';
const VITE_VERSION = '^5.2.8';
const VITE_DTS_VERSION = '^3.8.1';
const DEFAULT_DIR = 'packages';
const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
const data = {
name,
private: true,
version: '1.0.0',
license: 'UNLICENSED',
description: '',
keywords: [],
author: 'Robonen Andrew <robonenandrew@gmail.com>',
repository: {
type: 'git',
url: 'git+https://github.com/robonen/tools.git',
directory: path,
},
packageManager: PACKAGE_MANAGER,
engines: {
node: NODE_VERSION,
},
type: 'module',
files: ['dist'],
main: './dist/index.cjs',
module: './dist/index.js',
types: './dist/index.d.ts',
exports: {
'.': {
import: './dist/index.js',
require: './dist/index.cjs',
types: './dist/index.d.ts',
},
},
scripts: {
test: 'echo \"Error: no test specified\" && exit 1',
...(hasVite && {
dev: 'vite',
build: 'vite build',
preview: 'vite preview',
}),
},
devDependencies: {
'@robonen/tsconfig': 'workspace:*',
...(hasVite && {
vite: VITE_VERSION,
'vite-plugin-dts': VITE_DTS_VERSION,
}),
},
};
return JSON.stringify(data, null, 2);
};
const generateViteConfig = () => `import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
export default defineConfig({
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
plugins: [
dts({ insertTypesEntry: true }),
],
});
`;
const generateTsConfig = () => {
const data = {
extends: '@robonen/tsconfig/tsconfig.json',
};
return JSON.stringify(data, null, 2);
};
const generateReadme = (name: string) => `# ${name}`;
const createCommand = defineCommand({
meta: {
name: "create",
version: "1.0.0",
description: "Command to create a new project",
},
args: {
name: {
type: 'positional',
description: "Name of the project",
required: true,
},
path: {
type: 'positional',
description: "Relative path to the project folder",
required: false,
},
vite: {
type: 'boolean',
description: "Add Vite to the project",
required: false,
},
},
async run({ args }) {
const path = args.path ?? `./${DEFAULT_DIR}/${splitByCase(args.name).at(-1)}`;
const resolvedPath = resolve(path);
const hasVite = args.vite ?? false;
console.log(`Creating project in ${resolvedPath}`);
await mkdir(resolvedPath, { recursive: true });
writeFile(`${resolvedPath}/package.json`, generatePackageJson(args.name, path, hasVite));
writeFile(`${resolvedPath}/tsconfig.json`, generateTsConfig());
writeFile(`${resolvedPath}/README.md`, generateReadme(args.name));
if (hasVite) {
mkdir(`${resolvedPath}/src`, { recursive: true });
writeFile(`${resolvedPath}/vite.config.ts`, generateViteConfig());
}
console.log(`Project created successfully`);
},
});
runMain(createCommand);

37
package.json Normal file
View File

@@ -0,0 +1,37 @@
{
"name": "tools",
"private": true,
"version": "1.0.0",
"license": "UNLICENSED",
"description": "Set of useful tools for web development",
"keywords": [
"tools",
"web",
"ui",
"utilities"
],
"author": "Robonen Andrew <robonenandrew@gmail.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/robonen/tools.git"
},
"packageManager": "pnpm@8.15.6",
"engines": {
"node": ">=18.0.0"
},
"type": "module",
"devDependencies": {
"@types/node": "^20.12.6",
"citty": "^0.1.6",
"jiti": "^1.21.0",
"pathe": "^1.1.2",
"scule": "^1.3.0",
"vitepress": "^1.1.0"
},
"scripts": {
"create": "jiti ./cli.ts",
"docs:dev": "vitepress dev .",
"docs:build": "vitepress build .",
"docs:preview": "vitepress preview ."
}
}

View File

@@ -0,0 +1,45 @@
# @robonen/tsconfig
Базовый конфигурационный файл для TypeScript
## Установка
```bash
pnpm install -D @robonen/tsconfig
```
```json
{
"extends": "@robonen/tsconfig/tsconfig.json"
}
```
## Описание основных параметров
```json
{
"module": "Preserve", // использовать ту же версию модуля, что и сборщик
"noEmit": true, // не генерировать файлы
"moduleResolution": "Bundler", // разрешение модулей на основе сборщика
"target": "ESNext", // целевая версия JavaScript
"skipLibCheck": true, // не проверять типы, заданные во всех файлах описания типов (*.d.ts)
"esModuleInterop": true, // создать хелперы __importStar и __importDefault для обеспечения совместимости с экосистемой Babel и включить allowSyntheticDefaultImports для совместимости с системой типов
"allowSyntheticDefaultImports": true, // разрешить импортировать модули не имеющие внутри себя "import default"
"allowJs": true, // разрешить импортировать файлы JavaScript
"resolveJsonModule": true, // разрешить импортировать файлы JSON
"moduleDetection": "force", // заставляет TypeScript рассматривать все файлы как модули. Это помогает избежать ошибок cannot redeclare block-scoped variable»
"isolatedModules": true, // орабатывать каждый файл, как отдельный изолированный модуль
"removeComments": true, // удалять комментарии из исходного кода
"verbatimModuleSyntax": true, // сохранять синтаксис модулей в исходном коде (важно при импорте типов)
"useDefineForClassFields": true, // использование классов стандарта TC39, а не TypeScript
"strict": true, // включить все строгие проверки (noImplicitAny, noImplicitThis, alwaysStrict, strictNullChecks, strictFunctionTypes, strictPropertyInitialization)
"noUncheckedIndexedAccess": true, // запрещает доступ к массиву или объекту без предварительной проверки того, определен ли он
"declaration": true, // генерировать файлы описания типов (*.d.ts)
"composite": true, // указывает TypeScript создавать файлы .tsbuildinfo. Это сообщает TypeScript, что ваш проект является частью монорепозитория, а также помогает кэшировать сборки для более быстрой работы
"sourceMap": true, // генерировать карту исходного кода
"declarationMap": true // генерировать карту исходного кода для файлов описания типов (*.d.ts)
}
```

View File

@@ -0,0 +1,26 @@
{
"name": "@robonen/tsconfig",
"private": true,
"version": "1.0.0",
"license": "UNLICENSED",
"description": "",
"keywords": [
"tsconfig",
"typescript",
"ts",
"config"
],
"author": "Robonen Andrew <robonenandrew@gmail.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/robonen/tools.git",
"directory": "packages/tsconfig"
},
"packageManager": "pnpm@8.15.6",
"engines": {
"node": ">=18.0.0"
},
"files": [
"**tsconfig.json"
]
}

View File

@@ -0,0 +1,36 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base TypeScript Configuration",
"compilerOptions": {
/* Basic Options */
"module": "Preserve",
"noEmit": true,
"moduleResolution": "Bundler",
"sourceMap": true,
"target": "ESNext",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"removeComments": true,
"verbatimModuleSyntax": true,
"useDefineForClassFields": true,
/* Strict Type-Checking Options */
"strict": true,
"noUncheckedIndexedAccess": true,
/* Library transpiling */
"declaration": true,
/* Library in monorepo */
"composite": true,
"declarationMap": true
},
"exclude": ["node_modules", "dist"]
}

View File

@@ -1,37 +0,0 @@
# Функция изменения формы слов в зависимости от числительного
Часто встречается задача вывода правильного окончания слова с предшествующим ему числом.
Например:
- 1 депутат
- 24 депутат**а**
- 0, 5-9 или 10 депутат**ов**
## Примеры исользования
### Typescript
```typescript
import { plural } from 'plural';
const totalOrders = 2;
const words = ['заказ', 'заказа', 'заказов'];
const result = `${totalOrders} ${plural(totalOrders, words)}`;
console.log(result); // 2 заказа
```
### PHP
```php
include 'plural.php';
$totalOrders = 2;
$words = ['заказ', 'заказа', 'заказов'];
$result = "{$totalOrders} {plural($totalOrders, $words)}";
echo result; // 2 заказа
```

View File

@@ -1,9 +0,0 @@
<?php
function plural(int $count, array $words)
{
$cases = [2, 0, 1, 1, 1, 2];
return $words[
$count % 100 > 4 && $count % 100 < 20 ? 2 : $cases[min($count % 10, 5)]
];
}

View File

@@ -1,8 +0,0 @@
export type WordForms = [string, string, string];
export const plural = (count: number, words: WordForms): string => {
const cases = [2, 0, 1, 1, 1, 2];
return words[
count % 100 > 4 && count % 100 < 20 ? 2 : cases[Math.min(count % 10, 5)]
];
};

1138
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

2
pnpm-workspaces.yaml Normal file
View File

@@ -0,0 +1,2 @@
packages:
- packages/*

View File

@@ -1,66 +0,0 @@
type Split<S extends string, D extends string> = S extends ''
? []
: S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S];
type UnwrapNumbers<T extends string | string[] | number | number[]> = T extends number | number[]
? T
: T extends string
? T extends `${infer N extends number}`
? N
: never
: T extends [infer H extends string, ...infer T extends string[]]
? UnwrapNumbers<H> extends never
? never
: [UnwrapNumbers<H>, ...UnwrapNumbers<T>]
: [];
type IPv4Octets = [number, number, number, number];
type IPv6Octets = string[];
type IPv4<CIDR extends string> = Split<CIDR, '/'> extends [
infer IP extends string,
infer SUBNET extends string,
]
? UnwrapNumbers<Split<IP, '.'>> extends infer OCTETS
? OCTETS extends never
? never
: OCTETS extends IPv4Octets
? UnwrapNumbers<SUBNET> extends never
? never
: CIDR
: never
: never
: never;
type IPv6<CIDR extends string> = Split<CIDR, '/'> extends [
infer IP extends string,
infer SUBNET extends string,
]
? Split<IP, ':'> extends IPv6Octets
? UnwrapNumbers<SUBNET> extends never
? never
: CIDR
: never
: never;
type Test_1 = IPv4<'0.0.0.0/0'>;
type Test_2 = IPv4<'255.255.255.255/32'>;
type Test_3 = IPv4<'127.0.0.0.1/32'>;
type Test_4 = IPv4<'a'>;
type Test_5 = IPv4<'1.1.1.a/32'>;
type Test_6 = IPv4<'1.1.1.1'>;
type Test_7 = IPv4<'1.1.1.1/'>;
type Test_8 = IPv4<'1.1.1.1/a'>;
type Test_9 = IPv6<'::1/0'>;
type Test_10 = IPv6<'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128'>;
type Test_11 = IPv6<'::1/128'>;
type Test_12 = IPv6<'a'>;
type Test_13 = IPv6<'1:1:1:1:1:1:1:1/128'>;
type Test_14 = IPv6<'1:1:1:1:1:1:1:1'>;
type Test_15 = IPv6<'1:1:1:1:1:1:1:1/'>;
type Test_16 = IPv6<'1:1:1:1:1:1:1:1/a'>;
// TODO: fully-typed ipv6 (unwrap hex, full and abbreviated address representations)