diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 240b092..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "env": { "browser": true }, - "extends": "eslint:recommended", - "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, - "rules": { - "block-scoped-var": "error", - "consistent-return": "error", - "eqeqeq": "error", - "guard-for-in": "error", - "no-bitwise": "error", - "no-caller": "error", - "no-extra-parens": "off", - "no-extend-native": "error", - "no-loop-func": "error", - "no-new": "error", - "no-param-reassign": "error", - "no-return-assign": "error", - "no-sequences": "error", - "no-unused-expressions": "error", - "no-use-before-define": "error", - "no-undef": "error", - "no-eq-null": "error", - "radix": ["error", "always"], - "indent": ["error", 2, { "SwitchCase": 1 }], - "quotes": ["error", "double"], - "semi": ["error", "always"], - "strict": ["error", "global"] - } -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 438f872..1e80a5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,29 +11,30 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 + + - name: Set up Node toolchain + uses: actions/setup-node@v7 + with: + node-version: "24.x" - name: Set up PureScript toolchain uses: purescript-contrib/setup-purescript@main with: purescript: "unstable" purs-tidy: "latest" + spago: "latest" - name: Cache PureScript dependencies - uses: actions/cache@v2 + uses: actions/cache@v6 with: - key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}-2 + key: ${{ runner.os }}-spago-${{ hashFiles('spago.yaml', 'spago.lock') }} path: | .spago output - - name: Set up Node toolchain - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Cache NPM dependencies - uses: actions/cache@v2 + uses: actions/cache@v6 env: cache-name: cache-node-modules with: @@ -55,12 +56,3 @@ jobs: - name: Check formatting run: purs-tidy check src test - - - name: Verify Bower & Pulp - run: | - npm install bower pulp@16.0.0-0 - npx bower install - npx pulp build -- --censor-lib --strict - if [ -d "test" ]; then - npx pulp test - fi diff --git a/.gitignore b/.gitignore index 6a45203..6dda900 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,11 @@ !.github !.editorconfig !.tidyrc.json -!.eslintrc.json output generated-docs -bower_components node_modules package-lock.json *.lock +!spago.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 9644842..a65d25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,24 @@ Notable changes to this project are documented in this file. The format is based ## [Unreleased] Breaking changes: +- The day-of-week token is now `d` instead of `E`; numeric weekdays remain Monday = 1 through Sunday = 7. (#87 by @garyb) +- The `a` token now prints lowercase `am`/`pm`; use `A` for uppercase `AM`/`PM`. Both parse either case. `Meridiem` still prints uppercase; `MeridiemLowerCase` prints lowercase. +- `FormatterCommand` has new `MonthDigit` and `MeridiemLowerCase` constructors; update exhaustive matches. - DateTime Formatter Placeholders that contain any valid format characters are now escaped via square brackets. Square brackets are not valid Placeholder characters. (#74 by @ntwilson) - Support for different separators ([PR](https://github.com/purescript-contrib/purescript-formatters/pull/86) by @carstenkoenig) New features: +- Added support for single-digit months [#69](https://github.com/purescript-contrib/purescript-formatters/pull/69) (@vapaj) +- Added support for lower-case meridiem via `MeridiemLowerCase` [#67](https://github.com/purescript-contrib/purescript-formatters/pull/67) (@fsoikin) Bugfixes: +- Escape `X` and `d` in literal placeholders when printing DateTime formatters. (#87 by @garyb) +- Parse numbers without intermediate 32-bit integer overflow, and retain extra fractional digits at their correct scale. Minimum digit widths are unchanged. (#16) Other improvements: - Added module documentation for Data.Formatter.DateTime and Data.Formatter.Number (#73 by @ntwilson) +- Migrate to registry-backed Spago 1.0 and update repository tooling. ## [v7.0.0](https://github.com/purescript-contrib/purescript-formatters/releases/tag/v7.0.0) - 2022-04-28 diff --git a/README.md b/README.md index 6c9744f..205040e 100644 --- a/README.md +++ b/README.md @@ -57,15 +57,17 @@ This is a subset of common format/parse strings currently supported. + `YY` - 2 digit year ("99") + `MMMM` - Full Month ("January") + `MMM` - Short Month ("Jan") ++ `M` - Unpadded Month ("1") + `DD` - Padded Day ("02") + `D` - Day of month ("2") + `X` - Unix Timestamp ("1506875681") -+ `E` - Day of Week ("2") ++ `d` - Day of Week ("2", Monday = 1, Sunday = 7) + `dddd` - DOW Name ("Monday") + `ddd` - DOW Name Short ("Mon") + `HH` - 24 Hour ("13") + `hh` - 12 Hour ("1") + `a` - Meridiem ("am"/"pm") ++ `A` - Meridiem ("AM"/"PM") + `mm` - Minutes Padded ("02") + `m` - Minutes ("2") + `ss` - Seconds Padded ("02") diff --git a/bower.json b/bower.json deleted file mode 100644 index c7ecb82..0000000 --- a/bower.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "purescript-formatters", - "homepage": "https://github.com/slamdata/purescript-formatters", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/purescript-contrib/purescript-formatters.git" - }, - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "test", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-datetime": "^6.0.0", - "purescript-fixed-points": "^7.0.0", - "purescript-lists": "^7.0.0", - "purescript-numbers": "^9.0.0", - "purescript-parsing": "^9.0.0", - "purescript-prelude": "^6.0.0", - "purescript-transformers": "^6.0.0" - }, - "devDependencies": { - "purescript-aff": "^7.0.0", - "purescript-assert": "^6.0.0", - "purescript-console": "^6.0.0", - "purescript-effect": "^4.0.0" - } -} diff --git a/package.json b/package.json index ad3ecca..1009264 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "private": true, "scripts": { - "build": "eslint src && spago build --purs-args '--censor-lib --strict'", - "test": "spago test --no-install" + "build": "oxlint src && spago build --strict --pedantic-packages", + "test": "spago test --strict --pedantic-packages" }, "devDependencies": { - "eslint": "^7.6.0" + "oxlint": "^1.87.0" } } diff --git a/packages.dhall b/packages.dhall deleted file mode 100644 index 27e3d68..0000000 --- a/packages.dhall +++ /dev/null @@ -1,4 +0,0 @@ -let upstream = - https://raw.githubusercontent.com/purescript/package-sets/psc-0.15.7-20230216/src/packages.dhall - -in upstream diff --git a/spago.dhall b/spago.dhall deleted file mode 100644 index 08b5493..0000000 --- a/spago.dhall +++ /dev/null @@ -1,29 +0,0 @@ -{ name = "formatters" -, dependencies = - [ "aff" - , "arrays" - , "assert" - , "bifunctors" - , "console" - , "control" - , "datetime" - , "effect" - , "either" - , "enums" - , "foldable-traversable" - , "integers" - , "lists" - , "maybe" - , "newtype" - , "numbers" - , "ordered-collections" - , "parsing" - , "partial" - , "prelude" - , "strings" - , "transformers" - , "tuples" - ] -, packages = ./packages.dhall -, sources = [ "src/**/*.purs", "test/**/*.purs" ] -} diff --git a/spago.lock b/spago.lock new file mode 100644 index 0000000..2eb2b41 --- /dev/null +++ b/spago.lock @@ -0,0 +1,1355 @@ +{ + "workspace": { + "packages": { + "formatters": { + "path": "./", + "core": { + "dependencies": [ + { + "arrays": ">=7.0.0 <8.0.0" + }, + { + "bifunctors": ">=6.0.0 <7.0.0" + }, + { + "control": ">=6.0.0 <7.0.0" + }, + { + "datetime": ">=6.0.0 <7.0.0" + }, + { + "either": ">=6.0.0 <7.0.0" + }, + { + "enums": ">=6.0.0 <7.0.0" + }, + { + "foldable-traversable": ">=6.0.0 <7.0.0" + }, + { + "integers": ">=6.0.0 <7.0.0" + }, + { + "lists": ">=7.0.0 <8.0.0" + }, + { + "maybe": ">=6.0.0 <7.0.0" + }, + { + "newtype": ">=5.0.0 <6.0.0" + }, + { + "numbers": ">=9.0.0 <10.0.0" + }, + { + "ordered-collections": ">=3.0.0 <4.0.0" + }, + { + "parsing": ">=9.0.0 <12.0.0" + }, + { + "partial": ">=4.0.0 <5.0.0" + }, + { + "prelude": ">=6.0.0 <7.0.0" + }, + { + "strings": ">=6.0.0 <7.0.0" + }, + { + "transformers": ">=6.0.0 <7.0.0" + }, + { + "tuples": ">=7.0.0 <8.0.0" + } + ] + }, + "test": { + "dependencies": [ + "aff", + "assert", + "console", + "effect" + ] + } + } + }, + "package_set": { + "address": { + "registry": "81.4.0" + }, + "compiler": ">=0.15.15 <0.16.0", + "content": { + "abc-parser": "2.1.0", + "ace": "9.1.0", + "address-rfc2821": "0.1.1", + "aff": "8.0.0", + "aff-bus": "6.0.0", + "aff-coroutines": "9.0.0", + "aff-promise": "4.0.0", + "aff-retry": "2.0.0", + "affjax": "13.0.0", + "affjax-node": "1.0.0", + "affjax-web": "1.0.0", + "ansi": "7.0.0", + "apexcharts": "0.5.0", + "applicative-phases": "1.0.0", + "argonaut": "9.0.0", + "argonaut-aeson-generic": "0.4.1", + "argonaut-codecs": "9.1.0", + "argonaut-core": "7.0.0", + "argonaut-generic": "8.0.0", + "argonaut-traversals": "10.0.0", + "argparse-basic": "2.0.0", + "array-builder": "0.1.2", + "array-search": "0.6.0", + "arraybuffer": "13.2.0", + "arraybuffer-builder": "3.1.0", + "arraybuffer-types": "3.0.2", + "arrays": "7.3.0", + "arrays-extra": "0.6.2", + "arrays-zipper": "2.0.1", + "ask": "1.0.0", + "assert": "6.0.0", + "assert-multiple": "0.4.0", + "avar": "5.0.1", + "axon": "0.0.3", + "b64": "0.0.9", + "barbies": "1.0.1", + "barlow-lens": "1.0.0", + "baskerville-core": "0.1.3", + "baskerville-mycroft": "0.1.0", + "benchlib": "0.0.4", + "bifunctors": "6.1.0", + "bigints": "7.0.1", + "blessed": "1.0.0", + "bolson": "0.3.9", + "bookhound": "0.1.7", + "bower-json": "3.0.0", + "bytestrings": "9.0.0", + "call-by-name": "4.0.1", + "canvas": "6.0.0", + "canvas-action": "9.0.0", + "cartesian": "1.0.6", + "catenable-lists": "7.0.0", + "cbor-stream": "1.3.0", + "chameleon": "1.0.0", + "chameleon-halogen": "1.0.3", + "chameleon-react-basic": "1.1.0", + "chameleon-styled": "2.5.0", + "chameleon-transformers": "1.0.0", + "channel": "1.0.0", + "chartjs": "0.2.0", + "chartjs-halogen": "0.2.0", + "checked-exceptions": "3.1.1", + "choku": "1.0.2", + "classless": "0.1.1", + "classless-arbitrary": "0.1.1", + "classless-decode-json": "0.1.1", + "classless-encode-json": "0.1.3", + "classnames": "2.0.0", + "codec": "6.1.0", + "codec-argonaut": "10.0.0", + "codec-json": "2.0.0", + "colors": "7.0.1", + "compile-fail": "0.4.0", + "concur-core": "0.5.0", + "concur-react": "0.5.0", + "concurrent-queues": "3.0.0", + "console": "6.1.0", + "const": "6.0.0", + "contravariant": "6.0.0", + "control": "6.0.0", + "convertable-options": "1.0.0", + "coroutines": "7.0.0", + "css": "6.0.0", + "css-class-name-extractor": "0.0.4", + "css-frameworks": "1.1.0", + "csv-stream": "2.3.0", + "currency": "0.1.0", + "data-mvc": "0.0.2", + "datetime": "6.1.0", + "datetime-parsing": "0.2.0", + "debounce": "0.1.2", + "debug": "6.0.2", + "decimals": "7.1.0", + "default-values": "1.0.1", + "deku": "0.9.24", + "deno": "0.0.5", + "dissect": "1.0.0", + "distributive": "6.0.0", + "dodo-printer": "2.2.3", + "dom-filereader": "7.0.0", + "dom-indexed": "13.0.0", + "dom-simple": "0.4.0", + "dotenv": "4.0.3", + "droplet": "0.6.0", + "dts": "1.0.0", + "dual-numbers": "1.0.3", + "dynamic-buffer": "3.0.1", + "echarts-simple": "0.0.1", + "effect": "4.0.0", + "either": "6.1.0", + "elmish": "0.16.0", + "elmish-enzyme": "0.1.1", + "elmish-hooks": "0.12.0", + "elmish-html": "0.14.0", + "elmish-testing-library": "0.5.0", + "email-validate": "7.0.0", + "encoding": "0.0.10", + "enums": "6.0.1", + "env-names": "0.4.0", + "environment": "2.0.0", + "error": "2.0.0", + "eta-conversion": "0.3.2", + "exceptions": "6.1.0", + "exists": "6.0.0", + "exitcodes": "4.0.0", + "expect-inferred": "3.0.0", + "express": "0.9.1", + "ezfetch": "1.1.1", + "fahrtwind": "2.0.0", + "faker-ffi": "0.1.1", + "fakerjs": "0.0.1", + "fallback": "0.1.0", + "fast-vect": "1.2.0", + "fetch": "4.1.0", + "fetch-argonaut": "1.0.1", + "fetch-core": "5.1.0", + "fetch-yoga-json": "1.1.0", + "ffi-simple": "0.5.1", + "fft": "0.3.0", + "fft-js": "0.1.0", + "filterable": "5.0.0", + "fix-functor": "0.1.0", + "fixed-points": "7.0.0", + "fixed-precision": "5.0.0", + "flame": "1.6.0", + "float32": "2.0.0", + "fmt": "0.2.1", + "foldable-traversable": "6.0.0", + "foldable-traversable-extra": "0.0.6", + "foreign": "7.0.0", + "foreign-object": "4.1.0", + "foreign-readwrite": "3.4.0", + "forgetmenot": "0.1.0", + "fork": "6.0.0", + "form-urlencoded": "7.0.0", + "formatters": "7.0.0", + "framer-motion": "3.1.0", + "free": "7.1.0", + "freeap": "7.0.0", + "freer-free": "0.0.1", + "freet": "7.0.0", + "functions": "6.0.0", + "functor1": "3.0.0", + "functors": "5.0.0", + "fuzzy": "0.4.0", + "gen": "4.0.0", + "generate-values": "1.0.1", + "generic-router": "0.0.1", + "geojson": "0.0.5", + "geometria": "2.2.0", + "gesso": "1.0.1", + "gojs": "0.1.1", + "golden-test": "0.1.0", + "golem-fetch": "0.1.0", + "grain": "3.0.0", + "grain-router": "3.0.0", + "grain-virtualized": "3.0.0", + "graphql-client": "10.1.1", + "graphs": "8.1.0", + "group": "4.1.1", + "halogen": "7.0.0", + "halogen-bootstrap5": "5.3.2", + "halogen-canvas": "1.0.0", + "halogen-css": "10.0.0", + "halogen-declarative-canvas": "0.0.8", + "halogen-echarts-simple": "0.0.4", + "halogen-formless": "4.0.3", + "halogen-helix": "1.1.0", + "halogen-hooks": "0.6.3", + "halogen-hooks-extra": "0.9.0", + "halogen-infinite-scroll": "1.1.0", + "halogen-store": "0.5.4", + "halogen-storybook": "2.0.0", + "halogen-subscriptions": "2.0.0", + "halogen-svg-elems": "8.0.0", + "halogen-typewriter": "1.0.4", + "halogen-use-trigger-hooks": "1.0.0", + "halogen-vdom": "8.0.0", + "halogen-vdom-string-renderer": "0.5.0", + "halogen-widgets": "0.3.1", + "halogen-xterm": "2.0.0", + "harmonia": "0.2.0", + "heckin": "2.0.1", + "heterogeneous": "0.7.0", + "homogeneous": "0.4.0", + "http-methods": "6.1.0", + "httpurple": "4.0.0", + "huffman": "0.4.0", + "humdrum": "0.0.1", + "hylograph-canvas": "0.1.0", + "hylograph-components": "0.1.0", + "hylograph-d3-kernel": "0.1.0", + "hylograph-graph": "0.3.0", + "hylograph-layout": "0.3.0", + "hylograph-music": "0.4.0", + "hylograph-optics": "0.1.0", + "hylograph-selection": "0.5.3", + "hylograph-simulation": "0.6.0", + "hylograph-simulation-core": "0.1.0", + "hylograph-simulation-halogen": "0.5.1", + "hylograph-transitions": "0.1.0", + "hylograph-wasm-kernel": "0.1.0", + "hyrule": "2.3.9", + "i18next": "0.1.0", + "idb": "0.2.0", + "identity": "6.0.0", + "identy": "4.0.1", + "indexed-db": "1.0.0", + "indexed-monad": "3.0.0", + "ink": "0.0.1", + "int64": "3.0.0", + "integers": "6.0.0", + "interpolate": "5.0.2", + "intersection-observer": "1.0.1", + "invariant": "6.0.0", + "jarilo": "1.0.1", + "jelly": "0.10.0", + "jelly-router": "0.3.0", + "jelly-signal": "0.4.0", + "jest": "1.0.0", + "js-abort-controller": "1.0.0", + "js-bigints": "2.2.1", + "js-date": "8.0.0", + "js-fetch": "0.2.1", + "js-fileio": "3.0.0", + "js-intl": "1.3.0", + "js-iterators": "0.1.1", + "js-maps": "0.1.2", + "js-promise": "1.0.0", + "js-promise-aff": "1.0.0", + "js-temporal": "3.0.0", + "js-timers": "6.1.0", + "js-uri": "3.1.0", + "jsdom": "1.0.0", + "jsinc": "0.5.1", + "json": "1.1.0", + "json-codecs": "5.0.0", + "justifill": "0.5.0", + "jwt": "0.0.9", + "labeled-data": "0.2.0", + "language-cst-parser": "0.14.1", + "lazy": "6.0.0", + "lazy-joe": "1.0.0", + "lcg": "4.0.0", + "leibniz": "5.0.0", + "leveldb": "1.0.1", + "liminal": "1.0.1", + "linalg": "6.0.0", + "linear": "0.1.0", + "lists": "7.0.0", + "literals": "1.0.2", + "logging": "3.0.0", + "logging-journald": "0.4.0", + "lumi-components": "18.0.0", + "machines": "7.0.0", + "maps-eager": "0.5.0", + "marionette": "1.0.0", + "marionette-react-basic-hooks": "0.1.1", + "markdown-it-js": "0.1.0", + "marked": "0.1.0", + "matrices": "5.0.1", + "matryoshka": "1.0.0", + "maybe": "6.0.0", + "media-types": "6.0.0", + "meowclient": "1.0.0", + "midi": "4.0.0", + "milkdown": "0.1.0", + "milkis": "9.0.0", + "mimetype": "0.0.1", + "minibench": "4.0.1", + "mmorph": "7.0.0", + "monad-control": "5.0.0", + "monad-logger": "1.3.1", + "monad-loops": "0.5.0", + "monad-unlift": "1.0.1", + "monoid-extras": "0.0.1", + "monoidal": "0.16.0", + "mote": "3.0.0", + "motion": "4.0.0", + "motsunabe": "2.0.0", + "mvc": "0.0.1", + "mycroft": "0.1.1", + "mycroft-z3-wasm": "0.1.0", + "mysql": "6.0.1", + "n3": "0.1.0", + "nano-id": "1.1.0", + "nanoid": "0.1.0", + "naturals": "3.0.0", + "nested-functor": "0.2.1", + "newtype": "5.0.0", + "next-purs-rsc": "0.1.0", + "nextjs": "0.1.1", + "nextui": "0.2.0", + "node-buffer": "9.0.0", + "node-child-process": "11.1.0", + "node-event-emitter": "3.0.0", + "node-execa": "5.0.0", + "node-fs": "9.2.0", + "node-glob-basic": "2.0.0", + "node-http": "9.1.0", + "node-http2": "1.1.1", + "node-human-signals": "1.0.0", + "node-net": "5.1.0", + "node-os": "5.1.0", + "node-path": "5.0.1", + "node-process": "11.2.0", + "node-readline": "8.1.1", + "node-sqlite": "0.2.0", + "node-sqlite3": "8.0.0", + "node-stream-pipes": "2.1.6", + "node-streams": "9.0.1", + "node-tls": "0.3.1", + "node-url": "7.0.1", + "node-workerbees": "0.3.1", + "node-zlib": "0.4.0", + "nonempty": "7.0.0", + "now": "6.0.0", + "npm-package-json": "2.0.0", + "nqueens": "0.1.4", + "nullable": "6.0.0", + "numberfield": "0.2.2", + "numbers": "9.0.1", + "oak": "3.1.1", + "oak-debug": "1.2.2", + "object-maps": "0.3.0", + "ocarina": "1.5.4", + "oooooooooorrrrrrrmm-lib": "0.0.1", + "open-colors-scales-and-schemes": "1.0.0", + "open-drawing": "6.0.4", + "open-folds": "6.4.0", + "open-foreign-generic": "11.0.3", + "open-memoize": "6.2.0", + "open-mkdirp-aff": "1.2.0", + "open-pairing": "6.2.0", + "open-smolder": "12.0.2", + "options": "7.0.0", + "optparse": "5.0.1", + "ordered-collections": "3.2.0", + "ordered-set": "0.5.2", + "orders": "6.0.0", + "org-doc": "0.1.0", + "owoify": "1.2.0", + "pairs": "9.0.1", + "parallel": "7.0.0", + "parsing": "11.0.0", + "parsing-dataview": "3.2.4", + "partial": "4.0.0", + "pathy": "9.0.0", + "pha": "0.13.0", + "phaser": "0.7.0", + "phylio": "1.1.2", + "pipes": "8.0.0", + "pirates-charm": "0.0.1", + "play": "1.0.0", + "pmock": "1.1.0", + "point-free": "1.0.0", + "pointed-list": "0.5.1", + "polymorphic-vectors": "4.0.0", + "posix-types": "6.0.0", + "postgresql": "2.0.21", + "precise": "6.0.0", + "precise-datetime": "7.0.0", + "prelude": "6.0.2", + "prettier-printer": "3.0.0", + "printf": "0.1.0", + "priority-queue": "0.1.2", + "profunctor": "6.0.1", + "profunctor-lenses": "8.0.0", + "prospero": "0.1.0", + "protobuf": "4.4.0", + "ps-spa": "0.7.1", + "psa-utils": "8.0.0", + "psci-support": "6.0.0", + "punycode": "1.0.0", + "pursfmt": "0.17.0", + "qualified-do": "2.2.0", + "quantities": "12.2.0", + "quickcheck": "8.0.1", + "quickcheck-combinators": "0.1.3", + "quickcheck-laws": "7.0.0", + "quickcheck-utf8": "0.0.0", + "random": "6.0.0", + "rationals": "6.0.0", + "rdf": "0.1.0", + "react": "11.0.0", + "react-aria": "0.2.0", + "react-basic": "17.0.0", + "react-basic-classic": "3.0.0", + "react-basic-dnd": "10.1.0", + "react-basic-dom": "7.0.0", + "react-basic-dom-beta": "0.1.1", + "react-basic-emotion": "7.1.0", + "react-basic-hooks": "9.1.1", + "react-basic-storybook": "2.0.0", + "react-dnd-kit": "0.6.0", + "react-dom": "8.0.0", + "react-halo": "3.0.0", + "react-icons": "1.1.6", + "react-markdown": "0.1.0", + "react-testing-library": "4.0.1", + "react-virtuoso": "1.0.0", + "reactix": "0.6.1", + "read": "1.0.1", + "recharts": "1.1.0", + "record": "4.0.0", + "record-extra": "5.0.1", + "record-extra-srghma": "0.2.8", + "record-ptional-fields": "0.1.2", + "record-studio": "1.0.4", + "ref-lifted": "0.0.2", + "refs": "6.0.0", + "remotedata": "5.0.1", + "repr": "0.5.0", + "resize-arrays": "0.0.1", + "resize-observer": "1.0.0", + "resource": "2.0.1", + "resourcet": "1.0.0", + "result": "1.0.3", + "return": "0.2.0", + "ring-modules": "5.0.1", + "rito": "0.3.4", + "roman": "0.4.0", + "rough-notation": "1.0.2", + "routing": "11.0.0", + "routing-duplex": "0.7.0", + "run": "5.0.0", + "safe-coerce": "2.0.0", + "safely": "4.0.1", + "school-of-music": "1.3.0", + "selection-foldable": "0.2.0", + "selective-functors": "1.0.1", + "semirings": "7.0.0", + "seowebchecker": "1.0.4", + "shuffle": "2.0.0", + "sigil": "0.3.0", + "sigil-hats": "0.3.0", + "signal": "13.0.0", + "simple-emitter": "3.0.1", + "simple-i18n": "2.0.1", + "simple-json": "9.0.0", + "simple-json-generics": "0.2.1", + "simple-ulid": "3.0.0", + "sized-matrices": "1.0.0", + "sized-vectors": "5.0.2", + "slug": "3.2.0", + "small-ffi": "4.0.1", + "soundfonts": "4.1.0", + "sparse-matrices": "2.0.1", + "sparse-polynomials": "3.0.1", + "spec": "8.1.2", + "spec-discovery": "8.4.1", + "spec-mocha": "5.1.1", + "spec-node": "0.0.3", + "spec-quickcheck": "5.0.2", + "spec-reporter-xunit": "0.7.1", + "splitmix": "2.1.0", + "ssrs": "1.0.0", + "st": "6.2.0", + "statistics": "0.3.2", + "strictlypositiveint": "1.0.1", + "string-parsers": "8.0.0", + "strings": "6.0.1", + "strings-extra": "4.0.0", + "stringutils": "0.0.13", + "structured-logging": "1.0.0", + "substitute": "0.2.3", + "supabase": "0.1.0", + "supply": "0.2.0", + "svg-parser": "3.0.0", + "systemd-journald": "0.3.0", + "tagged": "4.0.2", + "tailrec": "6.1.0", + "tanstack-query": "3.0.0", + "tecton": "0.2.1", + "tecton-halogen": "0.2.0", + "temporal": "0.2.0", + "test-unit": "17.0.0", + "text-formatting": "0.1.0", + "thermite": "6.3.1", + "thermite-dom": "0.3.1", + "these": "6.0.0", + "threading": "0.0.3", + "tidy": "0.11.1", + "tidy-codegen": "4.0.1", + "tldr": "0.0.0", + "toestand": "0.9.0", + "tramaj-halogen": "0.4.1", + "tramaj-purs": "0.4.1", + "transformation-matrix": "1.0.1", + "transformers": "6.1.0", + "transit": "1.0.0", + "tree-rose": "4.0.2", + "trivial-unfold": "0.5.0", + "ts-bridge": "4.0.0", + "tuples": "7.0.0", + "two-or-more": "1.0.0", + "type-equality": "4.0.1", + "typedenv": "2.0.1", + "typelevel": "6.0.0", + "typelevel-lists": "2.1.0", + "typelevel-peano": "1.0.1", + "typelevel-prelude": "7.0.0", + "typelevel-regex": "0.0.3", + "typelevel-rows": "0.1.0", + "typisch": "0.4.0", + "uint": "7.0.0", + "ulid": "3.0.1", + "uncurried-transformers": "1.1.0", + "undefined": "2.0.0", + "undefined-is-not-a-problem": "1.1.0", + "unfoldable": "6.0.0", + "unicode": "6.0.0", + "unique": "0.6.1", + "unlift": "1.0.1", + "unordered-collections": "3.1.0", + "unsafe-coerce": "6.0.0", + "unsafe-reference": "5.0.0", + "untagged-to-tagged": "0.1.4", + "untagged-union": "1.0.0", + "uri": "9.0.0", + "url-immutable": "1.0.0", + "url-regex-safe": "0.1.1", + "uuid": "9.0.0", + "uuidv4": "1.0.0", + "validation": "6.0.0", + "variant": "8.0.0", + "variant-encodings": "2.0.0", + "variant-gen": "1.0.0", + "vectorfield": "1.0.1", + "vectors": "2.1.0", + "versions": "7.0.0", + "visx": "0.0.2", + "vitest": "2.0.1", + "wasm-base": "0.2.0", + "web-chain": "1.0.1", + "web-clipboard": "6.0.0", + "web-cssom": "2.0.0", + "web-cssom-view": "0.1.0", + "web-dom": "6.0.0", + "web-dom-parser": "8.0.0", + "web-dom-xpath": "3.0.0", + "web-encoding": "3.0.0", + "web-events": "4.0.0", + "web-fetch": "4.0.1", + "web-file": "4.0.0", + "web-geometry": "0.1.0", + "web-html": "4.1.1", + "web-pointerevents": "2.0.0", + "web-proletarian": "1.0.0", + "web-promise": "3.2.0", + "web-resize-observer": "2.1.0", + "web-router": "1.0.0", + "web-socket": "4.0.0", + "web-storage": "5.0.0", + "web-streams": "4.0.0", + "web-touchevents": "4.0.0", + "web-uievents": "5.0.0", + "web-url": "2.0.0", + "web-workers": "2.0.0", + "web-xhr": "5.0.1", + "webb-aff-list": "0.0.2", + "webb-array": "0.0.2", + "webb-channel": "0.0.3", + "webb-commandline": "0.0.3", + "webb-directory": "0.0.7", + "webb-file": "0.0.1", + "webb-map": "0.0.1", + "webb-monad": "0.0.2", + "webb-mutex": "0.0.7", + "webb-parsing": "0.0.5", + "webb-random": "0.0.1", + "webb-refer": "0.0.3", + "webb-set": "0.0.1", + "webb-slot": "0.0.1", + "webb-stateful": "0.0.3", + "webb-string": "0.0.1", + "webb-test": "0.0.1", + "webb-thread": "0.0.2", + "webb-writer": "0.0.3", + "webextension-polyfill": "0.1.0", + "webgpu": "0.0.1", + "which": "2.0.0", + "whine-core": "0.0.34", + "xterm": "1.0.0", + "yaml-next": "3.1.1", + "yoga-acp-om": "0.1.1", + "yoga-better-auth": "0.2.2", + "yoga-bun-yoga": "0.2.1", + "yoga-config": "0.2.1", + "yoga-country": "3.0.0", + "yoga-docker-compose": "0.1.1", + "yoga-dynamodb": "0.1.1", + "yoga-elasticsearch": "0.1.1", + "yoga-fastify": "0.5.4", + "yoga-fastify-om": "0.4.7", + "yoga-fetch": "1.0.1", + "yoga-fetch-om": "0.8.0", + "yoga-format": "1.0.0", + "yoga-heroui": "2.0.3", + "yoga-http-api": "0.3.4", + "yoga-jaeger": "0.1.1", + "yoga-json": "5.2.1", + "yoga-language": "1.0.0", + "yoga-next-fastify": "0.2.0", + "yoga-om": "2.2.1", + "yoga-om-layer": "2.1.0", + "yoga-om-strom": "0.4.3", + "yoga-om-workerbees": "0.1.2", + "yoga-opentelemetry": "0.2.0", + "yoga-options": "0.1.1", + "yoga-pino": "0.1.1", + "yoga-postgres": "6.0.0", + "yoga-react": "1.0.2", + "yoga-react-dom": "2.0.1", + "yoga-react-native": "0.2.1", + "yoga-react-om": "0.2.0", + "yoga-redis": "0.1.1", + "yoga-scylladb": "0.1.1", + "yoga-shadcn": "1.0.5", + "yoga-sql-types": "0.2.0", + "yoga-sqlite": "0.3.4", + "yoga-sqlite-om": "0.3.0", + "yoga-sse": "0.1.1", + "yoga-subtlecrypto": "0.1.0", + "yoga-test-docker": "0.1.2", + "yoga-timezone": "1.0.0", + "yoga-tree": "1.0.0", + "yoga-tree-svg": "0.1.0", + "yoga-tree-utils": "1.0.0", + "z3": "0.0.2", + "zipperarray": "2.0.0" + } + }, + "extra_packages": {} + }, + "packages": { + "aff": { + "type": "registry", + "version": "8.0.0", + "integrity": "sha256-9BRIrSdRoYybfiUmBqgKJ66cPqxYHb277bVWQMn/9sg=", + "dependencies": [ + "control", + "datetime", + "effect", + "either", + "exceptions", + "functions", + "newtype", + "parallel", + "partial", + "prelude", + "st", + "tailrec", + "transformers", + "unsafe-coerce" + ] + }, + "arrays": { + "type": "registry", + "version": "7.3.0", + "integrity": "sha256-GD4z7LCi9wzi7FCQqbWhvs8paoUK9NO+HwgXZ+KP8Rk=", + "dependencies": [ + "bifunctors", + "control", + "foldable-traversable", + "functions", + "maybe", + "nonempty", + "partial", + "prelude", + "safe-coerce", + "st", + "tailrec", + "tuples", + "unfoldable", + "unsafe-coerce" + ] + }, + "assert": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-hCZ1J8/71nQiRwsSV2j7iicppScOegBFZrLI6sPf9F8=", + "dependencies": [ + "console", + "effect", + "prelude" + ] + }, + "bifunctors": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-CvZRb8uI75eKIqiYntR+k05Wk6tKlMS5w8sCA2AFna0=", + "dependencies": [ + "const", + "either", + "newtype", + "prelude", + "tuples" + ] + }, + "console": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-3T5PPz1mATZmV0C4GzkCkpLlCa5xG7u8ZNgnLrrrI1Q=", + "dependencies": [ + "effect", + "prelude" + ] + }, + "const": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-S+m0lQsxnji0z1XRUmkn+3RFuWJ5CXWuIr1u3i8Bfoc=", + "dependencies": [ + "invariant", + "newtype", + "prelude" + ] + }, + "contravariant": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-nuckEWqL80NaPmR/l9AUyrfycNh18XcvlZV9PAamwqc=", + "dependencies": [ + "const", + "either", + "newtype", + "prelude", + "tuples" + ] + }, + "control": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-0vxyYh5FL+ilxgLo954w1LqCUC0RSIivNNlb9CaNUQg=", + "dependencies": [ + "newtype", + "prelude" + ] + }, + "datetime": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-tbv6eDXy6QOD7YSknxYSbsDF32OC2JrNbSBpzMs7Doc=", + "dependencies": [ + "bifunctors", + "control", + "either", + "enums", + "foldable-traversable", + "functions", + "gen", + "integers", + "lists", + "maybe", + "newtype", + "numbers", + "ordered-collections", + "partial", + "prelude", + "tuples" + ] + }, + "distributive": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-netyJK4B32PDYHD0tF2AenKgo3urzbI770ycM0pArAo=", + "dependencies": [ + "identity", + "newtype", + "prelude", + "tuples", + "type-equality" + ] + }, + "effect": { + "type": "registry", + "version": "4.0.0", + "integrity": "sha256-DdqU3bncRTjgMVPPRlAuGHVeajSPEcRaRhNuio7if6s=", + "dependencies": [ + "prelude" + ] + }, + "either": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-tBHx3PgtH4GZOApZCDkcM7szRTYJinrUrVGWazQCUUg=", + "dependencies": [ + "control", + "invariant", + "maybe", + "prelude" + ] + }, + "enums": { + "type": "registry", + "version": "6.0.1", + "integrity": "sha256-sdZOmLX5+5pASGpvVyT0vSD5BBYQjZiayjV5XiPutso=", + "dependencies": [ + "control", + "either", + "gen", + "maybe", + "newtype", + "nonempty", + "partial", + "prelude", + "tuples", + "unfoldable" + ] + }, + "exceptions": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-nRrr+N0wk0TUFOWLR7e1n1oxcXo/X345bQUtoY9lW6A=", + "dependencies": [ + "effect", + "either", + "maybe", + "prelude" + ] + }, + "exists": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-vtLbrWNaI+pzx/2fw2AQnCovoLtcosClIhjO26QKkh8=", + "dependencies": [ + "unsafe-coerce" + ] + }, + "foldable-traversable": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-NGGWyCio/xjce6fPP7y3wwg7CheqllID6aJudDxbWhA=", + "dependencies": [ + "bifunctors", + "const", + "control", + "either", + "functors", + "identity", + "maybe", + "newtype", + "orders", + "prelude", + "tuples" + ] + }, + "functions": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-0jgtOr+SFO4UScKc40oA/9Vdcf2rLn3h3vZlY0KfqVo=", + "dependencies": [ + "prelude" + ] + }, + "functors": { + "type": "registry", + "version": "5.0.0", + "integrity": "sha256-W1o/4wcp6S3chodYL8uixlXK2mfr1L+SzM1R+whqUco=", + "dependencies": [ + "bifunctors", + "const", + "contravariant", + "control", + "distributive", + "either", + "invariant", + "maybe", + "newtype", + "prelude", + "profunctor", + "tuples", + "unsafe-coerce" + ] + }, + "gen": { + "type": "registry", + "version": "4.0.0", + "integrity": "sha256-kyOateeOtQa07vfTMYeSuCKdelgKE1R65fwKkZ10wsY=", + "dependencies": [ + "either", + "foldable-traversable", + "identity", + "maybe", + "newtype", + "nonempty", + "prelude", + "tailrec", + "tuples", + "unfoldable" + ] + }, + "identity": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-RY/iXPpxpqvKHYfJeFVNI1J06kohB9rbtWYuha8t0LE=", + "dependencies": [ + "control", + "invariant", + "newtype", + "prelude" + ] + }, + "integers": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-Y8yozC1uHRLcruobEyv5/bMKzE1BoKJ/olHv6o+bgm8=", + "dependencies": [ + "maybe", + "numbers", + "prelude" + ] + }, + "invariant": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-HyoT1I5qIHRq8RSZji6zZDvqnpVjLw748utrKwZtqsw=", + "dependencies": [ + "control", + "prelude" + ] + }, + "lazy": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-MHRC+1Pc+AjortBp4b/e1e6KpEpsDwaJBNBB7TOL+1I=", + "dependencies": [ + "control", + "foldable-traversable", + "invariant", + "prelude" + ] + }, + "lists": { + "type": "registry", + "version": "7.0.0", + "integrity": "sha256-/mGd7wLoU+wsTcqii7SSUByxsvwvjnu2PjwAD47yYb4=", + "dependencies": [ + "bifunctors", + "control", + "foldable-traversable", + "lazy", + "maybe", + "newtype", + "nonempty", + "partial", + "prelude", + "tailrec", + "tuples", + "unfoldable" + ] + }, + "maybe": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-8X6XEgtZ5lqfkEuc1hWOofnL6mwhk8gSJTxwvbR/SbY=", + "dependencies": [ + "control", + "invariant", + "newtype", + "prelude" + ] + }, + "newtype": { + "type": "registry", + "version": "5.0.0", + "integrity": "sha256-dt6w0cty4OS+Pjt9fsh3iU6ottqSU83mdAZkaEEvo6c=", + "dependencies": [ + "prelude", + "safe-coerce" + ] + }, + "nonempty": { + "type": "registry", + "version": "7.0.0", + "integrity": "sha256-Ctkq8/+KiGqCE53Y/LvibA/coy7ev9HtRZU+GYS7yfQ=", + "dependencies": [ + "control", + "foldable-traversable", + "maybe", + "prelude", + "tuples", + "unfoldable" + ] + }, + "nullable": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-CDAZk+L9Sc0GCFTkE8n+qdp4Ys8avvnkU+m2hVALt7M=", + "dependencies": [ + "functions", + "maybe", + "prelude" + ] + }, + "numbers": { + "type": "registry", + "version": "9.0.1", + "integrity": "sha256-ZydYckgwRAnRaFC7pu6fADhzdwX+UqHZrfJyet64zls=", + "dependencies": [ + "functions", + "maybe", + "prelude" + ] + }, + "ordered-collections": { + "type": "registry", + "version": "3.2.0", + "integrity": "sha256-NaEXc2cz/7lJfxPWEja4XlZbGxuRqJwFKQJjsJf51kQ=", + "dependencies": [ + "arrays", + "control", + "foldable-traversable", + "functions", + "gen", + "lists", + "maybe", + "newtype", + "partial", + "prelude", + "safe-coerce", + "tailrec", + "tuples", + "unfoldable" + ] + }, + "orders": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-nSMxZK7wxyDULkjZqvMyB71mVo6b0AyLLHosDVbv1XM=", + "dependencies": [ + "newtype", + "prelude" + ] + }, + "parallel": { + "type": "registry", + "version": "7.0.0", + "integrity": "sha256-bAGpVhrFz2IyHkA4MR7+tE/19FZ7dzYBdYPgK5svkqw=", + "dependencies": [ + "control", + "effect", + "either", + "foldable-traversable", + "functors", + "maybe", + "newtype", + "prelude", + "profunctor", + "refs", + "transformers" + ] + }, + "parsing": { + "type": "registry", + "version": "11.0.0", + "integrity": "sha256-N88gb/XbM29vw/Uop83shuMFT4wieQCz4wlQAct5eMs=", + "dependencies": [ + "arrays", + "control", + "effect", + "either", + "enums", + "foldable-traversable", + "functions", + "identity", + "integers", + "lazy", + "lists", + "maybe", + "newtype", + "nullable", + "numbers", + "partial", + "prelude", + "st", + "strings", + "tailrec", + "transformers", + "tuples", + "unfoldable", + "unicode", + "unsafe-coerce" + ] + }, + "partial": { + "type": "registry", + "version": "4.0.0", + "integrity": "sha256-mdrlJBAgFMh79hNRW39uv7c+BwnnaOrAMmdh7+VhEhs=", + "dependencies": [] + }, + "prelude": { + "type": "registry", + "version": "6.0.2", + "integrity": "sha256-IqykrDRquGUD/LRSad7y75hZv3bUsGQ4knc8tdez8Qw=", + "dependencies": [] + }, + "profunctor": { + "type": "registry", + "version": "6.0.1", + "integrity": "sha256-Ow0mJC+PIIRq/a3mzCWY5vxuTgR/e+Ee68+mWeSRnWY=", + "dependencies": [ + "control", + "distributive", + "either", + "exists", + "invariant", + "newtype", + "prelude", + "tuples" + ] + }, + "refs": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-KKD5G9dR2SauuDGULUJsxjqDOEi1Jqm+2Sq5U2mY8YU=", + "dependencies": [ + "effect", + "prelude" + ] + }, + "safe-coerce": { + "type": "registry", + "version": "2.0.0", + "integrity": "sha256-EJrxtKt5xC7TYjBZrSBO/J7Aw3DmtilYjt4olpAXWVc=", + "dependencies": [ + "unsafe-coerce" + ] + }, + "st": { + "type": "registry", + "version": "6.2.0", + "integrity": "sha256-WI4MEkkwUd4pnwZQ3G8VKWgX5t1RSNJludm5e9boaRo=", + "dependencies": [ + "effect", + "partial", + "prelude", + "tailrec", + "unsafe-coerce" + ] + }, + "strings": { + "type": "registry", + "version": "6.0.1", + "integrity": "sha256-FNto2hoNW5Da6W6crIk77YXMagBvOGHZE1kO7eZ1vLM=", + "dependencies": [ + "arrays", + "control", + "either", + "enums", + "foldable-traversable", + "gen", + "integers", + "maybe", + "newtype", + "nonempty", + "partial", + "prelude", + "tailrec", + "tuples", + "unfoldable", + "unsafe-coerce" + ] + }, + "tailrec": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-xrorCfP0xV1wubs27idQCHQqdvSUuqmmL/am7Ji1wVU=", + "dependencies": [ + "bifunctors", + "effect", + "either", + "identity", + "maybe", + "partial", + "prelude", + "refs" + ] + }, + "transformers": { + "type": "registry", + "version": "6.1.0", + "integrity": "sha256-QJmgNT/y7ljPHSsXaW4zxF/982BFiQ90KNL1g/NtEOQ=", + "dependencies": [ + "control", + "distributive", + "effect", + "either", + "exceptions", + "foldable-traversable", + "identity", + "lazy", + "maybe", + "newtype", + "prelude", + "st", + "tailrec", + "tuples", + "unfoldable" + ] + }, + "tuples": { + "type": "registry", + "version": "7.0.0", + "integrity": "sha256-BKy7EfK8S1KeTCwyWqyW1wwcpSAQuh3IsczSVgS6fzY=", + "dependencies": [ + "control", + "invariant", + "prelude" + ] + }, + "type-equality": { + "type": "registry", + "version": "4.0.1", + "integrity": "sha256-BBqYOSnAKmayRvR5mtZoCf4SlYs2ipatD/+5nQx73aw=", + "dependencies": [] + }, + "unfoldable": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-BK/6By1sKp/ztk+9CF3q43SV0QDTLVdsGqFerKHSmbg=", + "dependencies": [ + "foldable-traversable", + "maybe", + "partial", + "prelude", + "tuples" + ] + }, + "unicode": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-5KugCNyYxscmZpnMbssOQA96XimuBN/bjHpdrtd5qaY=", + "dependencies": [ + "arrays", + "enums", + "integers", + "maybe", + "partial", + "prelude", + "strings", + "unsafe-coerce" + ] + }, + "unsafe-coerce": { + "type": "registry", + "version": "6.0.0", + "integrity": "sha256-0L1QsaY20OILjfU5TV72d3U/tSjhmL9hJ32WMp857Lk=", + "dependencies": [] + } + } +} diff --git a/spago.yaml b/spago.yaml new file mode 100644 index 0000000..fcf54cd --- /dev/null +++ b/spago.yaml @@ -0,0 +1,41 @@ +package: + name: formatters + description: A PureScript alternative to numeral.js, moment.js, etc. + publish: + version: 8.0.0 + license: Apache-2.0 + location: + githubOwner: purescript-contrib + githubRepo: purescript-formatters + build: + strict: true + dependencies: + - arrays: ">=7.0.0 <8.0.0" + - bifunctors: ">=6.0.0 <7.0.0" + - control: ">=6.0.0 <7.0.0" + - datetime: ">=6.0.0 <7.0.0" + - either: ">=6.0.0 <7.0.0" + - enums: ">=6.0.0 <7.0.0" + - foldable-traversable: ">=6.0.0 <7.0.0" + - integers: ">=6.0.0 <7.0.0" + - lists: ">=7.0.0 <8.0.0" + - maybe: ">=6.0.0 <7.0.0" + - newtype: ">=5.0.0 <6.0.0" + - numbers: ">=9.0.0 <10.0.0" + - ordered-collections: ">=3.0.0 <4.0.0" + - parsing: ">=9.0.0 <12.0.0" + - partial: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.0 <7.0.0" + - strings: ">=6.0.0 <7.0.0" + - transformers: ">=6.0.0 <7.0.0" + - tuples: ">=7.0.0 <8.0.0" + test: + main: Test.Main + dependencies: + - aff + - assert + - console + - effect +workspace: + packageSet: + registry: 81.4.0 diff --git a/src/Data/Formatter/DateTime.purs b/src/Data/Formatter/DateTime.purs index 72dec85..f66e03a 100644 --- a/src/Data/Formatter/DateTime.purs +++ b/src/Data/Formatter/DateTime.purs @@ -4,15 +4,17 @@ -- | + `YY` - 2 digit year (99) -- | + `MMMM` - Full Month (January) -- | + `MMM` - Short Month (Jan) +-- | + `M` - Unpadded Month (1) -- | + `DD` - Padded Day (02) -- | + `D` - Day of month (2) -- | + `X` - Unix Timestamp (1506875681) --- | + `E` - Day of Week (2) +-- | + `d` - Day of Week (2, Monday = 1, Sunday = 7) -- | + `dddd` - DOW Name (Monday) -- | + `ddd` - DOW Name Short (Mon) -- | + `HH` - 24 Hour (13) -- | + `hh` - 12 Hour (1) -- | + `a` - Meridiem (am/pm) +-- | + `A` - Meridiem (AM/PM) -- | + `mm` - Minutes Padded (02) -- | + `m` - Minutes (2) -- | + `ss` - Seconds Padded (02) @@ -80,6 +82,7 @@ data FormatterCommand | YearAbsolute | MonthFull | MonthShort + | MonthDigit | MonthTwoDigits | DayOfMonthTwoDigits | DayOfMonth @@ -90,6 +93,7 @@ data FormatterCommand | Hours24 | Hours12 | Meridiem + | MeridiemLowerCase | Minutes | MinutesTwoDigits | Seconds @@ -118,16 +122,18 @@ printFormatterCommand = case _ of YearAbsolute -> "Y" MonthFull -> "MMMM" MonthShort -> "MMM" + MonthDigit -> "M" MonthTwoDigits -> "MM" DayOfMonthTwoDigits -> "DD" DayOfMonth -> "D" UnixTimestamp -> "X" - DayOfWeek -> "E" + DayOfWeek -> "d" DayOfWeekName -> "dddd" DayOfWeekNameShort -> "ddd" Hours24 -> "HH" Hours12 -> "hh" - Meridiem -> "a" + Meridiem -> "A" + MeridiemLowerCase -> "a" Minutes -> "m" MinutesTwoDigits -> "mm" Seconds -> "s" @@ -141,7 +147,7 @@ printFormatterCommand = case _ of needsEscaping = CU.toCharArray >>> Array.any (flip Array.elem formatCharacters) formatCharacters :: Array Char -formatCharacters = CU.toCharArray "YMDEHhamsS" +formatCharacters = CU.toCharArray "YMDXdHhaAmsS" -- | The format string representation of a `Formatter`. -- | @@ -180,15 +186,17 @@ formatterCommandParser = , Tuple "MMMM" MonthFull , Tuple "MMM" MonthShort , Tuple "MM" MonthTwoDigits + , Tuple "M" MonthDigit , Tuple "DD" DayOfMonthTwoDigits , Tuple "D" DayOfMonth - , Tuple "E" DayOfWeek , Tuple "X" UnixTimestamp , Tuple "dddd" DayOfWeekName , Tuple "ddd" DayOfWeekNameShort + , Tuple "d" DayOfWeek , Tuple "HH" Hours24 , Tuple "hh" Hours12 - , Tuple "a" Meridiem + , Tuple "A" Meridiem + , Tuple "a" MeridiemLowerCase , Tuple "mm" MinutesTwoDigits , Tuple "m" Minutes , Tuple "ss" SecondsTwoDigits @@ -223,6 +231,7 @@ formatCommand dt@(DT.DateTime d t) = case _ of YearAbsolute -> show $ fromEnum $ D.year d MonthFull -> show $ D.month d MonthShort -> printShortMonth $ D.month d + MonthDigit -> show $ fromEnum $ D.month d MonthTwoDigits -> padSingleDigit $ fromEnum $ D.month d DayOfMonthTwoDigits -> padSingleDigit $ fromEnum $ D.day d DayOfMonth -> show $ fromEnum $ D.day d @@ -233,6 +242,7 @@ formatCommand dt@(DT.DateTime d t) = case _ of Hours24 -> padSingleDigit (fromEnum $ T.hour t) Hours12 -> padSingleDigit $ fix12 $ (fromEnum $ T.hour t) `mod` 12 Meridiem -> if (fromEnum $ T.hour t) >= 12 then "PM" else "AM" + MeridiemLowerCase -> if (fromEnum $ T.hour t) >= 12 then "pm" else "am" Minutes -> show $ fromEnum $ T.minute t MinutesTwoDigits -> padSingleDigit <<< fromEnum $ T.minute t Seconds -> show $ fromEnum $ T.second t @@ -268,7 +278,7 @@ format :: Formatter -> DT.DateTime -> String format f d = foldMap (formatCommand d) f -- | Format a DateTime according to the format defined in the given format string. --- | If the format string is empty or contains a reserved character such as a single "M" or "H", +-- | If the format string is empty or contains a reserved character such as a single "H", -- | will return a `Left` value. -- | Note that any non-reserved `Char` is treated as a placeholder, so while "yyyy-MM-dd" might -- | not produce the format you want (since "yyyy" isn't a recognized format), @@ -419,6 +429,8 @@ unformatCommandParser = case _ of (fromEnum <$> parseMonth) MonthShort -> _ { month = _ } `modifyWithParser` (fromEnum <$> parseShortMonth) + MonthDigit -> _ { month = _ } `modifyWithParser` + (parseInt 2 (validateRange 1 12) "Incorrect month digit") MonthTwoDigits -> _ { month = _ } `modifyWithParser` (parseInt 2 (validateRange 1 12 <> exactLength) "Incorrect 2-digit month") DayOfMonthTwoDigits -> _ { day = _ } `modifyWithParser` @@ -450,6 +462,7 @@ unformatCommandParser = case _ of Hours12 -> _ { hour = _ } `modifyWithParser` (parseInt 2 (validateRange 0 12 <> exactLength) "Incorrect 12 hour") Meridiem -> _ { meridiem = _ } `modifyWithParser` parseMeridiem + MeridiemLowerCase -> _ { meridiem = _ } `modifyWithParser` parseMeridiem MinutesTwoDigits -> _ { minute = _ } `modifyWithParser` (parseInt 2 (validateRange 0 59 <> exactLength) "Incorrect 2-digit minute") Minutes -> _ { minute = _ } `modifyWithParser` diff --git a/src/Data/Formatter/Number.purs b/src/Data/Formatter/Number.purs index 30665e3..46e2061 100644 --- a/src/Data/Formatter/Number.purs +++ b/src/Data/Formatter/Number.purs @@ -49,6 +49,7 @@ import Prelude import Data.Array (many, some) import Data.Array as Arr import Data.Either (Either, either) +import Data.Foldable (foldMap) import Data.Formatter.Internal (foldDigits, repeat) import Data.Formatter.Parser.Number (parseDigit) import Data.Formatter.Parser.Utils (runP) @@ -220,6 +221,8 @@ format (Formatter f) num = do -- | Attempt to parse a `String` as a `Number` according to the format defined in the -- | given `Formatter`. +-- | The digit counts are minimum widths: additional fractional digits are retained. +-- | Parsing uses the precision of `Number`, without an intermediate 32-bit `Int`. unformat :: Formatter -> String -> Either String Number unformat = runP <<< unformatParser @@ -262,18 +265,12 @@ unformatParser (Formatter f) = do _ -> P.fail "Incorrect symbol, expected ',' or '.'" beforeDigits <- digitsWithCommas - before <- - if Arr.length beforeDigits < f.before then - P.fail "Error: too few digits before dot" - else - pure $ Int.toNumber $ foldDigits beforeDigits + when (Arr.length beforeDigits < f.before) $ + P.fail "Error: too few digits before dot" afterDigits <- some parseDigit - after <- - if Arr.length afterDigits < f.after then - P.fail "Error: too few digits after dot" - else - pure $ Int.toNumber $ foldDigits afterDigits + when (Arr.length afterDigits < f.after) $ + P.fail "Error: too few digits after dot" abbr <- if f.abbreviations then do @@ -295,10 +292,9 @@ unformatParser (Formatter f) = do _ -> pure 0 else pure 0 - pure $ - Number.pow 10.0 (Int.toNumber abbr) - * sign - * (before + after / Number.pow 10.0 (Int.toNumber f.after)) + case Number.fromString (foldMap show beforeDigits <> "." <> foldMap show afterDigits) of + Nothing -> P.fail "Number out of range" + Just number -> pure $ Number.pow 10.0 (Int.toNumber abbr) * sign * number -- | Format a Number according to the format defined in the given format string. -- | If the format string fails to parse, will return a `Left` value. diff --git a/test/src/DateTime.purs b/test/src/DateTime.purs index b938d34..ea203cc 100644 --- a/test/src/DateTime.purs +++ b/test/src/DateTime.purs @@ -5,7 +5,8 @@ import Prelude import Control.Alternative (guard) import Control.Monad.Reader.Class (class MonadReader) import Data.DateTime (DateTime) -import Data.Either (Either(..)) +import Data.Either (Either(..), isLeft) +import Data.Foldable (for_) import Data.Formatter.DateTime as FDT import Data.List (fromFoldable) import Effect.Aff.Class (class MonadAff) @@ -13,6 +14,35 @@ import Test.Utils (forAll, makeDateTime, describe, it, shouldEqual) datetimeTest :: forall m. MonadReader Int m => MonadAff m => m Unit datetimeTest = describe "Data.Formatter.DateTime" do + describe "meridiem token convention" do + forAll show "formats and parses both cases at midnight and noon" [ 0, 11, 12, 23 ] \hour -> do + let + date = makeDateTime 2020 5 20 hour 0 0 0 + lower = if hour < 12 then "am" else "pm" + upper = if hour < 12 then "AM" else "PM" + FDT.formatDateTime "a A" date `shouldEqual` Right (lower <> " " <> upper) + for_ [ "a", "A" ] \token -> do + let prefix = "2020-05-20 " <> (if hour == 0 || hour == 12 then "12" else "11") <> " " + FDT.unformatDateTime ("YYYY-MM-DD hh " <> token) (prefix <> lower) `shouldEqual` Right date + FDT.unformatDateTime ("YYYY-MM-DD hh " <> token) (prefix <> upper) `shouldEqual` Right date + + forAll identity "preserves escaped token literals" [ "X", "d", "ddd", "M", "a", "A" ] \literal -> do + let str = "[" <> literal <> "]" + (FDT.printFormatter <$> FDT.parseFormatString str) `shouldEqual` Right str + FDT.formatDateTime str (makeDateTime 2020 5 20 0 0 0 0) `shouldEqual` Right literal + + describe "single-digit months" do + forAll show "formats and parses unpadded months" [ 1, 5, 10, 12 ] \month -> do + let date = makeDateTime 2020 month 20 0 0 0 0 + FDT.formatDateTime "D.M.YYYY" date `shouldEqual` Right ("20." <> show month <> ".2020") + FDT.unformatDateTime "D.M.YYYY" ("20." <> show month <> ".2020") `shouldEqual` Right date + it "also accepts padded input" $ + FDT.unformatDateTime "D.M.YYYY" "20.05.2020" `shouldEqual` Right (makeDateTime 2020 5 20 0 0 0 0) + forAll identity "rejects invalid months" [ "0", "13", "123" ] \month -> + isLeft (FDT.unformatDateTime "D.M.YYYY" ("20." <> month <> ".2020")) `shouldEqual` true + it "keeps MM padded" $ + isLeft (FDT.unformatDateTime "D.MM.YYYY" "20.5.2020") `shouldEqual` true + forAll (\a -> a.format <> " | " <> a.dateStr) "formatDateTime/unformatDateTime should format/unformat dateTime" [ { format: "MM/DD/YYYY", dateStr: "04/12/2017", date: makeDateTime 2017 4 12 11 3 4 234 } @@ -24,22 +54,25 @@ datetimeTest = describe "Data.Formatter.DateTime" do , { format: "MMM D", dateStr: "Apr 1", date: makeDateTime 2017 4 1 0 0 0 0 } , { format: "dddd, MMM D", dateStr: "Saturday, Apr 1", date: makeDateTime 2017 4 1 0 0 0 0 } , { format: "ddd, MMM D", dateStr: "Sat, Apr 1", date: makeDateTime 2017 4 1 0 0 0 0 } - , { format: "E", dateStr: "6", date: makeDateTime 2017 4 1 0 0 0 0 } - , { format: "hh:mm:ss:SSS a", dateStr: "11:03:04:234 AM", date: makeDateTime 2017 4 12 11 3 4 234 } + , { format: "d", dateStr: "6", date: makeDateTime 2017 4 1 0 0 0 0 } + , { format: "hh:mm:ss:SSS A", dateStr: "11:03:04:234 AM", date: makeDateTime 2017 4 12 11 3 4 234 } , { format: "YY", dateStr: "17", date: makeDateTime 2017 4 12 11 3 4 234 } , { format: "YY", dateStr: "17", date: makeDateTime 20017 4 12 0 0 0 0 } -- Format 20017 with YY , { format: "YY", dateStr: "00", date: makeDateTime 0 4 12 0 0 0 0 } -- Format 0 with YY , { format: "YY", dateStr: "01", date: makeDateTime (-1) 4 12 0 0 0 0 } -- Format -1 with YY - , { format: "hh:m:s a", dateStr: "11:3:4 AM", date: makeDateTime 2017 4 12 11 3 4 234 } - , { format: "hh a", dateStr: "12 AM", date: makeDateTime 0 1 1 0 0 0 0 } - , { format: "hh a", dateStr: "12 PM", date: makeDateTime 0 1 1 12 0 0 0 } - , { format: "hh:mm:ss a", dateStr: "11:03:04 AM", date: makeDateTime 2017 4 12 11 3 4 234 } + , { format: "hh:m:s A", dateStr: "11:3:4 AM", date: makeDateTime 2017 4 12 11 3 4 234 } + , { format: "hh:m:s a", dateStr: "11:3:4 am", date: makeDateTime 2017 4 12 11 3 4 234 } + , { format: "hh A", dateStr: "12 AM", date: makeDateTime 0 1 1 0 0 0 0 } + , { format: "hh A", dateStr: "12 PM", date: makeDateTime 0 1 1 12 0 0 0 } + , { format: "hh a", dateStr: "12 am", date: makeDateTime 0 1 1 0 0 0 0 } + , { format: "hh a", dateStr: "12 pm", date: makeDateTime 0 1 1 12 0 0 0 } + , { format: "hh:mm:ss A", dateStr: "11:03:04 AM", date: makeDateTime 2017 4 12 11 3 4 234 } , { format: "hh:mm:ss.SSS", dateStr: "11:12:30.123", date: makeDateTime 2017 4 10 11 12 30 123 } , { format: "hh:mm:ss.SSS", dateStr: "11:12:30.023", date: makeDateTime 2017 4 10 11 12 30 23 } , { format: "hh:mm:ss.SSS", dateStr: "11:12:30.003", date: makeDateTime 2017 4 10 11 12 30 3 } , { format: "hh:mm:ss.SS", dateStr: "11:12:30.12", date: makeDateTime 2017 4 10 11 12 30 123 } , { format: "hh:mm:ss.S", dateStr: "11:12:30.1", date: makeDateTime 2017 4 10 11 12 30 123 } - , { format: "hhmmss a", dateStr: "110304 AM", date: makeDateTime 2017 4 12 11 3 4 234 } + , { format: "hhmmss A", dateStr: "110304 AM", date: makeDateTime 2017 4 12 11 3 4 234 } , { format: "hhmmssSSS", dateStr: "111230123", date: makeDateTime 2017 4 10 11 12 30 123 } , { format: "hhmmssSSS", dateStr: "111230023", date: makeDateTime 2017 4 10 11 12 30 23 } , { format: "hhmmssSSS", dateStr: "111230003", date: makeDateTime 2017 4 10 11 12 30 3 } @@ -86,6 +119,13 @@ datetimeTest = describe "Data.Formatter.DateTime" do invalidDateformats (\f -> (FDT.parseFormatString f.str) `shouldEqual` (Left $ "Expected EOF " <> f.pos)) + describe "parseFormatString/printFormatter should roundtrip" do + forAll + _.str + "should parse" + dateformats + (\f -> (FDT.printFormatter <$> FDT.parseFormatString f.str) `shouldEqual` (Right f.str)) + forAll (\a -> a.format <> " | " <> a.date) "s ≡ format (unformat s)" @@ -122,8 +162,6 @@ dates = invalidDateformats :: Array { str :: String, pos :: String } invalidDateformats = [ { str: "YY-h-dddd HH:mm Z", pos: "(line 1, col 4)" } - , { str: "YYYY-MM-DD M", pos: "(line 1, col 12)" } - , { str: "YYYYM", pos: "(line 1, col 5)" } , { str: "YYYY-MM-DD[ [at] ] hh", pos: "(line 1, col 11)" } , { str: "YYYY-MM-DD [at hh", pos: "(line 1, col 12)" } , { str: "YYYY-MM-DD at] hh", pos: "(line 1, col 14)" } @@ -141,6 +179,24 @@ dateformats = , FDT.DayOfMonthTwoDigits ] } + , { str: "HH:mmA" + , lossless: false + , format: fromFoldable + [ FDT.Hours24 + , FDT.Placeholder ":" + , FDT.MinutesTwoDigits + , FDT.Meridiem + ] + } + , { str: "HH:mma" + , lossless: false + , format: fromFoldable + [ FDT.Hours24 + , FDT.Placeholder ":" + , FDT.MinutesTwoDigits + , FDT.MeridiemLowerCase + ] + } , { str: "Y-MM-DD HH:mm:ss:SSS" , lossless: true , format: fromFoldable @@ -200,7 +256,7 @@ dateformats = , FDT.MinutesTwoDigits ] } - , { str: "Y-MM-DD [at] HH:mm:ss:SSS" + , { str: "Y-MM-DD[ at ]HH:mm:ss:SSS" , lossless: true , format: fromFoldable [ FDT.YearAbsolute @@ -230,4 +286,15 @@ dateformats = , FDT.Milliseconds ] } + , { str: "YYYY-DD-MM[X]" + , lossless: false + , format: fromFoldable + [ FDT.YearFull + , FDT.Placeholder "-" + , FDT.DayOfMonthTwoDigits + , FDT.Placeholder "-" + , FDT.MonthTwoDigits + , FDT.Placeholder "X" + ] + } ] diff --git a/test/src/Number.purs b/test/src/Number.purs index 275d6dd..573c297 100644 --- a/test/src/Number.purs +++ b/test/src/Number.purs @@ -3,13 +3,38 @@ module Test.Number (numberTest) where import Prelude import Control.Monad.Reader.Class (class MonadReader) -import Data.Either (Either(..)) -import Data.Formatter.Number (Formatter(..), format, parseFormatString, printFormatter, unformat, withSeparators) +import Data.Either (Either(..), isLeft) +import Data.Formatter.Number (Formatter(..), format, parseFormatString, printFormatter, unformat, unformatNumber, withSeparators) import Effect.Aff.Class (class MonadAff) import Test.Utils (forAll, describe, shouldEqual) numberTest :: forall m. MonadReader Int m => MonadAff m => m Unit numberTest = describe "Data.Formatter.Number" do + forAll _.input "parses numbers without Int overflow or fractional rescaling" + [ { pattern: "0.0", input: "2147483647.0", expected: 2147483647.0 } + , { pattern: "0.0", input: "2147483648.0", expected: 2147483648.0 } + , { pattern: "0.0", input: "-2147483649.0", expected: -2147483649.0 } + , { pattern: "0.0", input: "1507142848002.0", expected: 1507142848002.0 } + , { pattern: "0.0", input: "9007199254740991.0", expected: 9007199254740991.0 } + , { pattern: "00.00", input: "50.0234252", expected: 50.0234252 } + , { pattern: "0.0", input: "-50.0234252", expected: -50.0234252 } + , { pattern: "0.00", input: "0.000000000123456789", expected: 0.000000000123456789 } + , { pattern: "0.0", input: "1.2345678901234567", expected: 1.2345678901234567 } + , { pattern: "0,0.00", input: "1,507,142,848,002.25", expected: 1507142848002.25 } + , { pattern: "+0.00a", input: "+1.250K", expected: 1250.0 } + ] + (\{ pattern, input, expected } -> unformatNumber pattern input `shouldEqual` Right expected) + + forAll identity "keeps minimum widths and rejects malformed input" + [ "5.00", "50.0", "50.00x", "50.00.1" ] + (\input -> isLeft (unformatNumber "00.00a" input) `shouldEqual` true) + + forAll _.input "parses localized numbers without overflow or rescaling" + [ { input: "1.507.142.848.002,25", expected: 1507142848002.25 } + , { input: "-50,0234252", expected: -50.0234252 } + ] + (\{ input, expected } -> unformat (germanStyleSeparators fmt1') input `shouldEqual` Right expected) + forAll _.str "should print formatter" numberformatts