uconvert 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # uconvert
2
+
3
+ Unit conversion utilities.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install uconvert
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### `convert(value, options)`
14
+
15
+ Converts a numeric value from one unit to another.
16
+
17
+ **Parameters:**
18
+
19
+ - `value` (number) — The value in the source unit.
20
+ - `options` (object):
21
+ - `fromUnits` — Source unit (use `MetricUnits` or `ImperialUnits`).
22
+ - `toUnits` — Target unit. Must be the same dimension as `fromUnits` (e.g. length ↔ length), or an error is thrown.
23
+ - `roundTo` (optional) — Number of decimal places to round the result. Omit to return the unrounded value.
24
+
25
+ **Returns:** The value in the target unit (optionally rounded).
26
+
27
+ **Example:**
28
+
29
+ ```ts
30
+ import { convert, MetricUnits, ImperialUnits } from "uconvert";
31
+
32
+ // 5 feet to meters, rounded to 2 decimals
33
+ convert(5, {
34
+ fromUnits: ImperialUnits.FT,
35
+ toUnits: MetricUnits.M,
36
+ roundTo: 2,
37
+ });
38
+ // => 1.52
39
+
40
+ // Same unit: returns value unchanged
41
+ convert(100, { fromUnits: MetricUnits.CM, toUnits: MetricUnits.CM });
42
+ // => 100
43
+ ```
44
+
45
+ ### `round(value, decimalPlaces?)`
46
+
47
+ Rounds a number to a given number of decimal places.
48
+
49
+ **Parameters:**
50
+
51
+ - `value` (number) — The number to round.
52
+ - `decimalPlaces` (optional) — Number of digits after the decimal point. Omit to return the value unchanged.
53
+
54
+ **Returns:** The rounded number (or the original number if `decimalPlaces` is omitted).
55
+
56
+ **Example:**
57
+
58
+ ```ts
59
+ import { round } from "uconvert";
60
+
61
+ round(1.2345, 2); // => 1.23
62
+ round(1.2345); // => 1.2345
63
+ ```
64
+
65
+ ## Exported types and enums
66
+
67
+ Use these with `convert` for type-safe unit arguments:
68
+
69
+ | Export | Description |
70
+ |--------|-------------|
71
+ | `ConvertOptions` | Options object for `convert`: `{ fromUnits, toUnits, roundTo? }`. |
72
+ | `Units` | Union type: `MetricUnits \| ImperialUnits`. |
73
+ | `MetricUnits` | Enum: `CM`, `M`, `KG`, `KM_H`. |
74
+ | `ImperialUnits` | Enum: `IN`, `FT`, `LB`, `MPH`. |
75
+ | `UnitSystem` | Enum: `METRIC`, `IMPERIAL`. |
76
+ | `Dimension` | Enum: `LENGTH`, `WEIGHT`, `SPEED`. |
77
+
78
+ `fromUnits` and `toUnits` must use the same dimension (e.g. both length, or both weight); otherwise `convert` throws.
79
+
80
+ ---
81
+
82
+ ## Height utility
83
+
84
+ The `height` object provides helpers for converting between centimeters and feet–inches and for parsing feet–inches strings.
85
+
86
+ **Import:**
87
+
88
+ ```ts
89
+ import { height } from "uconvert";
90
+ ```
91
+
92
+ ### `height.toFeetInches(valueInCm, roundTo?)`
93
+
94
+ Converts a height in centimeters to feet and inches.
95
+
96
+ **Parameters:**
97
+
98
+ - `valueInCm` (number) — Height in centimeters.
99
+ - `roundTo` (optional) — Number of decimal places for the inches part. Omit for unrounded.
100
+
101
+ **Returns:** A `FeetInches` tuple `[feet, inches]`.
102
+
103
+ **Example:**
104
+
105
+ ```ts
106
+ height.toFeetInches(170); // => [5, 6.93...]
107
+ height.toFeetInches(170, 1); // => [5, 6.9]
108
+ ```
109
+
110
+ ### `height.toCentimeters(feetInches)`
111
+
112
+ Converts a feet–inches tuple to centimeters.
113
+
114
+ **Parameters:**
115
+
116
+ - `feetInches` — A `FeetInches` tuple `[feet, inches]` (e.g. from `toFeetInches` or `parseFeetInches`).
117
+
118
+ **Returns:** Height in centimeters (number).
119
+
120
+ **Example:**
121
+
122
+ ```ts
123
+ height.toCentimeters([5, 10]); // => 177.8
124
+ ```
125
+
126
+ ### `height.parseFeetInches(input)`
127
+
128
+ Parses a string into a `[feet, inches]` tuple. Accepts formats like `"5 ft 10 in"`, `"5'10\""`, `"5 10"`, and variations with "feet"/"foot"/"inches"/"inch".
129
+
130
+ **Parameters:**
131
+
132
+ - `input` (string) — String to parse.
133
+
134
+ **Returns:** A `FeetInches` tuple `[feet, inches]`. Returns `[0, 0]` if parsing fails or input is not a string.
135
+
136
+ **Example:**
137
+
138
+ ```ts
139
+ height.parseFeetInches("5 ft 10 in"); // => [5, 10]
140
+ height.parseFeetInches("5'10\""); // => [5, 10]
141
+ height.parseFeetInches("6 2"); // => [6, 2]
142
+ ```
143
+
144
+ ### `FeetInches` type
145
+
146
+ Tuple type `[number, number]`: first element is feet, second is inches. Use it when passing or receiving values from `toFeetInches`, `toCentimeters`, and `parseFeetInches`.
147
+
148
+ ```ts
149
+ import { height, type FeetInches } from "uconvert";
150
+
151
+ const fi: FeetInches = height.toFeetInches(170);
152
+ height.toCentimeters(fi);
153
+ ```
package/dist/index.d.mts CHANGED
@@ -38,4 +38,13 @@ declare const convert: (value: number, { fromUnits, toUnits, roundTo }: ConvertO
38
38
 
39
39
  declare const round: (value: number, decimalPlaces?: number) => number;
40
40
 
41
- export { Dimension, ImperialUnits, MeasurementType, MetricUnits, UnitSystem, type Units, convert, round };
41
+ type FeetInches = [number, number];
42
+
43
+ declare class Height {
44
+ toFeetInches(valueInCm: number, roundTo?: number): FeetInches;
45
+ toCentimeters([feet, inches]: FeetInches): number;
46
+ parseFeetInches(input: string): FeetInches;
47
+ }
48
+ declare const height: Height;
49
+
50
+ export { type ConvertOptions, Dimension, ImperialUnits, MeasurementType, MetricUnits, UnitSystem, type Units, convert, height, round };
package/dist/index.d.ts CHANGED
@@ -38,4 +38,13 @@ declare const convert: (value: number, { fromUnits, toUnits, roundTo }: ConvertO
38
38
 
39
39
  declare const round: (value: number, decimalPlaces?: number) => number;
40
40
 
41
- export { Dimension, ImperialUnits, MeasurementType, MetricUnits, UnitSystem, type Units, convert, round };
41
+ type FeetInches = [number, number];
42
+
43
+ declare class Height {
44
+ toFeetInches(valueInCm: number, roundTo?: number): FeetInches;
45
+ toCentimeters([feet, inches]: FeetInches): number;
46
+ parseFeetInches(input: string): FeetInches;
47
+ }
48
+ declare const height: Height;
49
+
50
+ export { type ConvertOptions, Dimension, ImperialUnits, MeasurementType, MetricUnits, UnitSystem, type Units, convert, height, round };
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  MetricUnits: () => MetricUnits,
27
27
  UnitSystem: () => UnitSystem,
28
28
  convert: () => convert,
29
+ height: () => height_default,
29
30
  round: () => round
30
31
  });
31
32
  module.exports = __toCommonJS(index_exports);
@@ -139,6 +140,66 @@ var convert = (value, { fromUnits, toUnits, roundTo }) => {
139
140
  const factor = getFactor2(fromUnits, toUnits);
140
141
  return round(value * factor, roundTo);
141
142
  };
143
+
144
+ // src/services/height/height.utils.ts
145
+ var parseFeetInches = (input) => {
146
+ if (typeof input !== "string") return [0, 0];
147
+ const normalized = input.toLowerCase().replace(/[""]/g, "in").replace(/[''′]+/g, "ft").replace(/(feet|foot)/g, "ft").replace(/(inches|inch)/g, "in").replace(/[\s]+/g, " ").trim();
148
+ const parseNumber = (value) => parseFloat(value || "0");
149
+ const ftMatch = normalized.match(/([\d.]+)\s*ft/);
150
+ const inMatch = normalized.match(/([\d.]+)\s*in/);
151
+ const feet = parseNumber(ftMatch?.[1]);
152
+ const inches = parseNumber(inMatch?.[1]);
153
+ if (ftMatch && inMatch) {
154
+ return [feet, inches];
155
+ }
156
+ const [firstNumberString, secondNumberString] = normalized.match(
157
+ /[\d.]+/g
158
+ ) || ["0", "0"];
159
+ const firstNumber = parseNumber(firstNumberString);
160
+ const secondNumber = parseNumber(secondNumberString);
161
+ if (!ftMatch && !inMatch) {
162
+ return [firstNumber, secondNumber];
163
+ }
164
+ if (ftMatch && !inMatch) {
165
+ return [feet, secondNumber];
166
+ }
167
+ if (!ftMatch && inMatch) {
168
+ return [0, inches];
169
+ }
170
+ return [0, 0];
171
+ };
172
+ var heightUtils = {
173
+ parseFeetInches
174
+ };
175
+ var height_utils_default = heightUtils;
176
+
177
+ // src/services/height/height.ts
178
+ var { parseFeetInches: parseFeetInches2 } = height_utils_default;
179
+ var Height = class {
180
+ toFeetInches(valueInCm, roundTo) {
181
+ const totalInches = convert(valueInCm, {
182
+ fromUnits: "cm" /* CM */,
183
+ toUnits: "in" /* IN */,
184
+ roundTo
185
+ });
186
+ const feet = Math.floor(totalInches / 12);
187
+ const inches = round(totalInches - feet * 12, roundTo);
188
+ return [feet, inches];
189
+ }
190
+ toCentimeters([feet, inches]) {
191
+ const totalInches = feet * 12 + inches;
192
+ return convert(totalInches, {
193
+ fromUnits: "in" /* IN */,
194
+ toUnits: "cm" /* CM */
195
+ });
196
+ }
197
+ parseFeetInches(input) {
198
+ return parseFeetInches2(input);
199
+ }
200
+ };
201
+ var height = new Height();
202
+ var height_default = height;
142
203
  // Annotate the CommonJS export names for ESM import in node:
143
204
  0 && (module.exports = {
144
205
  Dimension,
@@ -147,6 +208,7 @@ var convert = (value, { fromUnits, toUnits, roundTo }) => {
147
208
  MetricUnits,
148
209
  UnitSystem,
149
210
  convert,
211
+ height,
150
212
  round
151
213
  });
152
214
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants/common/common.constants.ts","../src/utils/math/math.ts","../src/constants/conversion/conversion.constants.ts","../src/constants/strings/strings.ts","../src/utils/strings/strings.utils.ts","../src/utils/convert/convert.utils.ts","../src/utils/convert/convert.ts"],"sourcesContent":["export * from \"./constants/common/common.constants\";\nexport * from \"./utils/convert/convert\";\nexport * from \"./utils/math/math\";\n","export enum UnitSystem {\n METRIC = \"Si\",\n IMPERIAL = \"Imperial\",\n}\n\nexport enum Dimension {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n}\n\nexport enum MeasurementType {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n HEIGHT = \"height\",\n LENGTH_FEET = \"length_feet\",\n}\n\nexport enum MetricUnits {\n CM = \"cm\",\n M = \"m\",\n KG = \"kg\",\n KM_H = \"km/h\",\n}\n\nexport enum ImperialUnits {\n IN = \"in\",\n FT = \"ft\",\n LB = \"lb\",\n MPH = \"mph\",\n}\n\nexport type Units = MetricUnits | ImperialUnits;\n","export const round = (value: number, decimalPlaces?: number) => {\n if (decimalPlaces === undefined) return value;\n\n return parseFloat(value.toFixed(decimalPlaces));\n};\n","import {\n Dimension,\n ImperialUnits,\n MetricUnits,\n Units,\n} from \"../common/common.constants\";\n\n/** Factor to multiply value by to get value in canonical unit for that dimension. */\nexport const UNIT_TO_CANONICAL: Record<Units, number> = {\n [MetricUnits.CM]: 1,\n [MetricUnits.M]: 100,\n [ImperialUnits.IN]: 2.54,\n [ImperialUnits.FT]: 30.48,\n [MetricUnits.KG]: 1,\n [ImperialUnits.LB]: 0.45359237,\n [MetricUnits.KM_H]: 1,\n [ImperialUnits.MPH]: 1.609344,\n};\n\nexport const UNIT_DIMENSION: Record<Units, Dimension> = {\n [MetricUnits.CM]: Dimension.LENGTH,\n [MetricUnits.M]: Dimension.LENGTH,\n [ImperialUnits.IN]: Dimension.LENGTH,\n [ImperialUnits.FT]: Dimension.LENGTH,\n [MetricUnits.KG]: Dimension.WEIGHT,\n [ImperialUnits.LB]: Dimension.WEIGHT,\n [MetricUnits.KM_H]: Dimension.SPEED,\n [ImperialUnits.MPH]: Dimension.SPEED,\n};\n","const strings = {\n convertDimensionMismatch:\n \"Cannot convert between different dimensions: {0} ({1}) and {2} ({3})\",\n};\n\nexport default strings;\n","export const formatMessage = (message: string, ...args: any[]) =>\n message.replace(/{(\\d+)}/g, (match, number) =>\n args[number] !== undefined ? args[number] : match,\n );\n","import { Units } from \"../../constants/common/common.constants\";\nimport {\n UNIT_DIMENSION,\n UNIT_TO_CANONICAL,\n} from \"../../constants/conversion/conversion.constants\";\nimport strings from \"../../constants/strings/strings\";\nimport { round } from \"../math/math\";\nimport { formatMessage } from \"../strings/strings.utils\";\nimport { ConvertOptions } from \"./convert.types\";\n\nconst throwConversionError = ({ fromUnits, toUnits }: ConvertOptions) => {\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n throw new Error(\n formatMessage(\n strings.convertDimensionMismatch,\n fromUnits,\n fromDim,\n toUnits,\n toDim,\n ),\n );\n};\n\nconst getFactor = (fromUnits: Units, toUnits: Units) => {\n if (fromUnits === toUnits) return 1;\n\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n if (fromDim !== toDim) throwConversionError({ fromUnits, toUnits });\n\n const toCanonicalFrom = UNIT_TO_CANONICAL[fromUnits];\n const toCanonicalTo = UNIT_TO_CANONICAL[toUnits];\n return toCanonicalFrom / toCanonicalTo;\n};\n\nconst convertUtils = {\n getFactor,\n};\n\nexport default convertUtils;\n","import { round } from \"../math/math\";\nimport { ConvertOptions } from \"./convert.types\";\nimport utils from \"./convert.utils\";\n\nconst { getFactor } = utils;\n\nexport const convert = (\n value: number,\n { fromUnits, toUnits, roundTo }: ConvertOptions,\n) => {\n const factor = getFactor(fromUnits, toUnits);\n\n return round(value * factor, roundTo);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,cAAW;AAFD,SAAAA;AAAA,GAAA;AAKL,IAAK,YAAL,kBAAKC,eAAL;AACL,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,WAAQ;AAHE,SAAAA;AAAA,GAAA;AAML,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,WAAQ;AACR,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,iBAAc;AALJ,SAAAA;AAAA,GAAA;AAQL,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,aAAA,QAAK;AACL,EAAAA,aAAA,OAAI;AACJ,EAAAA,aAAA,QAAK;AACL,EAAAA,aAAA,UAAO;AAJG,SAAAA;AAAA,GAAA;AAOL,IAAK,gBAAL,kBAAKC,mBAAL;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,SAAM;AAJI,SAAAA;AAAA,GAAA;;;AC1BL,IAAM,QAAQ,CAAC,OAAe,kBAA2B;AAC9D,MAAI,kBAAkB,OAAW,QAAO;AAExC,SAAO,WAAW,MAAM,QAAQ,aAAa,CAAC;AAChD;;;ACIO,IAAM,oBAA2C;AAAA,EACtD,cAAe,GAAG;AAAA,EAClB,YAAc,GAAG;AAAA,EACjB,cAAiB,GAAG;AAAA,EACpB,cAAiB,GAAG;AAAA,EACpB,cAAe,GAAG;AAAA,EAClB,cAAiB,GAAG;AAAA,EACpB,kBAAiB,GAAG;AAAA,EACpB,gBAAkB,GAAG;AACvB;AAEO,IAAM,iBAA2C;AAAA,EACtD,cAAe;AAAA,EACf,YAAc;AAAA,EACd,cAAiB;AAAA,EACjB,cAAiB;AAAA,EACjB,cAAe;AAAA,EACf,cAAiB;AAAA,EACjB,kBAAiB;AAAA,EACjB,gBAAkB;AACpB;;;AC5BA,IAAM,UAAU;AAAA,EACd,0BACE;AACJ;AAEA,IAAO,kBAAQ;;;ACLR,IAAM,gBAAgB,CAAC,YAAoB,SAChD,QAAQ;AAAA,EAAQ;AAAA,EAAY,CAAC,OAAO,WAClC,KAAK,MAAM,MAAM,SAAY,KAAK,MAAM,IAAI;AAC9C;;;ACOF,IAAM,uBAAuB,CAAC,EAAE,WAAW,QAAQ,MAAsB;AACvE,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,eAAe,OAAO;AAEpC,QAAM,IAAI;AAAA,IACR;AAAA,MACE,gBAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,YAAY,CAAC,WAAkB,YAAmB;AACtD,MAAI,cAAc,QAAS,QAAO;AAElC,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,eAAe,OAAO;AAEpC,MAAI,YAAY,MAAO,sBAAqB,EAAE,WAAW,QAAQ,CAAC;AAElE,QAAM,kBAAkB,kBAAkB,SAAS;AACnD,QAAM,gBAAgB,kBAAkB,OAAO;AAC/C,SAAO,kBAAkB;AAC3B;AAEA,IAAM,eAAe;AAAA,EACnB;AACF;AAEA,IAAO,wBAAQ;;;ACtCf,IAAM,EAAE,WAAAC,WAAU,IAAI;AAEf,IAAM,UAAU,CACrB,OACA,EAAE,WAAW,SAAS,QAAQ,MAC3B;AACH,QAAM,SAASA,WAAU,WAAW,OAAO;AAE3C,SAAO,MAAM,QAAQ,QAAQ,OAAO;AACtC;","names":["UnitSystem","Dimension","MeasurementType","MetricUnits","ImperialUnits","getFactor"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants/common/common.constants.ts","../src/utils/math/math.ts","../src/constants/conversion/conversion.constants.ts","../src/constants/strings/strings.ts","../src/utils/strings/strings.utils.ts","../src/utils/convert/convert.utils.ts","../src/utils/convert/convert.ts","../src/services/height/height.utils.ts","../src/services/height/height.ts"],"sourcesContent":["export * from \"./constants/common/common.constants\";\nexport * from \"./utils/convert/convert\";\nexport * from \"./utils/convert/convert.types\";\nexport * from \"./utils/math/math\";\n\nexport { default as height } from \"./services/height/height\";\n","export enum UnitSystem {\n METRIC = \"Si\",\n IMPERIAL = \"Imperial\",\n}\n\nexport enum Dimension {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n}\n\nexport enum MeasurementType {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n HEIGHT = \"height\",\n LENGTH_FEET = \"length_feet\",\n}\n\nexport enum MetricUnits {\n CM = \"cm\",\n M = \"m\",\n KG = \"kg\",\n KM_H = \"km/h\",\n}\n\nexport enum ImperialUnits {\n IN = \"in\",\n FT = \"ft\",\n LB = \"lb\",\n MPH = \"mph\",\n}\n\nexport type Units = MetricUnits | ImperialUnits;\n","export const round = (value: number, decimalPlaces?: number) => {\n if (decimalPlaces === undefined) return value;\n\n return parseFloat(value.toFixed(decimalPlaces));\n};\n","import {\n Dimension,\n ImperialUnits,\n MetricUnits,\n Units,\n} from \"../common/common.constants\";\n\n/** Factor to multiply value by to get value in canonical unit for that dimension. */\nexport const UNIT_TO_CANONICAL: Record<Units, number> = {\n [MetricUnits.CM]: 1,\n [MetricUnits.M]: 100,\n [ImperialUnits.IN]: 2.54,\n [ImperialUnits.FT]: 30.48,\n [MetricUnits.KG]: 1,\n [ImperialUnits.LB]: 0.45359237,\n [MetricUnits.KM_H]: 1,\n [ImperialUnits.MPH]: 1.609344,\n};\n\nexport const UNIT_DIMENSION: Record<Units, Dimension> = {\n [MetricUnits.CM]: Dimension.LENGTH,\n [MetricUnits.M]: Dimension.LENGTH,\n [ImperialUnits.IN]: Dimension.LENGTH,\n [ImperialUnits.FT]: Dimension.LENGTH,\n [MetricUnits.KG]: Dimension.WEIGHT,\n [ImperialUnits.LB]: Dimension.WEIGHT,\n [MetricUnits.KM_H]: Dimension.SPEED,\n [ImperialUnits.MPH]: Dimension.SPEED,\n};\n","const strings = {\n convertDimensionMismatch:\n \"Cannot convert between different dimensions: {0} ({1}) and {2} ({3})\",\n};\n\nexport default strings;\n","export const formatMessage = (message: string, ...args: any[]) =>\n message.replace(/{(\\d+)}/g, (match, number) =>\n args[number] !== undefined ? args[number] : match,\n );\n","import { Units } from \"../../constants/common/common.constants\";\nimport {\n UNIT_DIMENSION,\n UNIT_TO_CANONICAL,\n} from \"../../constants/conversion/conversion.constants\";\nimport strings from \"../../constants/strings/strings\";\nimport { round } from \"../math/math\";\nimport { formatMessage } from \"../strings/strings.utils\";\nimport { ConvertOptions } from \"./convert.types\";\n\nconst throwConversionError = ({ fromUnits, toUnits }: ConvertOptions) => {\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n throw new Error(\n formatMessage(\n strings.convertDimensionMismatch,\n fromUnits,\n fromDim,\n toUnits,\n toDim,\n ),\n );\n};\n\nconst getFactor = (fromUnits: Units, toUnits: Units) => {\n if (fromUnits === toUnits) return 1;\n\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n if (fromDim !== toDim) throwConversionError({ fromUnits, toUnits });\n\n const toCanonicalFrom = UNIT_TO_CANONICAL[fromUnits];\n const toCanonicalTo = UNIT_TO_CANONICAL[toUnits];\n return toCanonicalFrom / toCanonicalTo;\n};\n\nconst convertUtils = {\n getFactor,\n};\n\nexport default convertUtils;\n","import { round } from \"../math/math\";\nimport { ConvertOptions } from \"./convert.types\";\nimport utils from \"./convert.utils\";\n\nconst { getFactor } = utils;\n\nexport const convert = (\n value: number,\n { fromUnits, toUnits, roundTo }: ConvertOptions,\n) => {\n const factor = getFactor(fromUnits, toUnits);\n\n return round(value * factor, roundTo);\n};\n","import { FeetInches } from \"./height.types\";\n\nconst parseFeetInches = (input: string): FeetInches => {\n // NOTE: this function is AI generated, but it's well tested and works as expected. And it's covered by unit tests.\n if (typeof input !== \"string\") return [0, 0];\n\n const normalized = input\n .toLowerCase()\n .replace(/[\"\"]/g, \"in\")\n .replace(/[''′]+/g, \"ft\")\n .replace(/(feet|foot)/g, \"ft\")\n .replace(/(inches|inch)/g, \"in\")\n .replace(/[\\s]+/g, \" \")\n .trim();\n\n const parseNumber = (value: string | undefined): number =>\n parseFloat(value || \"0\");\n\n const ftMatch = normalized.match(/([\\d.]+)\\s*ft/);\n const inMatch = normalized.match(/([\\d.]+)\\s*in/);\n\n const feet = parseNumber(ftMatch?.[1]);\n const inches = parseNumber(inMatch?.[1]);\n\n if (ftMatch && inMatch) {\n return [feet, inches];\n }\n\n const [firstNumberString, secondNumberString] = normalized.match(\n /[\\d.]+/g,\n ) || [\"0\", \"0\"];\n\n const firstNumber = parseNumber(firstNumberString);\n const secondNumber = parseNumber(secondNumberString);\n\n if (!ftMatch && !inMatch) {\n return [firstNumber, secondNumber];\n }\n\n if (ftMatch && !inMatch) {\n return [feet, secondNumber];\n }\n\n if (!ftMatch && inMatch) {\n return [0, inches];\n }\n\n return [0, 0];\n};\n\nconst heightUtils = {\n parseFeetInches,\n};\n\nexport default heightUtils;\n","import {\n ImperialUnits,\n MetricUnits,\n} from \"../../constants/common/common.constants\";\nimport { convert } from \"../../utils/convert/convert\";\nimport { round } from \"../../utils/math/math\";\nimport { FeetInches } from \"./height.types\";\nimport utils from \"./height.utils\";\n\nconst { parseFeetInches } = utils;\n\nclass Height {\n toFeetInches(valueInCm: number, roundTo?: number): FeetInches {\n const totalInches = convert(valueInCm, {\n fromUnits: MetricUnits.CM,\n toUnits: ImperialUnits.IN,\n roundTo,\n });\n\n const feet = Math.floor(totalInches / 12);\n const inches = round(totalInches - feet * 12, roundTo);\n\n return [feet, inches];\n }\n\n toCentimeters([feet, inches]: FeetInches): number {\n const totalInches = feet * 12 + inches;\n\n return convert(totalInches, {\n fromUnits: ImperialUnits.IN,\n toUnits: MetricUnits.CM,\n });\n }\n\n parseFeetInches(input: string): FeetInches {\n return parseFeetInches(input);\n }\n}\n\nconst height = new Height();\n\nexport default height;\n"],"mappings":"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","names":["UnitSystem","Dimension","MeasurementType","MetricUnits","ImperialUnits","getFactor","parseFeetInches"]}
package/dist/index.mjs CHANGED
@@ -107,6 +107,66 @@ var convert = (value, { fromUnits, toUnits, roundTo }) => {
107
107
  const factor = getFactor2(fromUnits, toUnits);
108
108
  return round(value * factor, roundTo);
109
109
  };
110
+
111
+ // src/services/height/height.utils.ts
112
+ var parseFeetInches = (input) => {
113
+ if (typeof input !== "string") return [0, 0];
114
+ const normalized = input.toLowerCase().replace(/[""]/g, "in").replace(/[''′]+/g, "ft").replace(/(feet|foot)/g, "ft").replace(/(inches|inch)/g, "in").replace(/[\s]+/g, " ").trim();
115
+ const parseNumber = (value) => parseFloat(value || "0");
116
+ const ftMatch = normalized.match(/([\d.]+)\s*ft/);
117
+ const inMatch = normalized.match(/([\d.]+)\s*in/);
118
+ const feet = parseNumber(ftMatch?.[1]);
119
+ const inches = parseNumber(inMatch?.[1]);
120
+ if (ftMatch && inMatch) {
121
+ return [feet, inches];
122
+ }
123
+ const [firstNumberString, secondNumberString] = normalized.match(
124
+ /[\d.]+/g
125
+ ) || ["0", "0"];
126
+ const firstNumber = parseNumber(firstNumberString);
127
+ const secondNumber = parseNumber(secondNumberString);
128
+ if (!ftMatch && !inMatch) {
129
+ return [firstNumber, secondNumber];
130
+ }
131
+ if (ftMatch && !inMatch) {
132
+ return [feet, secondNumber];
133
+ }
134
+ if (!ftMatch && inMatch) {
135
+ return [0, inches];
136
+ }
137
+ return [0, 0];
138
+ };
139
+ var heightUtils = {
140
+ parseFeetInches
141
+ };
142
+ var height_utils_default = heightUtils;
143
+
144
+ // src/services/height/height.ts
145
+ var { parseFeetInches: parseFeetInches2 } = height_utils_default;
146
+ var Height = class {
147
+ toFeetInches(valueInCm, roundTo) {
148
+ const totalInches = convert(valueInCm, {
149
+ fromUnits: "cm" /* CM */,
150
+ toUnits: "in" /* IN */,
151
+ roundTo
152
+ });
153
+ const feet = Math.floor(totalInches / 12);
154
+ const inches = round(totalInches - feet * 12, roundTo);
155
+ return [feet, inches];
156
+ }
157
+ toCentimeters([feet, inches]) {
158
+ const totalInches = feet * 12 + inches;
159
+ return convert(totalInches, {
160
+ fromUnits: "in" /* IN */,
161
+ toUnits: "cm" /* CM */
162
+ });
163
+ }
164
+ parseFeetInches(input) {
165
+ return parseFeetInches2(input);
166
+ }
167
+ };
168
+ var height = new Height();
169
+ var height_default = height;
110
170
  export {
111
171
  Dimension,
112
172
  ImperialUnits,
@@ -114,6 +174,7 @@ export {
114
174
  MetricUnits,
115
175
  UnitSystem,
116
176
  convert,
177
+ height_default as height,
117
178
  round
118
179
  };
119
180
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants/common/common.constants.ts","../src/utils/math/math.ts","../src/constants/conversion/conversion.constants.ts","../src/constants/strings/strings.ts","../src/utils/strings/strings.utils.ts","../src/utils/convert/convert.utils.ts","../src/utils/convert/convert.ts"],"sourcesContent":["export enum UnitSystem {\n METRIC = \"Si\",\n IMPERIAL = \"Imperial\",\n}\n\nexport enum Dimension {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n}\n\nexport enum MeasurementType {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n HEIGHT = \"height\",\n LENGTH_FEET = \"length_feet\",\n}\n\nexport enum MetricUnits {\n CM = \"cm\",\n M = \"m\",\n KG = \"kg\",\n KM_H = \"km/h\",\n}\n\nexport enum ImperialUnits {\n IN = \"in\",\n FT = \"ft\",\n LB = \"lb\",\n MPH = \"mph\",\n}\n\nexport type Units = MetricUnits | ImperialUnits;\n","export const round = (value: number, decimalPlaces?: number) => {\n if (decimalPlaces === undefined) return value;\n\n return parseFloat(value.toFixed(decimalPlaces));\n};\n","import {\n Dimension,\n ImperialUnits,\n MetricUnits,\n Units,\n} from \"../common/common.constants\";\n\n/** Factor to multiply value by to get value in canonical unit for that dimension. */\nexport const UNIT_TO_CANONICAL: Record<Units, number> = {\n [MetricUnits.CM]: 1,\n [MetricUnits.M]: 100,\n [ImperialUnits.IN]: 2.54,\n [ImperialUnits.FT]: 30.48,\n [MetricUnits.KG]: 1,\n [ImperialUnits.LB]: 0.45359237,\n [MetricUnits.KM_H]: 1,\n [ImperialUnits.MPH]: 1.609344,\n};\n\nexport const UNIT_DIMENSION: Record<Units, Dimension> = {\n [MetricUnits.CM]: Dimension.LENGTH,\n [MetricUnits.M]: Dimension.LENGTH,\n [ImperialUnits.IN]: Dimension.LENGTH,\n [ImperialUnits.FT]: Dimension.LENGTH,\n [MetricUnits.KG]: Dimension.WEIGHT,\n [ImperialUnits.LB]: Dimension.WEIGHT,\n [MetricUnits.KM_H]: Dimension.SPEED,\n [ImperialUnits.MPH]: Dimension.SPEED,\n};\n","const strings = {\n convertDimensionMismatch:\n \"Cannot convert between different dimensions: {0} ({1}) and {2} ({3})\",\n};\n\nexport default strings;\n","export const formatMessage = (message: string, ...args: any[]) =>\n message.replace(/{(\\d+)}/g, (match, number) =>\n args[number] !== undefined ? args[number] : match,\n );\n","import { Units } from \"../../constants/common/common.constants\";\nimport {\n UNIT_DIMENSION,\n UNIT_TO_CANONICAL,\n} from \"../../constants/conversion/conversion.constants\";\nimport strings from \"../../constants/strings/strings\";\nimport { round } from \"../math/math\";\nimport { formatMessage } from \"../strings/strings.utils\";\nimport { ConvertOptions } from \"./convert.types\";\n\nconst throwConversionError = ({ fromUnits, toUnits }: ConvertOptions) => {\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n throw new Error(\n formatMessage(\n strings.convertDimensionMismatch,\n fromUnits,\n fromDim,\n toUnits,\n toDim,\n ),\n );\n};\n\nconst getFactor = (fromUnits: Units, toUnits: Units) => {\n if (fromUnits === toUnits) return 1;\n\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n if (fromDim !== toDim) throwConversionError({ fromUnits, toUnits });\n\n const toCanonicalFrom = UNIT_TO_CANONICAL[fromUnits];\n const toCanonicalTo = UNIT_TO_CANONICAL[toUnits];\n return toCanonicalFrom / toCanonicalTo;\n};\n\nconst convertUtils = {\n getFactor,\n};\n\nexport default convertUtils;\n","import { round } from \"../math/math\";\nimport { ConvertOptions } from \"./convert.types\";\nimport utils from \"./convert.utils\";\n\nconst { getFactor } = utils;\n\nexport const convert = (\n value: number,\n { fromUnits, toUnits, roundTo }: ConvertOptions,\n) => {\n const factor = getFactor(fromUnits, toUnits);\n\n return round(value * factor, roundTo);\n};\n"],"mappings":";AAAO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,cAAW;AAFD,SAAAA;AAAA,GAAA;AAKL,IAAK,YAAL,kBAAKC,eAAL;AACL,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,WAAQ;AAHE,SAAAA;AAAA,GAAA;AAML,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,WAAQ;AACR,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,iBAAc;AALJ,SAAAA;AAAA,GAAA;AAQL,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,aAAA,QAAK;AACL,EAAAA,aAAA,OAAI;AACJ,EAAAA,aAAA,QAAK;AACL,EAAAA,aAAA,UAAO;AAJG,SAAAA;AAAA,GAAA;AAOL,IAAK,gBAAL,kBAAKC,mBAAL;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,QAAK;AACL,EAAAA,eAAA,SAAM;AAJI,SAAAA;AAAA,GAAA;;;AC1BL,IAAM,QAAQ,CAAC,OAAe,kBAA2B;AAC9D,MAAI,kBAAkB,OAAW,QAAO;AAExC,SAAO,WAAW,MAAM,QAAQ,aAAa,CAAC;AAChD;;;ACIO,IAAM,oBAA2C;AAAA,EACtD,cAAe,GAAG;AAAA,EAClB,YAAc,GAAG;AAAA,EACjB,cAAiB,GAAG;AAAA,EACpB,cAAiB,GAAG;AAAA,EACpB,cAAe,GAAG;AAAA,EAClB,cAAiB,GAAG;AAAA,EACpB,kBAAiB,GAAG;AAAA,EACpB,gBAAkB,GAAG;AACvB;AAEO,IAAM,iBAA2C;AAAA,EACtD,cAAe;AAAA,EACf,YAAc;AAAA,EACd,cAAiB;AAAA,EACjB,cAAiB;AAAA,EACjB,cAAe;AAAA,EACf,cAAiB;AAAA,EACjB,kBAAiB;AAAA,EACjB,gBAAkB;AACpB;;;AC5BA,IAAM,UAAU;AAAA,EACd,0BACE;AACJ;AAEA,IAAO,kBAAQ;;;ACLR,IAAM,gBAAgB,CAAC,YAAoB,SAChD,QAAQ;AAAA,EAAQ;AAAA,EAAY,CAAC,OAAO,WAClC,KAAK,MAAM,MAAM,SAAY,KAAK,MAAM,IAAI;AAC9C;;;ACOF,IAAM,uBAAuB,CAAC,EAAE,WAAW,QAAQ,MAAsB;AACvE,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,eAAe,OAAO;AAEpC,QAAM,IAAI;AAAA,IACR;AAAA,MACE,gBAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,YAAY,CAAC,WAAkB,YAAmB;AACtD,MAAI,cAAc,QAAS,QAAO;AAElC,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,eAAe,OAAO;AAEpC,MAAI,YAAY,MAAO,sBAAqB,EAAE,WAAW,QAAQ,CAAC;AAElE,QAAM,kBAAkB,kBAAkB,SAAS;AACnD,QAAM,gBAAgB,kBAAkB,OAAO;AAC/C,SAAO,kBAAkB;AAC3B;AAEA,IAAM,eAAe;AAAA,EACnB;AACF;AAEA,IAAO,wBAAQ;;;ACtCf,IAAM,EAAE,WAAAC,WAAU,IAAI;AAEf,IAAM,UAAU,CACrB,OACA,EAAE,WAAW,SAAS,QAAQ,MAC3B;AACH,QAAM,SAASA,WAAU,WAAW,OAAO;AAE3C,SAAO,MAAM,QAAQ,QAAQ,OAAO;AACtC;","names":["UnitSystem","Dimension","MeasurementType","MetricUnits","ImperialUnits","getFactor"]}
1
+ {"version":3,"sources":["../src/constants/common/common.constants.ts","../src/utils/math/math.ts","../src/constants/conversion/conversion.constants.ts","../src/constants/strings/strings.ts","../src/utils/strings/strings.utils.ts","../src/utils/convert/convert.utils.ts","../src/utils/convert/convert.ts","../src/services/height/height.utils.ts","../src/services/height/height.ts"],"sourcesContent":["export enum UnitSystem {\n METRIC = \"Si\",\n IMPERIAL = \"Imperial\",\n}\n\nexport enum Dimension {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n}\n\nexport enum MeasurementType {\n LENGTH = \"length\",\n WEIGHT = \"weight\",\n SPEED = \"speed\",\n HEIGHT = \"height\",\n LENGTH_FEET = \"length_feet\",\n}\n\nexport enum MetricUnits {\n CM = \"cm\",\n M = \"m\",\n KG = \"kg\",\n KM_H = \"km/h\",\n}\n\nexport enum ImperialUnits {\n IN = \"in\",\n FT = \"ft\",\n LB = \"lb\",\n MPH = \"mph\",\n}\n\nexport type Units = MetricUnits | ImperialUnits;\n","export const round = (value: number, decimalPlaces?: number) => {\n if (decimalPlaces === undefined) return value;\n\n return parseFloat(value.toFixed(decimalPlaces));\n};\n","import {\n Dimension,\n ImperialUnits,\n MetricUnits,\n Units,\n} from \"../common/common.constants\";\n\n/** Factor to multiply value by to get value in canonical unit for that dimension. */\nexport const UNIT_TO_CANONICAL: Record<Units, number> = {\n [MetricUnits.CM]: 1,\n [MetricUnits.M]: 100,\n [ImperialUnits.IN]: 2.54,\n [ImperialUnits.FT]: 30.48,\n [MetricUnits.KG]: 1,\n [ImperialUnits.LB]: 0.45359237,\n [MetricUnits.KM_H]: 1,\n [ImperialUnits.MPH]: 1.609344,\n};\n\nexport const UNIT_DIMENSION: Record<Units, Dimension> = {\n [MetricUnits.CM]: Dimension.LENGTH,\n [MetricUnits.M]: Dimension.LENGTH,\n [ImperialUnits.IN]: Dimension.LENGTH,\n [ImperialUnits.FT]: Dimension.LENGTH,\n [MetricUnits.KG]: Dimension.WEIGHT,\n [ImperialUnits.LB]: Dimension.WEIGHT,\n [MetricUnits.KM_H]: Dimension.SPEED,\n [ImperialUnits.MPH]: Dimension.SPEED,\n};\n","const strings = {\n convertDimensionMismatch:\n \"Cannot convert between different dimensions: {0} ({1}) and {2} ({3})\",\n};\n\nexport default strings;\n","export const formatMessage = (message: string, ...args: any[]) =>\n message.replace(/{(\\d+)}/g, (match, number) =>\n args[number] !== undefined ? args[number] : match,\n );\n","import { Units } from \"../../constants/common/common.constants\";\nimport {\n UNIT_DIMENSION,\n UNIT_TO_CANONICAL,\n} from \"../../constants/conversion/conversion.constants\";\nimport strings from \"../../constants/strings/strings\";\nimport { round } from \"../math/math\";\nimport { formatMessage } from \"../strings/strings.utils\";\nimport { ConvertOptions } from \"./convert.types\";\n\nconst throwConversionError = ({ fromUnits, toUnits }: ConvertOptions) => {\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n throw new Error(\n formatMessage(\n strings.convertDimensionMismatch,\n fromUnits,\n fromDim,\n toUnits,\n toDim,\n ),\n );\n};\n\nconst getFactor = (fromUnits: Units, toUnits: Units) => {\n if (fromUnits === toUnits) return 1;\n\n const fromDim = UNIT_DIMENSION[fromUnits];\n const toDim = UNIT_DIMENSION[toUnits];\n\n if (fromDim !== toDim) throwConversionError({ fromUnits, toUnits });\n\n const toCanonicalFrom = UNIT_TO_CANONICAL[fromUnits];\n const toCanonicalTo = UNIT_TO_CANONICAL[toUnits];\n return toCanonicalFrom / toCanonicalTo;\n};\n\nconst convertUtils = {\n getFactor,\n};\n\nexport default convertUtils;\n","import { round } from \"../math/math\";\nimport { ConvertOptions } from \"./convert.types\";\nimport utils from \"./convert.utils\";\n\nconst { getFactor } = utils;\n\nexport const convert = (\n value: number,\n { fromUnits, toUnits, roundTo }: ConvertOptions,\n) => {\n const factor = getFactor(fromUnits, toUnits);\n\n return round(value * factor, roundTo);\n};\n","import { FeetInches } from \"./height.types\";\n\nconst parseFeetInches = (input: string): FeetInches => {\n // NOTE: this function is AI generated, but it's well tested and works as expected. And it's covered by unit tests.\n if (typeof input !== \"string\") return [0, 0];\n\n const normalized = input\n .toLowerCase()\n .replace(/[\"\"]/g, \"in\")\n .replace(/[''′]+/g, \"ft\")\n .replace(/(feet|foot)/g, \"ft\")\n .replace(/(inches|inch)/g, \"in\")\n .replace(/[\\s]+/g, \" \")\n .trim();\n\n const parseNumber = (value: string | undefined): number =>\n parseFloat(value || \"0\");\n\n const ftMatch = normalized.match(/([\\d.]+)\\s*ft/);\n const inMatch = normalized.match(/([\\d.]+)\\s*in/);\n\n const feet = parseNumber(ftMatch?.[1]);\n const inches = parseNumber(inMatch?.[1]);\n\n if (ftMatch && inMatch) {\n return [feet, inches];\n }\n\n const [firstNumberString, secondNumberString] = normalized.match(\n /[\\d.]+/g,\n ) || [\"0\", \"0\"];\n\n const firstNumber = parseNumber(firstNumberString);\n const secondNumber = parseNumber(secondNumberString);\n\n if (!ftMatch && !inMatch) {\n return [firstNumber, secondNumber];\n }\n\n if (ftMatch && !inMatch) {\n return [feet, secondNumber];\n }\n\n if (!ftMatch && inMatch) {\n return [0, inches];\n }\n\n return [0, 0];\n};\n\nconst heightUtils = {\n parseFeetInches,\n};\n\nexport default heightUtils;\n","import {\n ImperialUnits,\n MetricUnits,\n} from \"../../constants/common/common.constants\";\nimport { convert } from \"../../utils/convert/convert\";\nimport { round } from \"../../utils/math/math\";\nimport { FeetInches } from \"./height.types\";\nimport utils from \"./height.utils\";\n\nconst { parseFeetInches } = utils;\n\nclass Height {\n toFeetInches(valueInCm: number, roundTo?: number): FeetInches {\n const totalInches = convert(valueInCm, {\n fromUnits: MetricUnits.CM,\n toUnits: ImperialUnits.IN,\n roundTo,\n });\n\n const feet = Math.floor(totalInches / 12);\n const inches = round(totalInches - feet * 12, roundTo);\n\n return [feet, inches];\n }\n\n toCentimeters([feet, inches]: FeetInches): number {\n const totalInches = feet * 12 + inches;\n\n return convert(totalInches, {\n fromUnits: ImperialUnits.IN,\n toUnits: MetricUnits.CM,\n });\n }\n\n parseFeetInches(input: string): FeetInches {\n return parseFeetInches(input);\n }\n}\n\nconst height = new Height();\n\nexport default height;\n"],"mappings":"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","names":["UnitSystem","Dimension","MeasurementType","MetricUnits","ImperialUnits","getFactor","parseFeetInches"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uconvert",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -19,7 +19,8 @@
19
19
  "example": "npm run build && node example/run.mjs",
20
20
  "example:cjs": "node example/run.cjs",
21
21
  "format": "prettier --write .",
22
- "test": "echo \"Error: no test specified\" && exit 1"
22
+ "postinstall": "husky",
23
+ "test": "jest"
23
24
  },
24
25
  "repository": {
25
26
  "type": "git",
@@ -33,8 +34,15 @@
33
34
  "url": "https://github.com/AndrewTkachuk42/uconvert/issues"
34
35
  },
35
36
  "homepage": "https://github.com/AndrewTkachuk42/uconvert#readme",
37
+ "overrides": {
38
+ "minimatch": "^10.2.2"
39
+ },
36
40
  "devDependencies": {
41
+ "@types/jest": "^29.5.14",
42
+ "husky": "^9.1.7",
43
+ "jest": "^30.2.0",
37
44
  "prettier": "^3.4.2",
45
+ "ts-jest": "^29.4.6",
38
46
  "prettier-plugin-organize-imports": "^4.1.0",
39
47
  "tsup": "^8.5.1",
40
48
  "typescript": "^5.9.3"