feat: added support for more units
This commit is contained in:
parent
df26f0243b
commit
750bfb912f
3 changed files with 102 additions and 6 deletions
|
|
@ -29,8 +29,10 @@ const TEMP_UNIT_DEG = '°\\s*[FfCc]';
|
|||
const TEMP_UNIT_BARE = '[FfCc]';
|
||||
const WEIGHT_UNIT = '(?:kg|g|oz|lbs?|ounces?|pounds?)';
|
||||
const VOLUME_UNIT = '(?:cups?|tablespoons?|table\\s+spoons?|tbsp|teaspoons?|tsp|ml|mL|L|liters?|litres?|quarts?|gallons?|pints?|fl\\.?\\s*oz|fluid\\s+ounces?|parts\\s+by\\s+(?:volume|weight))';
|
||||
const TIME_UNIT = '(?:minutes?|mins?|hours?|hrs?|days?|weeks?|months?|seconds?|secs?)';
|
||||
const TIME_UNIT = '(?:minutes?|mins?|hours?|hrs?|days?|weeks?|months?|years?|seconds?|secs?)';
|
||||
const DIM_UNIT = '(?:inch(?:es)?|in\\.?|cm|mm)';
|
||||
const PRESSURE_UNIT = '(?:PSI|psi|kPa|bar)';
|
||||
const PH_UNIT = '(?:pH)';
|
||||
|
||||
// ─── Amount Parsing ───────────────────────────────────────
|
||||
|
||||
|
|
@ -192,6 +194,7 @@ function normalizeUnit(unit) {
|
|||
if (/^weeks?$/.test(u)) return 'week';
|
||||
if (/^months?$/.test(u)) return 'month';
|
||||
if (/^seconds?$/.test(u) || /^secs?$/.test(u)) return 'second';
|
||||
if (/^years?$/.test(u)) return 'year';
|
||||
|
||||
// Dimension units
|
||||
if (/^inch(es)?$/.test(u) || u === 'in') return 'inch';
|
||||
|
|
@ -199,6 +202,14 @@ function normalizeUnit(unit) {
|
|||
if (u === 'mm') return 'mm';
|
||||
if (/^f(oo|ee)t$/.test(u) || u === 'ft') return 'ft';
|
||||
|
||||
// Pressure
|
||||
if (u === 'psi') return 'psi';
|
||||
if (u === 'kpa') return 'kPa';
|
||||
if (u === 'bar') return 'bar';
|
||||
|
||||
// pH (normalizeUnit lowercases input, so 'pH' becomes 'ph')
|
||||
if (u === 'ph') return 'pH';
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
|
|
@ -208,8 +219,10 @@ function unitType(normalizedUnit) {
|
|||
if (['°F', '°C'].includes(normalizedUnit)) return 'temperature';
|
||||
if (['g', 'kg', 'oz', 'lb'].includes(normalizedUnit)) return 'weight';
|
||||
if (['cup', 'tablespoon', 'teaspoon', 'ml', 'L', 'quart', 'gallon', 'pint', 'fl oz', 'parts by volume', 'parts by weight'].includes(normalizedUnit)) return 'volume';
|
||||
if (['minute', 'hour', 'day', 'week', 'month', 'second'].includes(normalizedUnit)) return 'time';
|
||||
if (['minute', 'hour', 'day', 'week', 'month', 'year', 'second'].includes(normalizedUnit)) return 'time';
|
||||
if (['inch', 'cm', 'mm', 'ft'].includes(normalizedUnit)) return 'dimension';
|
||||
if (['psi', 'kPa', 'bar'].includes(normalizedUnit)) return 'pressure';
|
||||
if (normalizedUnit === 'pH') return 'pH';
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -535,6 +548,72 @@ function findCounts(text) {
|
|||
return results;
|
||||
}
|
||||
|
||||
// ─── Pressure Matcher ─────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Find pressure measurements in text.
|
||||
*
|
||||
* Handles: 11 PSI, 15 psi, 100 kPa
|
||||
*/
|
||||
function findPressures(text) {
|
||||
const results = [];
|
||||
|
||||
const pressureRe = new RegExp(
|
||||
`(${AMOUNT})\\s*(${PRESSURE_UNIT})\\b`,
|
||||
'gi'
|
||||
);
|
||||
|
||||
let m;
|
||||
while ((m = pressureRe.exec(text)) !== null) {
|
||||
const amount = parseAmount(m[1]);
|
||||
const unit = normalizeUnit(m[2]);
|
||||
results.push({
|
||||
match: m[0],
|
||||
index: m.index,
|
||||
type: 'pressure',
|
||||
amount,
|
||||
unit,
|
||||
approximate: typeof amount.approximate === 'boolean' ? amount.approximate : false,
|
||||
alt: null,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ─── pH Matcher ───────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Find pH measurements in text.
|
||||
*
|
||||
* Handles: 4.0 pH, 4.0 ph, 3.5 pH
|
||||
*/
|
||||
function findPH(text) {
|
||||
const results = [];
|
||||
|
||||
const phRe = new RegExp(
|
||||
`(${AMOUNT})\\s*(${PH_UNIT})\\b`,
|
||||
'g'
|
||||
);
|
||||
|
||||
let m;
|
||||
while ((m = phRe.exec(text)) !== null) {
|
||||
const amount = parseAmount(m[1]);
|
||||
const unit = normalizeUnit(m[2]);
|
||||
results.push({
|
||||
match: m[0],
|
||||
index: m.index,
|
||||
type: 'pH',
|
||||
amount,
|
||||
unit,
|
||||
approximate: typeof amount.approximate === 'boolean' ? amount.approximate : false,
|
||||
alt: null,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ─── Main Matcher ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
|
@ -552,6 +631,8 @@ function findAllMeasurements(text) {
|
|||
...findWeights(text),
|
||||
...findVolumes(text),
|
||||
...findTimes(text),
|
||||
...findPressures(text),
|
||||
...findPH(text),
|
||||
...findCounts(text),
|
||||
];
|
||||
|
||||
|
|
@ -595,6 +676,8 @@ module.exports = {
|
|||
findWeights,
|
||||
findVolumes,
|
||||
findTimes,
|
||||
findPressures,
|
||||
findPH,
|
||||
findCounts,
|
||||
|
||||
// Parsing utilities (exported for testing)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@ function unitLabel(unit, plural) {
|
|||
'day': plural ? 'days' : 'day',
|
||||
'week': plural ? 'weeks' : 'week',
|
||||
'month': plural ? 'months' : 'month',
|
||||
'year': plural ? 'years' : 'year',
|
||||
'second': plural ? 'seconds' : 'sec',
|
||||
'psi': 'PSI', 'kPa': 'kPa', 'bar': 'bar',
|
||||
'pH': 'pH',
|
||||
'parts by volume': 'parts by volume',
|
||||
'parts by weight': 'parts by weight',
|
||||
};
|
||||
|
|
@ -108,6 +111,10 @@ function toMetricValue(value, unit) {
|
|||
case 'inch': return { value: value * 2.54, unit: 'cm' };
|
||||
case 'cm': return { value, unit: 'cm' };
|
||||
case 'mm': return { value, unit: 'mm' };
|
||||
// Pressure
|
||||
case 'psi': return { value: value * 6.89476, unit: 'kPa' };
|
||||
case 'kPa': return { value, unit: 'kPa' };
|
||||
case 'bar': return { value: value * 100, unit: 'kPa' };
|
||||
default: return { value, unit };
|
||||
}
|
||||
}
|
||||
|
|
@ -137,6 +144,10 @@ function toImperialValue(value, unit) {
|
|||
case 'cm': return { value: value / 2.54, unit: 'inch' };
|
||||
case 'mm': return { value: value / 25.4, unit: 'inch' };
|
||||
case 'inch': return { value, unit: 'inch' };
|
||||
// Pressure
|
||||
case 'kPa': return { value: value / 6.89476, unit: 'psi' };
|
||||
case 'bar': return { value: value * 14.5038, unit: 'psi' };
|
||||
case 'psi': return { value, unit: 'psi' };
|
||||
default: return { value, unit };
|
||||
}
|
||||
}
|
||||
|
|
@ -317,6 +328,7 @@ function collapsedTime(value, unit) {
|
|||
if (unit === 'day') return formatValueUnit(value, 'day');
|
||||
if (unit === 'week') return formatValueUnit(value, 'week');
|
||||
if (unit === 'month') return formatValueUnit(value, 'month');
|
||||
if (unit === 'year') return formatValueUnit(value, 'year');
|
||||
if (unit === 'second') return formatValueUnit(value, 'second');
|
||||
const mins = toMinutes(value, unit);
|
||||
if (mins === null) return formatValueUnit(value, unit);
|
||||
|
|
@ -327,6 +339,7 @@ function expandedTime(value, unit) {
|
|||
if (unit === 'day') return formatValueUnit(value, 'day');
|
||||
if (unit === 'week') return formatValueUnit(value, 'week');
|
||||
if (unit === 'month') return formatValueUnit(value, 'month');
|
||||
if (unit === 'year') return formatValueUnit(value, 'year');
|
||||
if (unit === 'second') return formatValueUnit(value, 'second');
|
||||
const mins = toMinutes(value, unit);
|
||||
if (mins === null) return formatValueUnit(value, unit);
|
||||
|
|
@ -350,7 +363,7 @@ function generateTexts(measurement) {
|
|||
const { type, amount, unit, approximate } = measurement;
|
||||
|
||||
// Non-convertible units
|
||||
if (unit === 'parts by volume' || unit === 'parts by weight') {
|
||||
if (unit === 'parts by volume' || unit === 'parts by weight' || unit === 'pH') {
|
||||
const text = formatMeasurementText(amount, unit, approximate);
|
||||
return { defaultText: text, altText: text };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,6 @@ draft: true
|
|||
|
||||
#recipe #diet/plant_based #diet/vegetarian
|
||||
|
||||
[^1]: can be substituted with store bought kombucha for first scoby
|
||||
[^2]: tap water works but is slower and has a higher chance of failure due to chlorine/fluoride in water
|
||||
[^3]: sweet to sour balance matches [Kombucha Calibrator](/recipe/kombucha-calibrator/) or ph is below then 4.0
|
||||
[^1]: can be substituted with store bought kombucha for first scoby
|
||||
[^2]: tap water works but is slower and has a higher chance of failure due to chlorine/fluoride in water
|
||||
[^3]: sweet to sour balance matches [Kombucha Calibrator](/recipe/kombucha-calibrator/) is under a 4.0 pH
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue