diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index b46aabe..0000000 --- a/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "eslint-config-airbnb-es5", - "rules": { - "no-shadow": 0 - }, - "env": { - "mocha": true - } -} diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 9e7c66a..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,22 +0,0 @@ -checks: - javascript: true -build: - nodes: - analysis: - tests: - override: - - js-scrutinizer-run - - - command: eslint-run - use_website_config: false - environment: - node: - version: 8.11.1 - tests: true - coverage: - tests: - override: - - command: npm test - coverage: - file: coverage/clover.xml - format: clover \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3fb0230..0000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: node_js -node_js: - - "6" - - "node" - -script: - - npm test \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..37d88b9 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +export default [ + { + files: ['src/**/*.js', 'test/**/*.js'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + rules: { + 'no-unused-vars': 'warn', + 'no-var': 'error', + 'prefer-const': 'error', + }, + }, + { + files: ['test/**/*.js'], + languageOptions: { + globals: { + describe: 'readonly', + it: 'readonly', + }, + }, + }, +]; diff --git a/package.json b/package.json index 90440c3..775ab48 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "name": "floatify", - "version": "1.4.0", + "version": "2.0.0", "description": "Common helper to floatify strings", + "type": "module", + "exports": "./src/floatify.js", "main": "src/floatify.js", "directories": { "test": "test" @@ -14,14 +16,12 @@ }, "homepage": "https://github.com/pocketrocket/floatify#readme", "engines": { - "node": ">= 6.0.0" + "node": ">= 18.0.0" }, "scripts": { - "lint": "eslint src", - "test": "nyc --reporter=html --reporter=text --reporter=clover mocha test", - "prepublishOnly": "# also runs npm run prebuild", - "prebuild": "npm run test # also runs npm run pretest", - "pretest": "npm run lint" + "lint": "eslint src test", + "test": "node --test", + "coverage": "node --test --experimental-test-coverage" }, "keywords": [ "floatify", @@ -33,12 +33,7 @@ "numbers" ], "devDependencies": { - "babel-eslint": "^10.0.1", - "eslint": "^10.2.1", - "eslint-config-airbnb-es5": "^1.2.0", - "eslint-plugin-react": "^7.12.4", - "mocha": "^11.7.2", - "nyc": "^18.0.0" + "eslint": "^9.39.0" }, "author": "pocketrocket ", "contributors": [ diff --git a/src/floatify.js b/src/floatify.js index 024962f..8eddc2c 100644 --- a/src/floatify.js +++ b/src/floatify.js @@ -1,70 +1,62 @@ -'use strict'; +// floatify — locale-aware string → float parser. +// Handles dot/comma/space thousands and decimal separators and guesses the +// intended format. Behaviour is covered by the test suite in test/floatify.js. -var floatify = function floatify(str, preferDecimalSeparator) { +const floatify = (str, preferDecimalSeparator) => { // by default we prefer thousands separators - var preferThousandsSeparators = preferDecimalSeparator !== true; + const preferThousandsSeparators = preferDecimalSeparator !== true; - var toFloatFormat = function toFloatFormat(str, ts, ds) { - var string = str; - var decimalSeparator = ds || ''; - - string = string.split(ts || '').join(''); + const toFloatFormat = (input, ts, ds) => { + const decimalSeparator = ds || ''; + let string = input.split(ts || '').join(''); if (decimalSeparator !== '') { string = string.split(decimalSeparator).join('.'); } - return parseFloat(string); }; - var parseParts = function parseParts(str, ele, count, preferThousandsSeparators) { - var string = str; - var element = ele; - var parts = string.split(element); + const parseParts = (string, element, count, preferThousands) => { + const parts = string.split(element); + // Shared across the loop and the nested parsers (was var-hoisted before). + let current; + let left; + let leftVal; + let parseResult; - function parseMidPart() { + const parseMidPart = () => { if (current.length !== 3) { return Number.NaN; } - if (left.length > 3) { return Number.NaN; } - // no decision, continue return null; - } + }; - function parseEndPart() { - if ((leftVal === 0 || isNaN(leftVal) || left.length > 3)) { + const parseEndPart = () => { + if (leftVal === 0 || Number.isNaN(leftVal) || left.length > 3) { return toFloatFormat(string, '', element); } - if (current.length === 3) { - if (preferThousandsSeparators) { + if (preferThousands) { return toFloatFormat(string, element, ''); } return toFloatFormat(string, '', element); } - if (count === 1) { return toFloatFormat(string, '', element); } - return Number.NaN; - } + }; - for (var i = 1; i < parts.length; i++) { - var current = parts[i]; - var left = parts[i - 1]; - var leftVal = parseInt(left, 10); - var isLast = parts.length - 1 === i; - var parseResult; - - if (!isLast) { - parseResult = parseMidPart(); - } else { - parseResult = parseEndPart(); - } + for (let i = 1; i < parts.length; i += 1) { + current = parts[i]; + left = parts[i - 1]; + leftVal = parseInt(left, 10); + const isLast = parts.length - 1 === i; + + parseResult = isLast ? parseEndPart() : parseMidPart(); if (parseResult !== null) { break; @@ -73,28 +65,30 @@ var floatify = function floatify(str, preferDecimalSeparator) { return parseResult || Number.NaN; }; - var parse = function parse(str, preferThousandsSeparators) { - var string = str; - var spacePos; - var spaceSplit; - var spaceCount; - var dotPos; - var commaPos; - var lDotPos; - var lCommaPos; - var dotCount; - var commaCount; - - function parseMixedSeparators() { - // format is using dot and comma - - // last dot position - lDotPos = string.lastIndexOf('.'); - // last comma position - lCommaPos = string.lastIndexOf(','); - - // order of 1st dot -> comma must be same as last dot -> comma - // 123.123.123,123 -> ok 123.123,123.123 -> not ok + const parse = (input, preferThousands) => { + let string = input.trim(); + + const dotPos = string.indexOf('.'); + const commaPos = string.indexOf(','); + const spacePos = string.indexOf(' '); + + if (dotPos + commaPos + spacePos === -3) { + // life is good, no separators + return toFloatFormat(string); + } + + const spaceSplit = string.split(' '); + const spaceCount = spaceSplit.length - 1; + const dotCount = string.split('.').length - 1; + const commaCount = string.split(',').length - 1; + + // format is using dot and comma + const parseMixedSeparators = () => { + // order of 1st dot -> comma must match last dot -> comma + // 123.123.123,123 -> ok | 123.123,123.123 -> not ok + const lDotPos = string.lastIndexOf('.'); + const lCommaPos = string.lastIndexOf(','); + if (Math.sign(dotPos - commaPos) !== Math.sign(lDotPos - lCommaPos)) { return Number.NaN; } @@ -113,62 +107,38 @@ var floatify = function floatify(str, preferDecimalSeparator) { } // best guess: , is thousands separator and . is decimal point return toFloatFormat(string, '.', ','); - } + }; - function parseSingleSeparators() { + const parseSingleSeparators = () => { if (dotPos !== -1) { // only dot(s) in format - return parseParts(string, '.', dotCount, preferThousandsSeparators); + return parseParts(string, '.', dotCount, preferThousands); } - if (commaPos !== -1) { // only comma(s) in format - return parseParts(string, ',', commaCount, preferThousandsSeparators); + return parseParts(string, ',', commaCount, preferThousands); } - return toFloatFormat(string); - } + }; - function precheckFormat() { - // only combination of 2 separators allowed + const precheckFormat = () => { + // only a combination of 2 separators is allowed if (dotCount > 0 && commaCount > 0 && spaceCount > 0) { return false; } - - // if there is any separator (space, comma, dot) found more than once, - // all other must not be found more than once + // if any separator occurs more than once, the others must not if (dotCount > 1 && (commaCount > 1 || spaceCount > 1)) { return false; } - return !(commaCount > 1 && spaceCount > 1); - } - - string = string.trim(); - - // 1st dot position - dotPos = string.indexOf('.'); - // 1st comma position - commaPos = string.indexOf(','); - // 1st space position - spacePos = string.indexOf(' '); - - if (dotPos + commaPos + spacePos === -3) { - // life is good, no separators - return toFloatFormat(string); - } - - spaceSplit = string.split(' '); - spaceCount = spaceSplit.length - 1; - dotCount = string.split('.').length - 1; - commaCount = string.split(',').length - 1; + }; if (!precheckFormat()) { return Number.NaN; } if (spaceCount > 0) { - if (!string.match(/^(\d{1,3})?(\s\d{3})*([,\.]\d+)?$/)) { + if (!string.match(/^(\d{1,3})?(\s\d{3})*([,.]\d+)?$/)) { return Number.NaN; } string = spaceSplit.join(''); @@ -184,8 +154,4 @@ var floatify = function floatify(str, preferDecimalSeparator) { return parse(str, preferThousandsSeparators); }; -if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = floatify; - } -} +export default floatify; diff --git a/test/floatify.js b/test/floatify.js index 919531d..172721c 100644 --- a/test/floatify.js +++ b/test/floatify.js @@ -1,7 +1,6 @@ -'use strict'; - -var floatify = require('../src/floatify.js'); -var assert = require('assert'); +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import floatify from '../src/floatify.js'; const tests = [ {