mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 10:54:44 +00:00
9
cli.ts
9
cli.ts
@@ -7,6 +7,7 @@ const PACKAGE_MANAGER = 'pnpm@8.15.6';
|
|||||||
const NODE_VERSION = '>=18.0.0';
|
const NODE_VERSION = '>=18.0.0';
|
||||||
const VITE_VERSION = '^5.2.8';
|
const VITE_VERSION = '^5.2.8';
|
||||||
const VITE_DTS_VERSION = '^3.8.1';
|
const VITE_DTS_VERSION = '^3.8.1';
|
||||||
|
const PATHE_VERSION = '^1.1.2'
|
||||||
const DEFAULT_DIR = 'packages';
|
const DEFAULT_DIR = 'packages';
|
||||||
|
|
||||||
const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
|
const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
|
||||||
@@ -29,13 +30,13 @@ const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
|
|||||||
},
|
},
|
||||||
type: 'module',
|
type: 'module',
|
||||||
files: ['dist'],
|
files: ['dist'],
|
||||||
main: './dist/index.cjs',
|
main: './dist/index.umd.js',
|
||||||
module: './dist/index.js',
|
module: './dist/index.js',
|
||||||
types: './dist/index.d.ts',
|
types: './dist/index.d.ts',
|
||||||
exports: {
|
exports: {
|
||||||
'.': {
|
'.': {
|
||||||
import: './dist/index.js',
|
import: './dist/index.js',
|
||||||
require: './dist/index.cjs',
|
require: './dist/index.umd.js',
|
||||||
types: './dist/index.d.ts',
|
types: './dist/index.d.ts',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -51,7 +52,8 @@ const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
|
|||||||
'@robonen/tsconfig': 'workspace:*',
|
'@robonen/tsconfig': 'workspace:*',
|
||||||
...(hasVite && {
|
...(hasVite && {
|
||||||
vite: VITE_VERSION,
|
vite: VITE_VERSION,
|
||||||
'vite-plugin-dts': VITE_DTS_VERSION,
|
'vite-plugin-dts': VITE_DTS_VERSION,
|
||||||
|
pathe: PATHE_VERSION,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -61,6 +63,7 @@ const generatePackageJson = (name: string, path: string, hasVite: boolean) => {
|
|||||||
|
|
||||||
const generateViteConfig = () => `import { defineConfig } from 'vite';
|
const generateViteConfig = () => `import { defineConfig } from 'vite';
|
||||||
import dts from 'vite-plugin-dts';
|
import dts from 'vite-plugin-dts';
|
||||||
|
import { resolve } from 'pathe';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
1
packages/stdlib/README.md
Normal file
1
packages/stdlib/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# @robonen/stdlib
|
||||||
55
packages/stdlib/package.json
Normal file
55
packages/stdlib/package.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"name": "@robonen/stdlib",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"stdlib",
|
||||||
|
"utils",
|
||||||
|
"tools",
|
||||||
|
"helpers",
|
||||||
|
"math",
|
||||||
|
"algorithms",
|
||||||
|
"data-structures"
|
||||||
|
],
|
||||||
|
"author": "Robonen Andrew <robonenandrew@gmail.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/robonen/tools.git",
|
||||||
|
"directory": "packages/stdlib"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@8.15.6",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"main": "./dist/stdlib.umd.js",
|
||||||
|
"module": "./dist/stdlib.js",
|
||||||
|
"types": "./dist/stdlib.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/stdlib.js",
|
||||||
|
"require": "./dist/stdlib.umd.js",
|
||||||
|
"types": "./dist/stdlib.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"test:bench": "vitest bench",
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@robonen/tsconfig": "workspace:*",
|
||||||
|
"@vitest/coverage-v8": "^1.4.0",
|
||||||
|
"pathe": "^1.1.2",
|
||||||
|
"vite": "^5.2.8",
|
||||||
|
"vite-plugin-dts": "^3.8.1",
|
||||||
|
"vitest": "^1.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/stdlib/src/index.ts
Normal file
2
packages/stdlib/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './text';
|
||||||
|
export * from './math';
|
||||||
46
packages/stdlib/src/math/clamp/index.test.ts
Normal file
46
packages/stdlib/src/math/clamp/index.test.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe,it, expect } from 'vitest';
|
||||||
|
import { clamp } from '.';
|
||||||
|
|
||||||
|
describe('clamp', () => {
|
||||||
|
it('clamp a value within the given range', () => {
|
||||||
|
// value < min
|
||||||
|
expect(clamp(-10, 0, 100)).toBe(0);
|
||||||
|
|
||||||
|
// value > max
|
||||||
|
expect(clamp(200, 0, 100)).toBe(100);
|
||||||
|
|
||||||
|
// value within range
|
||||||
|
expect(clamp(50, 0, 100)).toBe(50);
|
||||||
|
|
||||||
|
// value at min
|
||||||
|
expect(clamp(0, 0, 100)).toBe(0);
|
||||||
|
|
||||||
|
// value at max
|
||||||
|
expect(clamp(100, 0, 100)).toBe(100);
|
||||||
|
|
||||||
|
// value at midpoint
|
||||||
|
expect(clamp(50, 100, 100)).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle floating-point numbers correctly', () => {
|
||||||
|
// floating-point value within range
|
||||||
|
expect(clamp(3.14, 0, 5)).toBe(3.14);
|
||||||
|
|
||||||
|
// floating-point value < min
|
||||||
|
expect(clamp(-1.5, 0, 10)).toBe(0);
|
||||||
|
|
||||||
|
// floating-point value > max
|
||||||
|
expect(clamp(15.75, 0, 10)).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle edge cases', () => {
|
||||||
|
// all values are the same
|
||||||
|
expect(clamp(5, 5, 5)).toBe(5);
|
||||||
|
|
||||||
|
// min > max
|
||||||
|
expect(clamp(10, 100, 50)).toBe(50);
|
||||||
|
|
||||||
|
// negative range and value
|
||||||
|
expect(clamp(-10, -100, -5)).toBe(-10);
|
||||||
|
});
|
||||||
|
});
|
||||||
16
packages/stdlib/src/math/clamp/index.ts
Normal file
16
packages/stdlib/src/math/clamp/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Clamps a number between a minimum and maximum value
|
||||||
|
*
|
||||||
|
* @param {number} value The number to clamp
|
||||||
|
* @param {number} min Minimum value
|
||||||
|
* @param {number} max Maximum value
|
||||||
|
* @returns {number} The clamped number
|
||||||
|
*/
|
||||||
|
export function clamp(value: number, min: number, max: number): number {
|
||||||
|
// The clamp function takes a value, a minimum, and a maximum as parameters.
|
||||||
|
// It ensures that the value falls within the range defined by the minimum and maximum values.
|
||||||
|
// If the value is less than the minimum, it returns the minimum value.
|
||||||
|
// If the value is greater than the maximum, it returns the maximum value.
|
||||||
|
// Otherwise, it returns the original value.
|
||||||
|
return Math.min(Math.max(value, min), max);
|
||||||
|
}
|
||||||
2
packages/stdlib/src/math/index.ts
Normal file
2
packages/stdlib/src/math/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './clamp';
|
||||||
|
export * from './mapRange';
|
||||||
46
packages/stdlib/src/math/mapRange/index.test.ts
Normal file
46
packages/stdlib/src/math/mapRange/index.test.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { mapRange } from './index';
|
||||||
|
|
||||||
|
describe('mapRange', () => {
|
||||||
|
it('map values from one range to another', () => {
|
||||||
|
// value at midpoint
|
||||||
|
expect(mapRange(5, 0, 10, 0, 100)).toBe(50);
|
||||||
|
|
||||||
|
// value at min
|
||||||
|
expect(mapRange(0, 0, 10, 0, 100)).toBe(0);
|
||||||
|
|
||||||
|
// value at max
|
||||||
|
expect(mapRange(10, 0, 10, 0, 100)).toBe(100);
|
||||||
|
|
||||||
|
// value outside range (below)
|
||||||
|
expect(mapRange(-5, 0, 10, 0, 100)).toBe(0);
|
||||||
|
|
||||||
|
// value outside range (above)
|
||||||
|
expect(mapRange(15, 0, 10, 0, 100)).toBe(100);
|
||||||
|
|
||||||
|
// value at midpoint of negative range
|
||||||
|
expect(mapRange(75, 50, 100, -50, 50)).toBe(0);
|
||||||
|
|
||||||
|
// value at midpoint of negative range
|
||||||
|
expect(mapRange(-25, -50, 0, 0, 100)).toBe(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle floating-point numbers correctly', () => {
|
||||||
|
// floating-point value
|
||||||
|
expect(mapRange(3.5, 0, 10, 0, 100)).toBe(35);
|
||||||
|
|
||||||
|
// positive floating-point ranges
|
||||||
|
expect(mapRange(1.25, 0, 2.5, 0, 100)).toBe(50);
|
||||||
|
|
||||||
|
// negative floating-point value
|
||||||
|
expect(mapRange(-2.5, -5, 0, 0, 100)).toBe(50);
|
||||||
|
|
||||||
|
// negative floating-point ranges
|
||||||
|
expect(mapRange(-1.25, -2.5, 0, 0, 100)).toBe(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle edge cases', () => {
|
||||||
|
// input range is zero (should return output min)
|
||||||
|
expect(mapRange(5, 0, 0, 0, 100)).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
23
packages/stdlib/src/math/mapRange/index.ts
Normal file
23
packages/stdlib/src/math/mapRange/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { clamp } from "../clamp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a value from one range to another
|
||||||
|
*
|
||||||
|
* @param {number} value The value to map
|
||||||
|
* @param {number} in_min The minimum value of the input range
|
||||||
|
* @param {number} in_max The maximum value of the input range
|
||||||
|
* @param {number} out_min The minimum value of the output range
|
||||||
|
* @param {number} out_max The maximum value of the output range
|
||||||
|
* @returns {number} The mapped value
|
||||||
|
*/
|
||||||
|
export function mapRange(value: number, in_min: number, in_max: number, out_min: number, out_max: number): number {
|
||||||
|
// Zero input range means invalid input, so return lowest output range value
|
||||||
|
if (in_min === in_max)
|
||||||
|
return out_min;
|
||||||
|
|
||||||
|
// To ensure the value is within the input range, clamp it
|
||||||
|
const clampedValue = clamp(value, in_min, in_max);
|
||||||
|
|
||||||
|
// Finally, map the value from the input range to the output range
|
||||||
|
return (clampedValue - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||||
|
}
|
||||||
2
packages/stdlib/src/text/index.ts
Normal file
2
packages/stdlib/src/text/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './levenshtein-distance';
|
||||||
|
export * from './trigram-distance';
|
||||||
32
packages/stdlib/src/text/levenshtein-distance/index.test.ts
Normal file
32
packages/stdlib/src/text/levenshtein-distance/index.test.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import {levenshteinDistance} from '.';
|
||||||
|
|
||||||
|
describe('levenshteinDistance', () => {
|
||||||
|
it('calculate edit distance between two strings', () => {
|
||||||
|
// just one substitution I at the beginning
|
||||||
|
expect(levenshteinDistance('islander', 'slander')).toBe(1);
|
||||||
|
|
||||||
|
// substitution M->K, T->M and add an A to the end
|
||||||
|
expect(levenshteinDistance('mart', 'karma')).toBe(3);
|
||||||
|
|
||||||
|
// substitution K->S, E->I and insert G at the end
|
||||||
|
expect(levenshteinDistance('kitten', 'sitting')).toBe(3);
|
||||||
|
|
||||||
|
// should add 4 letters FOOT at the beginning
|
||||||
|
expect(levenshteinDistance('ball', 'football')).toBe(4);
|
||||||
|
|
||||||
|
// should delete 4 letters FOOT at the beginning
|
||||||
|
expect(levenshteinDistance('football', 'foot')).toBe(4);
|
||||||
|
|
||||||
|
// needs to substitute the first 5 chars INTEN->EXECU
|
||||||
|
expect(levenshteinDistance('intention', 'execution')).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle empty strings', () => {
|
||||||
|
expect(levenshteinDistance('', '')).toBe(0);
|
||||||
|
expect(levenshteinDistance('a', '')).toBe(1);
|
||||||
|
expect(levenshteinDistance('', 'a')).toBe(1);
|
||||||
|
expect(levenshteinDistance('abc', '')).toBe(3);
|
||||||
|
expect(levenshteinDistance('', 'abc')).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
44
packages/stdlib/src/text/levenshtein-distance/index.ts
Normal file
44
packages/stdlib/src/text/levenshtein-distance/index.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Calculate the Levenshtein distance between two strings
|
||||||
|
*
|
||||||
|
* @param {string} left First string
|
||||||
|
* @param {string} right Second string
|
||||||
|
* @returns {number} The Levenshtein distance between the two strings
|
||||||
|
*/
|
||||||
|
export function levenshteinDistance(left: string, right: string): number {
|
||||||
|
// If the strings are equal, the distance is 0
|
||||||
|
if (left === right) return 0;
|
||||||
|
|
||||||
|
// If either string is empty, the distance is the length of the other string
|
||||||
|
if (left.length === 0) return right.length;
|
||||||
|
if (right.length === 0) return left.length;
|
||||||
|
|
||||||
|
// Create empty edit distance matrix for all possible modifications of
|
||||||
|
// substrings of left to substrings of right
|
||||||
|
const distanceMatrix = Array(right.length + 1).fill(null).map(() => Array(left.length + 1).fill(null));
|
||||||
|
|
||||||
|
// Fill the first row of the matrix
|
||||||
|
// If this is the first row, we're transforming from an empty string to left
|
||||||
|
// In this case, the number of operations equals the length of left substring
|
||||||
|
for (let i = 0; i <= left.length; i++)
|
||||||
|
distanceMatrix[0]![i]! = i;
|
||||||
|
|
||||||
|
// Fill the first column of the matrix
|
||||||
|
// If this is the first column, we're transforming empty string to right
|
||||||
|
// In this case, the number of operations equals the length of right substring
|
||||||
|
for (let j = 0; j <= right.length; j++)
|
||||||
|
distanceMatrix[j]![0]! = j;
|
||||||
|
|
||||||
|
for (let j = 1; j <= right.length; j++) {
|
||||||
|
for (let i = 1; i <= left.length; i++) {
|
||||||
|
const indicator = left[i - 1] === right[j - 1] ? 0 : 1;
|
||||||
|
distanceMatrix[j]![i]! = Math.min(
|
||||||
|
distanceMatrix[j]![i - 1]! + 1, // deletion
|
||||||
|
distanceMatrix[j - 1]![i]! + 1, // insertion
|
||||||
|
distanceMatrix[j - 1]![i - 1]! + indicator // substitution
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distanceMatrix[right.length]![left.length]!;
|
||||||
|
}
|
||||||
93
packages/stdlib/src/text/trigram-distance/index.test.ts
Normal file
93
packages/stdlib/src/text/trigram-distance/index.test.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { trigramDistance, trigramProfile } from '.';
|
||||||
|
|
||||||
|
describe('trigramProfile', () => {
|
||||||
|
it('trigram profile of a text with different trigrams', () => {
|
||||||
|
const different_trigrams = 'hello world';
|
||||||
|
const profile1 = trigramProfile(different_trigrams);
|
||||||
|
|
||||||
|
expect(profile1).toEqual(new Map([
|
||||||
|
['\n\nh', 1],
|
||||||
|
['\nhe', 1],
|
||||||
|
['hel', 1],
|
||||||
|
['ell', 1],
|
||||||
|
['llo', 1],
|
||||||
|
['lo ', 1],
|
||||||
|
['o w', 1],
|
||||||
|
[' wo', 1],
|
||||||
|
['wor', 1],
|
||||||
|
['orl', 1],
|
||||||
|
['rld', 1],
|
||||||
|
['ld\n', 1],
|
||||||
|
['d\n\n', 1]
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trigram profile of a text with repeated trigrams', () => {
|
||||||
|
const repeated_trigrams = 'hello hello';
|
||||||
|
const profile2 = trigramProfile(repeated_trigrams);
|
||||||
|
|
||||||
|
expect(profile2).toEqual(new Map([
|
||||||
|
['\n\nh', 1],
|
||||||
|
['\nhe', 1],
|
||||||
|
['hel', 2],
|
||||||
|
['ell', 2],
|
||||||
|
['llo', 2],
|
||||||
|
['lo ', 1],
|
||||||
|
['o h', 1],
|
||||||
|
[' he', 1],
|
||||||
|
['lo\n', 1],
|
||||||
|
['o\n\n', 1]
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trigram profile of an empty text', () => {
|
||||||
|
const text = '';
|
||||||
|
const profile = trigramProfile(text);
|
||||||
|
|
||||||
|
expect(profile).toEqual(new Map([
|
||||||
|
['\n\n\n', 2],
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trigramDistance', () => {
|
||||||
|
it('zero when comparing the same text', () => {
|
||||||
|
const profile1 = trigramProfile('hello world');
|
||||||
|
const profile2 = trigramProfile('hello world');
|
||||||
|
|
||||||
|
expect(trigramDistance(profile1, profile2)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('one for completely different text', () => {
|
||||||
|
const profile1 = trigramProfile('hello world');
|
||||||
|
const profile2 = trigramProfile('lorem ipsum');
|
||||||
|
|
||||||
|
expect(trigramDistance(profile1, profile2)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('one for empty text and non-empty text', () => {
|
||||||
|
const profile1 = trigramProfile('hello world');
|
||||||
|
const profile2 = trigramProfile('');
|
||||||
|
|
||||||
|
expect(trigramDistance(profile1, profile2)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('approximately 0.5 for similar text', () => {
|
||||||
|
const profile1 = trigramProfile('hello world');
|
||||||
|
const profile2 = trigramProfile('hello lorem');
|
||||||
|
|
||||||
|
const approx = trigramDistance(profile1, profile2);
|
||||||
|
|
||||||
|
expect(approx).toBeGreaterThan(0.45);
|
||||||
|
expect(approx).toBeLessThan(0.55);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('triangle inequality', () => {
|
||||||
|
const A = trigramDistance(trigramProfile('metric'), trigramProfile('123ric'));
|
||||||
|
const B = trigramDistance(trigramProfile('123ric'), trigramProfile('123456'));
|
||||||
|
const C = trigramDistance(trigramProfile('metric'), trigramProfile('123456'));
|
||||||
|
|
||||||
|
expect(A + B).toBeGreaterThanOrEqual(C);
|
||||||
|
});
|
||||||
|
});
|
||||||
49
packages/stdlib/src/text/trigram-distance/index.ts
Normal file
49
packages/stdlib/src/text/trigram-distance/index.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
export type Trigrams = Map<string, number>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts trigrams from a text and returns a map of trigram to count
|
||||||
|
*
|
||||||
|
* @param {string} text The text to extract trigrams
|
||||||
|
* @returns {Trigrams} A map of trigram to count
|
||||||
|
*/
|
||||||
|
export function trigramProfile(text: string): Trigrams {
|
||||||
|
text = '\n\n' + text + '\n\n';
|
||||||
|
|
||||||
|
const trigrams = new Map<string, number>();
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length - 2; i++) {
|
||||||
|
const trigram = text.slice(i, i + 3);
|
||||||
|
const count = trigrams.get(trigram) ?? 0;
|
||||||
|
trigrams.set(trigram, count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trigrams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the trigram distance between two strings
|
||||||
|
*
|
||||||
|
* @param {Trigrams} left First text trigram profile
|
||||||
|
* @param {Trigrams} right Second text trigram profile
|
||||||
|
* @returns {number} The trigram distance between the two strings
|
||||||
|
*/
|
||||||
|
export function trigramDistance(left: Trigrams, right: Trigrams) {
|
||||||
|
let distance = -4;
|
||||||
|
let total = -4;
|
||||||
|
|
||||||
|
for (const [trigram, left_count] of left) {
|
||||||
|
total += left_count;
|
||||||
|
const right_count = right.get(trigram) ?? 0;
|
||||||
|
distance += Math.abs(left_count - right_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [trigram, right_count] of right) {
|
||||||
|
total += right_count;
|
||||||
|
const left_count = left.get(trigram) ?? 0;
|
||||||
|
distance += Math.abs(left_count - right_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance < 0) return 0;
|
||||||
|
|
||||||
|
return distance / total;
|
||||||
|
}
|
||||||
3
packages/stdlib/tsconfig.json
Normal file
3
packages/stdlib/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "@robonen/tsconfig/tsconfig.json",
|
||||||
|
}
|
||||||
24
packages/stdlib/vite.config.ts
Normal file
24
packages/stdlib/vite.config.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import dts from 'vite-plugin-dts';
|
||||||
|
import { resolve } from 'pathe';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
name: 'Stdlib',
|
||||||
|
fileName: 'stdlib',
|
||||||
|
entry: resolve(__dirname, './src/index.ts'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
dts({
|
||||||
|
insertTypesEntry: true,
|
||||||
|
exclude: '**/*.test.ts',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"description": "",
|
"description": "Base typescript configuration for projects",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"tsconfig",
|
"tsconfig",
|
||||||
"typescript",
|
"typescript",
|
||||||
@@ -22,5 +22,8 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"**tsconfig.json"
|
"**tsconfig.json"
|
||||||
]
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,8 @@
|
|||||||
"module": "Preserve",
|
"module": "Preserve",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"sourceMap": true,
|
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -27,10 +26,9 @@
|
|||||||
|
|
||||||
/* Library transpiling */
|
/* Library transpiling */
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
|
||||||
/* Library in monorepo */
|
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"declarationMap": true
|
"sourceMap": false,
|
||||||
|
"declarationMap": false
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
1172
pnpm-lock.yaml
generated
1172
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
10
tools.code-workspace
Normal file
10
tools.code-workspace
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "packages/stdlib"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user