{"id":19281249,"url":"https://github.com/zouloux/ecma-core","last_synced_at":"2025-08-22T17:17:21.875Z","repository":{"id":57711759,"uuid":"509790390","full_name":"zouloux/ecma-core","owner":"zouloux","description":null,"archived":false,"fork":false,"pushed_at":"2022-12-07T17:04:21.000Z","size":49,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-05T16:45:43.794Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zouloux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-02T15:23:32.000Z","updated_at":"2022-07-15T14:41:15.000Z","dependencies_parsed_at":"2023-01-23T18:31:05.882Z","dependency_job_id":null,"html_url":"https://github.com/zouloux/ecma-core","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zouloux%2Fecma-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zouloux%2Fecma-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zouloux%2Fecma-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zouloux%2Fecma-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zouloux","download_url":"https://codeload.github.com/zouloux/ecma-core/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240385713,"owners_count":19793060,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T21:22:07.835Z","updated_at":"2025-02-23T22:19:08.163Z","avatar_url":"https://github.com/zouloux.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ecma-core\n\nEcma-Core is an __Isomorphic__ and __0-dependency__ typed core functions for __Node__ and __browser__.\n\nLibrary size is about ![](./bits/index.es2017.min.js.svg).\n\nNo Browser or Node specific code in this lib ! Only __isomorphic__ helpers.\n\n---\n\n## Table of contents\n- \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n- List of available helpers\n  - \u003ca href=\"#operations\"\u003eOperations\u003c/a\u003e\n  - \u003ca href=\"#numbers\"\u003eNumbers\u003c/a\u003e\n  - \u003ca href=\"#paths\"\u003ePaths\u003c/a\u003e\n  - \u003ca href=\"#strings\"\u003eStrings\u003c/a\u003e\n  - \u003ca href=\"#structs\"\u003eStructs\u003c/a\u003e\n  - \u003ca href=\"#time\"\u003eTime\u003c/a\u003e\n  - \u003ca href=\"#some-types\"\u003eSome types\u003c/a\u003e\n- \u003ca href=\"#unpkg\"\u003eUnpkg usage\u003c/a\u003e\n---\n\n## Usage\nInstall\n```bash\nnpm i @zouloux/ecma-core\n```\nModule import\n```tsx\nimport { toHex, limitRange } from \"@zouloux/ecma-core\"\n```\nCommonJS import\n```tsx\nconst { toHex, limitRange } = require( \"@zouloux/ecma-core\" )\n```\n\n---\n\n## Operations\n\n#### No-op\n\u003e No-op is a do nothing handler, typed like any handler `(...rest) =\u003e any|void` for compatibility\n```tsx\naddEventListener(\"click\", noop) // do nothing\n```\n\n#### Compare with operator\n```tsx\nimport { compareWithOperator } from \"./operations\";\ncompareWithOperator(2, 5, \"\u003c\") // true\n```\n\u003e Available operators\n```tsx\nexport type TCompareOperators = '===' | '==' | '!==' | '!=' | '\u003e=' | '\u003e' | '\u003c=' | '\u003c';\n```\n\n---\n\n## Numbers\n\n#### To Hex\n```tsx\ntoHex(16) // 0xF\n```\n\n#### UIDs\n```tsx\ncreateUID( antiCollectionEntropy = 1, dateEntropy = 1, randomEntropy = 1 )\ncreateUID() // '4c80-489f7937-14f7'\ncreateUID( 0, 0, 1000 ) // only random based '5b3eb'\n```\n\n#### Maths\n```tsx\n// Limit a value into a range of min and max\nlimitRange( min, value, max )\nlimitRange( 0, -10, 1 ) // 0\nlimitRange( 0, +10, 1 ) // 1\n\n// Limite a value into a random of -max and +max\nsymmetricLimitRange( value, max )\nsymmetricLimitRange( 10, 2 ) // 2\nsymmetricLimitRange( -10, 2 ) // -2\n\n// Compute modulo compatible with negative values\npositiveModulo( base, modulo )\npositiveModulo( 11, 2 ) // 1\npositiveModulo( 11, -2 ) // -1\n\n// Move value with offset until 0 or max is reached and loop\ncircularRange( value, max, offset )\ncircularRange( 0, 10, 1 ) // 1\ncircularRange( 0, 10, -1 ) // 9\ncircularRange( 3, 10, -5 ) // 8\n```\n\n#### Geometry\n```tsx\n// Compute distance between 2 points\ndistance( x1, x2, y1, y2 )\ndistanceBetweenPoints( { x: number, y: number }, { x: number, y: number } )\n\n// Compute angle between 3 points\nangle3( [{x, y}, {x, y}, {x, y}] )\n\n// From randians to degrees \nradToDeg( angle )\n// From degrees to radians\ndegToRad( angle )\n\n// Limit angle between -Math.PI and + Math.PI \nlimitAngle( angle )\nlimitAngle( Math.PI * 3 ) == -Math.PI\n```\n\n#### Random\n```tsx\n// Random positive number from 0 to max\nrandomPositive( max )\n\n// Random number from min to max\nrandomRange( min, max )\n\n// Random number between -max and + max\nrandomSymmetricRange( max )\n\n// Random sign (-1 or +1)\nrandomSign()\n\n// Random boolean\nrandBoolean( threshold = .5 ) // true or false\nrandBoolean( threshold = .9 ) // Higher change to have true\nrandBoolean( threshold = .1 ) // Higher change to have false\n\nrandomPick([\"a\", \"b\"]) // a or b\nrandomPick([\"a\", \"b\", \"c\", \"d\"]) // Any value randomly\n\n// Pick a random value from an object\nconst familly = {\n\ta: { name: \"Pauline\" },\n\tb: { name: \"John\" },\n\tc: { name: \"Brune\" },\n}\nrandomPickFromObject( familly ) // { name: \"Brune\" }\n\n// Randomize an array\narrayShuffle([\"a\", \"b\", \"c\"]) // [\"b\", \"c\", \"a\"]\n\n// Functional value map, see doc in src/numbers.ts\nfunctionalValueMap( valueMap )\n```\n\n---\n\n## Paths\n\u003e Isomorphic path utils without having to import browserified \"path\" in the browser.\n```tsx\ngetFileFromPath(\"./dir/dir/file.ext\"); // file.ext\n\ngetBaseFromPath(\"./dir/dir/file.ext\"); // ./dir/dir/\n\nextractPathFromBase(\"/my/base/dir/file.html\", \"/my/base\") // /dir/file.html\n\nremoveExtensions(\"file.config.js\") // file.config\nremoveExtensions(\"file.config.js\", 2) // file\n\nextractExtensions(\"file.module.less\") // ['less', 'module']\nextractExtensions(\"a-folder/\") // [] \n```\n\n---\n\n## Strings\n#### Check if string is a number\n```tsx\nisNumber(\"12\") // true\nisNumber(\"\") // false\nisNumber(\"0.5\") // true\nisNumber(\"1e10\") // true\nisNumber(\"0xF1\") // true\nisNumber(\"#51\") // false\nisNumber(\"NaN\") // false\n```\n\n#### Zero fill\n\u003e Will prepend zeros to match a certain number a chars\n```tsx\nfunction zeroFill ( totalChars:number, number:number, placeholder = '0' ) : string {}\nzeroFill(2, 1) // \"01\"\nzeroFill(10, 1) // \"0000000001\"\nzeroFill(2, 20) // \"20\"\nzeroFill(4, 14) // \"014\"\n```\n\n#### Trailing and leading\n\u003e Add or remove leading or trailing char\n```tsx\nfunction trailing ( source:string, add = true, char = '/' ) : string {}\nfunction leading ( source:string, add = true, char = '/' ) : string {}\n````\n\n```tsx\ntrailing(\"/lib/test\") // /lib/test/\ntrailing(\"/lib/test/\", false) // /lib/test\nleading(\"lib/test\") // /lib/test\nleading(\"/lib/test\", false) // lib/test\nleading(trailing(\"lib/test\")) // /lib/test/\n```\n\n#### nl2br\n\u003e Same as PHP function, convert \\n new lines to \u003cbr\u003e\n```tsx\nnl2br(`\nHi !\n`) // \u003cbr\u003eHi!\u003cbr\u003e\n\n// And if you need auto closing tag : \nnl2br( multiline, '\u003cbr/\u003e') \n```\n\n#### Repeat a char\n```tsx\nrepeat(10, 'a') // \"aaaaaaaaaa\"\n```\n\n#### Indent\n```tsx\nfunction indent ( total:number, content = '', tabSize = 0 ) : string {}\nindent(3, \"Hello\", 2) \t// \"      Hello\" ( 3x2 spaces before the Hello string )\nindent(3, \"Hello\") \t\t// \"\t\t\tHello\" ( 3 tabs, because tabSize is 0 )\n```\n\n#### upperCaseFirst / lowerCaseFirst\n```tsx\nupperCaseFirst(\"courgette? Oui!\") // \"Courgette, Oui!\"\nlowerCaseFirst(\"Fromage? Oui!\") // \"fromage? Oui!\"\n```\n\n#### dashToCamelCase / camelToDashCase\n```tsx\ndashToCamelCase(\"my-string\") // \"myString\"\n\ncamelToDashCase(\"myString\") // my-string\ncamelToDashCase(\"myString\", \"_\") // my_string\ncamelToDashCase(\"myString\", \"_\", true) // MY_STRING\n```\n\n#### Slugs\n\u003e QuickSlug does not replace special chars\n```tsx\nquickSlug(\"This is a test\") // \"this-is-a-test\"\nquickSlug(\"Le fromage rapé\") // \"le-fromage-rapé\" \u003c- still have special chars\nquickSlug(\"With      spaces\") // \"with-spaces\" \u003c- no repeated dashes\n```\n\u003e Slugify is slower, but it converts special chars to their corresponding ASCII char\n```tsx\nslugify(\"This is a test\") // \"this-is-a-test\"\nslugify(\"Le fromage rapé\") // \"le-fromage-rape\" \u003c- spcial chars are replaced !\nslugify(\"With      spaces\") // \"with-spaces\" \u003c- no repeated dashes\nslugify(\"Weird_- -test-héééé-haça@_ test\") // \"weird_-test-heeee-haca_-test\" \u003c- safe string\n```\n\n#### Parse query string safely\n\u003e Number and boolean are parsed. No support for PHP like array notation.\n\u003e Last parameter will pollute first parameters\n```tsx\nparseQueryString(\"?test=string\u0026special=With%20spaces\u0026boolean=false\u0026flag\")\n// {\n// \ttest: \"string\",\n// \tspecial: \"With spaces\",\n// \tboolean: false,\n// \tflag: true\n// }\n```\n\n#### Parse booleans\n```tsx\nfunction parseBoolean ( booleanAsString:string|number, strict = true, areTrue = ['true'], areFalse = ['false'] ):boolean|null {}\n\nparseBoolean(\"true\") // true\nparseBoolean(\"false\") // false\nparseBoolean(\"bla\") // null\nparseBoolean(\"bla\", false) // false\nparseBoolean(\"y\") // null\nparseBoolean(\"n\") // null\n\nparseBooleanCLI(\"y\") // true\nparseBooleanCLI(\"yes\") // true\nparseBooleanCLI(\"N\") // false\nparseBooleanCLI(\"no\") // false\nparseBooleanCLI(\"bla\") // null\n```\n\n#### countStartingChars\n```tsx\ncountStartingChars(\"\thow many tabs ?\") // 1\ncountStartingChars(\"  how many spaces ?\", \" \") // 2\n```\n\n#### untab\n```tsx\nfunction testUntab () {\n\treturn untab(`\n\t\tRemove tabs\n\t\tFrom this multi-line text into a function\n\t`)\n}\nconst string = testUntab();\n//'Remove tabs\\nFrom this multi-line text into a function'\n```\n\n---\n## Structs\n\n```tsx\nforceArray(\"ok\") // [\"ok\"]\nforceArray([\"ok\"]) // [\"ok\"]\n```\n\n```tsx\narrayFrom(4).map( i =\u003e console.log(i) ) // 0, 1, 2, 3\narrayFrom(4, i =\u003e i * 2 ).map( i =\u003e console.log(i) ) // 0, 2, 4, 6\n````\n\n---\n## Time\n\n#### Delay in seconds\n```tsx\nawait delay( 2 ) // wait 2 seconds\n```\n\n#### Create delta counter\n\u003e Create a counter usable in tick-loops to have time based animations\n```tsx\nconst deltaCounter = createDeltaCounter( baseFrameRate = 1000 / 60 )\nfunction tickBasedLoop () {\n\tconst [ timeFactor, delta ] = deltaCounter()\n\t// timeFactor -\u003e 1 \t\tIf exactly ticked 1 frame later\n\t// timeFactor -\u003e .5 \tIf ticked too soon\n\t// timeFactor -\u003e 2 \t\tIf ticked too late (skipped a frame)\n\t// timeFactor -\u003e 1.222\tRealistic values are like this\n\t// delta -\u003e 16.333\t\tHow many ms since last tick ?\n}\n```\n\n---\n## Some types\n\u003e Types are exported from their respective module, but here are some examples :\n```tsx\ntype AnyHandler = (...rest) =\u003e any|void\ntype FunctionalFilter \u003cGType\u003e = ( r:GType ) =\u003e GType\ntype ScalarValue = ( string | number | boolean )\ntype ScalarRecord = Record\u003cstring, ScalarValue\u003e\n```\n\n---\n## Unpkg\n\nHelpers are available on UNPKG.\nImport it with a script tag to use it in the browser.\nSpecify version for better performances (will not check last version at each request).\n```html\n\u003cscript src=\"https://unpkg.com/@zouloux/ecma-core@0.2.0\"\u003e\u003c/script\u003e\n```\n\n- [Whole lib](https://unpkg.com/@zouloux/ecma-core) ![](./bits/index.es2017.min.js.svg)\n- [numbers only](https://unpkg.com/@zouloux/ecma-core/dist/numbers.es2017.min.js) ![](./bits/numbers.es2017.min.js.svg)\n- [operations only](https://unpkg.com/@zouloux/ecma-core/dist/operations.es2017.min.js) ![](./bits/operations.es2017.min.js.svg)\n- [paths only](https://unpkg.com/@zouloux/ecma-core/dist/paths.es2017.min.js) ![](./bits/paths.es2017.min.js.svg)\n- [strings only](https://unpkg.com/@zouloux/ecma-core/dist/strings.es2017.min.js) ![](./bits/strings.es2017.min.js.svg)\n- [structs only](https://unpkg.com/@zouloux/ecma-core/dist/structs.es2017.min.js) ![](./bits/structs.es2017.min.js.svg)\n- [time only](https://unpkg.com/@zouloux/ecma-core/dist/time.es2017.min.js) ![](./bits/time.es2017.min.js.svg)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzouloux%2Fecma-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzouloux%2Fecma-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzouloux%2Fecma-core/lists"}