diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 00000000..d0cbda79 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,25 @@ +{ + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "contributors": [ + { + "login": "sandstreamdevelopment", + "name": "sandstreamdevelopment", + "avatar_url": "https://avatars2.githubusercontent.com/u/44231396?v=4", + "profile": "https://github.com/sandstreamdevelopment", + "contributions": [ + "business", + "financial", + "ideas" + ] + } + ], + "contributorsPerLine": 7, + "projectName": "std", + "projectOwner": "sandstreamdev", + "repoType": "github", + "repoHost": "https://github.com" +} diff --git a/.gitignore b/.gitignore index ad46b308..2b0ef73d 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ typings/ # next.js build output .next + +index.cjs.js +index.umd.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..eb4647a4 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +regenerate.js diff --git a/README.md b/README.md index 0d24f20d..3584d1ef 100644 --- a/README.md +++ b/README.md @@ -1 +1,19 @@ -# std \ No newline at end of file +# std + +[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors) + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + +
sandstreamdevelopment
sandstreamdevelopment

💼 💵 🤔
+ + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/array/any.js b/array/any.js new file mode 100644 index 00000000..f9a2b66c --- /dev/null +++ b/array/any.js @@ -0,0 +1 @@ +export default xs => xs && xs.length > 0; diff --git a/array/are.js b/array/are.js new file mode 100644 index 00000000..ba83b30e --- /dev/null +++ b/array/are.js @@ -0,0 +1,3 @@ +import is from "./is.js"; + +export default (...xs) => xs.every(is); diff --git a/array/difference.js b/array/difference.js new file mode 100644 index 00000000..63a40ba6 --- /dev/null +++ b/array/difference.js @@ -0,0 +1,5 @@ +export default (xs, ys) => { + const zs = new Set(ys); + + return xs.filter(x => !zs.has(x)); +}; diff --git a/array/differs.js b/array/differs.js new file mode 100644 index 00000000..1fac6689 --- /dev/null +++ b/array/differs.js @@ -0,0 +1,5 @@ +export default (xs, ys) => + (!xs && ys) || + (!ys && xs) || + xs.length !== ys.length || + xs.some((x, index) => x !== ys[index]); diff --git a/array/duplicates.js b/array/duplicates.js new file mode 100644 index 00000000..f3ff72f7 --- /dev/null +++ b/array/duplicates.js @@ -0,0 +1,2 @@ +export default xs => + xs.filter((value, index, self) => self.indexOf(value) !== index); diff --git a/array/empty.js b/array/empty.js new file mode 100644 index 00000000..d6d1738d --- /dev/null +++ b/array/empty.js @@ -0,0 +1 @@ +export default []; diff --git a/array/exact.js b/array/exact.js new file mode 100644 index 00000000..c5080095 --- /dev/null +++ b/array/exact.js @@ -0,0 +1,3 @@ +import range from "./range.js"; + +export default n => xs => range(n).map(index => xs[index]); diff --git a/array/except.js b/array/except.js new file mode 100644 index 00000000..b721e9d4 --- /dev/null +++ b/array/except.js @@ -0,0 +1 @@ +export default y => xs => xs.filter(x => x !== y); diff --git a/array/filterInPlace.js b/array/filterInPlace.js new file mode 100644 index 00000000..8b065931 --- /dev/null +++ b/array/filterInPlace.js @@ -0,0 +1,15 @@ +export default f => xs => { + let i = 0; + let j = 0; + + while (i < xs.length) { + const value = xs[i]; + if (f(value, i, xs)) { + xs[j++] = value; + } + i++; + } + + xs.length = j; + return xs; +}; diff --git a/array/find.js b/array/find.js new file mode 100644 index 00000000..9e4018a0 --- /dev/null +++ b/array/find.js @@ -0,0 +1,4 @@ +export default (predicate, fallback) => xs => { + const target = xs.find(predicate); + return target !== undefined ? target : fallback; +}; diff --git a/array/first.js b/array/first.js new file mode 100644 index 00000000..42b96520 --- /dev/null +++ b/array/first.js @@ -0,0 +1 @@ +export default ([x]) => x; diff --git a/array/flatMap.js b/array/flatMap.js new file mode 100644 index 00000000..00184cb9 --- /dev/null +++ b/array/flatMap.js @@ -0,0 +1 @@ +export default f => xs => xs.reduce((ys, y) => ys.concat(f(y)), []); diff --git a/array/flatten.js b/array/flatten.js new file mode 100644 index 00000000..936f23dd --- /dev/null +++ b/array/flatten.js @@ -0,0 +1 @@ +export default xs => [].concat(...xs); diff --git a/array/index.js b/array/index.js new file mode 100644 index 00000000..db09ff73 --- /dev/null +++ b/array/index.js @@ -0,0 +1,126 @@ +import any from "./any.js"; +import are from "./are.js"; +import difference from "./difference.js"; +import differs from "./differs.js"; +import duplicates from "./duplicates.js"; +import empty from "./empty.js"; +import exact from "./exact.js"; +import except from "./except.js"; +import filterInPlace from "./filterInPlace.js"; +import find from "./find.js"; +import first from "./first.js"; +import flatMap from "./flatMap.js"; +import flatten from "./flatten.js"; +import intersection from "./intersection.js"; +import is from "./is.js"; +import last from "./last.js"; +import lengthDiffers from "./lengthDiffers.js"; +import map from "./map.js"; +import midpoint from "./midpoint.js"; +import minMax from "./minMax.js"; +import multiple from "./multiple.js"; +import none from "./none.js"; +import partition from "./partition.js"; +import range from "./range.js"; +import repeat from "./repeat.js"; +import reverse from "./reverse.js"; +import reverseIf from "./reverseIf.js"; +import rotate from "./rotate.js"; +import second from "./second.js"; +import secondToLast from "./secondToLast.js"; +import shift from "./shift.js"; +import shuffle from "./shuffle.js"; +import shuffleInPlace from "./shuffleInPlace.js"; +import single from "./single.js"; +import sort from "./sort.js"; +import sum from "./sum.js"; +import transpose from "./transpose.js"; +import unique from "./unique.js"; +import zip from "./zip.js"; +import zipWith from "./zipWith.js"; + +export { + any, + are, + difference, + differs, + duplicates, + empty, + exact, + except, + filterInPlace, + find, + first, + flatMap, + flatten, + intersection, + is, + last, + lengthDiffers, + map, + midpoint, + minMax, + multiple, + none, + partition, + range, + repeat, + reverse, + reverseIf, + rotate, + second, + secondToLast, + shift, + shuffle, + shuffleInPlace, + single, + sort, + sum, + transpose, + unique, + zip, + zipWith +}; + +export default { + any, + are, + difference, + differs, + duplicates, + empty, + exact, + except, + filterInPlace, + find, + first, + flatMap, + flatten, + intersection, + is, + last, + lengthDiffers, + map, + midpoint, + minMax, + multiple, + none, + partition, + range, + repeat, + reverse, + reverseIf, + rotate, + second, + secondToLast, + shift, + shuffle, + shuffleInPlace, + single, + sort, + sum, + transpose, + unique, + zip, + zipWith +}; diff --git a/array/intersection.js b/array/intersection.js new file mode 100644 index 00000000..647ae519 --- /dev/null +++ b/array/intersection.js @@ -0,0 +1 @@ +export default (xs, ys) => xs.filter(value => ys.includes(value)); diff --git a/array/is.js b/array/is.js new file mode 100644 index 00000000..b0f97bbe --- /dev/null +++ b/array/is.js @@ -0,0 +1 @@ +export default value => Array.isArray(value); diff --git a/array/last.js b/array/last.js new file mode 100644 index 00000000..8302bdbb --- /dev/null +++ b/array/last.js @@ -0,0 +1 @@ +export default xs => xs[xs.length - 1]; diff --git a/array/lengthDiffers.js b/array/lengthDiffers.js new file mode 100644 index 00000000..f8019d11 --- /dev/null +++ b/array/lengthDiffers.js @@ -0,0 +1 @@ +export default (a, b) => a.length !== b.length; diff --git a/array/map.js b/array/map.js new file mode 100644 index 00000000..a61d5d13 --- /dev/null +++ b/array/map.js @@ -0,0 +1,4 @@ +export default (...fs) => { + const f = x => fs.reduce((x, f) => f(x), x); + return x => x.map(f); +}; diff --git a/array/midpoint.js b/array/midpoint.js new file mode 100644 index 00000000..6acdb881 --- /dev/null +++ b/array/midpoint.js @@ -0,0 +1 @@ +export default xs => xs[Math.floor(xs.length / 2)]; diff --git a/array/minMax.js b/array/minMax.js new file mode 100644 index 00000000..a1129efa --- /dev/null +++ b/array/minMax.js @@ -0,0 +1,7 @@ +const { max, min } = Math; + +export default ([head, ...tail]) => + tail.reduce(([min, max], current) => [min(min, current), max(max, current)], [ + head, + head + ]); diff --git a/array/multiple.js b/array/multiple.js new file mode 100644 index 00000000..19d1cdb0 --- /dev/null +++ b/array/multiple.js @@ -0,0 +1 @@ +export default xs => xs.length > 1; diff --git a/array/none.js b/array/none.js new file mode 100644 index 00000000..307b11e6 --- /dev/null +++ b/array/none.js @@ -0,0 +1,3 @@ +import any from "./any.js"; + +export default xs => !any(xs); diff --git a/array/partition.js b/array/partition.js new file mode 100644 index 00000000..039a6ce7 --- /dev/null +++ b/array/partition.js @@ -0,0 +1,8 @@ +export default predicate => xs => + xs.reduce( + ([left, right], current) => { + const pass = predicate(current); + return pass ? [left, [...right, current]] : [[...left, current], right]; + }, + [[], []] + ); diff --git a/array/range.js b/array/range.js new file mode 100644 index 00000000..837dd682 --- /dev/null +++ b/array/range.js @@ -0,0 +1,4 @@ +export default n => + Array(n) + .fill() + .map((_, index) => index); diff --git a/array/repeat.js b/array/repeat.js new file mode 100644 index 00000000..e90082e8 --- /dev/null +++ b/array/repeat.js @@ -0,0 +1 @@ +export default n => value => Array(n).fill(value); diff --git a/array/reverse.js b/array/reverse.js new file mode 100644 index 00000000..75a00a68 --- /dev/null +++ b/array/reverse.js @@ -0,0 +1 @@ +export default xs => [...xs].reverse(); diff --git a/array/reverseIf.js b/array/reverseIf.js new file mode 100644 index 00000000..dacd1541 --- /dev/null +++ b/array/reverseIf.js @@ -0,0 +1,3 @@ +import reverse from "./reverse.js"; + +export default predicate => xs => (predicate ? reverse(xs) : xs); diff --git a/array/rotate.js b/array/rotate.js new file mode 100644 index 00000000..32a160cd --- /dev/null +++ b/array/rotate.js @@ -0,0 +1,8 @@ +export default array => angle => { + const margin = Math.PI / 8; + const angleWithMargin = angle + margin; + const unit = Math.PI / 4; + const ratio = angleWithMargin / unit; + const offset = Math.floor(ratio); + return shift(offset)(array); +}; diff --git a/array/second.js b/array/second.js new file mode 100644 index 00000000..41953d02 --- /dev/null +++ b/array/second.js @@ -0,0 +1 @@ +export default ([, x]) => x; diff --git a/array/secondToLast.js b/array/secondToLast.js new file mode 100644 index 00000000..134e4b9c --- /dev/null +++ b/array/secondToLast.js @@ -0,0 +1 @@ +export default xs => xs[xs.length - 2]; diff --git a/array/shift.js b/array/shift.js new file mode 100644 index 00000000..b0cc0987 --- /dev/null +++ b/array/shift.js @@ -0,0 +1,2 @@ +export default n => xs => + xs.map((_, index) => xs[(index + (n % xs.length) + xs.length) % xs.length]); diff --git a/array/shuffle.js b/array/shuffle.js new file mode 100644 index 00000000..66db53c0 --- /dev/null +++ b/array/shuffle.js @@ -0,0 +1,3 @@ +import shuffleInPlace from "./shuffleInPlace.js"; + +export default xs => shuffleInPlace([...xs]); diff --git a/array/shuffleInPlace.js b/array/shuffleInPlace.js new file mode 100644 index 00000000..ae39da13 --- /dev/null +++ b/array/shuffleInPlace.js @@ -0,0 +1,8 @@ +export default xs => { + for (let i = 0; i < xs.length; i++) { + const j = Math.floor(Math.random() * (i + 1)); + [xs[i], xs[j]] = [xs[j], xs[i]]; + } + + return xs; +}; diff --git a/array/single.js b/array/single.js new file mode 100644 index 00000000..bdc53c3f --- /dev/null +++ b/array/single.js @@ -0,0 +1 @@ +export default xs => xs.length === 1; diff --git a/array/sort.js b/array/sort.js new file mode 100644 index 00000000..645230d6 --- /dev/null +++ b/array/sort.js @@ -0,0 +1 @@ +export default f => xs => [...xs].sort(f); diff --git a/array/sum.js b/array/sum.js new file mode 100644 index 00000000..3d49a4b1 --- /dev/null +++ b/array/sum.js @@ -0,0 +1,3 @@ +const add = (a, b) => a + b; + +export default xs => xs.reduce(add, 0); diff --git a/array/transpose.js b/array/transpose.js new file mode 100644 index 00000000..9da83662 --- /dev/null +++ b/array/transpose.js @@ -0,0 +1 @@ +export default xs => Object.keys(xs[0]).map(key => [xs.map(x => x[key]), key]); diff --git a/array/unique.js b/array/unique.js new file mode 100644 index 00000000..f03af709 --- /dev/null +++ b/array/unique.js @@ -0,0 +1 @@ +export default xs => [...new Set(xs)]; diff --git a/array/zip.js b/array/zip.js new file mode 100644 index 00000000..dcd8e433 --- /dev/null +++ b/array/zip.js @@ -0,0 +1,3 @@ +import zipWith from "./zipWith.js"; + +export default zipWith((x, y) => [x, y]); diff --git a/array/zipWith.js b/array/zipWith.js new file mode 100644 index 00000000..7cd6a442 --- /dev/null +++ b/array/zipWith.js @@ -0,0 +1 @@ +export default f => (xs, ys) => xs.map((x, index) => f(x, ys[index])); diff --git a/async/debounce.js b/async/debounce.js new file mode 100644 index 00000000..11423a1a --- /dev/null +++ b/async/debounce.js @@ -0,0 +1,15 @@ +export default (f, wait) => { + let timeout; + + return (...args) => { + const resolve = () => { + timeout = null; + + f(...args); + }; + + clearTimeout(timeout); + + timeout = setTimeout(resolve, wait); + }; +}; diff --git a/async/delay.js b/async/delay.js new file mode 100644 index 00000000..0c7bc064 --- /dev/null +++ b/async/delay.js @@ -0,0 +1,2 @@ +export default duration => + new Promise(resolve => setTimeout(resolve, duration)); diff --git a/async/index.js b/async/index.js new file mode 100644 index 00000000..8c3a3d37 --- /dev/null +++ b/async/index.js @@ -0,0 +1,7 @@ +import debounce from "./debounce.js"; +import delay from "./delay.js"; +import sequence from "./sequence.js"; + +export { debounce, delay, sequence }; + +export default { debounce, delay, sequence }; diff --git a/async/sequence.js b/async/sequence.js new file mode 100644 index 00000000..f689e49c --- /dev/null +++ b/async/sequence.js @@ -0,0 +1,14 @@ +export default async tasks => { + const results = tasks.map(_ => undefined); + + await tasks.reduce((chain, current, i) => { + return chain.then(() => + current().then(x => { + results[i] = x; + return x; + }) + ); + }, Promise.resolve()); + + return results; +}; diff --git a/date/byDateWithFallback.js b/date/byDateWithFallback.js new file mode 100644 index 00000000..3c6c5296 --- /dev/null +++ b/date/byDateWithFallback.js @@ -0,0 +1,18 @@ +export default now => ( + { endedAt: aEnd, startedAt: aStart }, + { endedAt: bEnd, startedAt: bStart } +) => { + const aEndDate = new Date(aEnd || now); + const aStartDate = new Date(aStart || now); + const bEndDate = new Date(bEnd || now); + const bStartDate = new Date(bStart || now); + const aEndDateValue = aEndDate.valueOf(); + const aStartDateValue = aStartDate.valueOf(); + const bEndDateValue = bEndDate.valueOf(); + const bStartDateValue = bStartDate.valueOf(); + + const startDateDifference = aStartDateValue - bStartDateValue; + const startDatesEqual = startDateDifference === 0; + + return startDatesEqual ? aEndDateValue - bEndDateValue : startDateDifference; +}; diff --git a/date/clamp.js b/date/clamp.js new file mode 100644 index 00000000..70e2c31a --- /dev/null +++ b/date/clamp.js @@ -0,0 +1,7 @@ +export default (min, max) => dateStringOrDate => { + const date = new Date(dateStringOrDate); + const clamped = new Date( + Math.min(max.valueOf(), Math.max(min.valueOf(), date.valueOf())) + ); + return clamped; +}; diff --git a/date/dateDiff.js b/date/dateDiff.js new file mode 100644 index 00000000..2e4d2ce6 --- /dev/null +++ b/date/dateDiff.js @@ -0,0 +1,6 @@ +export default (a, b) => { + const d1 = new Date(a); + const d2 = new Date(b); + + return d1.valueOf() - d2.valueOf(); +}; diff --git a/date/dateInRange.js b/date/dateInRange.js new file mode 100644 index 00000000..29997ef7 --- /dev/null +++ b/date/dateInRange.js @@ -0,0 +1,7 @@ +export default (from, to) => (date = new Date()) => { + const dateTime = new Date(date).getTime(); + const fromTime = new Date(from).getTime(); + const toTime = new Date(to).getTime(); + + return dateTime >= fromTime && dateTime <= toTime; +}; diff --git a/date/dayRange.js b/date/dayRange.js new file mode 100644 index 00000000..2cea4450 --- /dev/null +++ b/date/dayRange.js @@ -0,0 +1,24 @@ +import endOfDay from "./endOfDay.js"; +import offsetByBit from "./offsetByBit.js"; +import splitDateTime from "./splitDateTime.js"; +import startOfDay from "./startOfDay.js"; +import toISO from "./toISO.js"; +import toISOFromLocalDateTime from "./toISOFromLocalDateTime.js"; + +export default ({ + iso = false, + local = true, + now = new Date(), + timezoneOffset = 0 +}) => date => { + const convert = iso ? toISO : toISOFromLocalDateTime; + + const [start] = splitDateTime( + convert(startOfDay(date || now, timezoneOffset, local)) + ); + const [end] = splitDateTime( + convert(offsetByBit(endOfDay(date || now, timezoneOffset, local))) + ); + + return [start, end]; +}; diff --git a/date/daysInMonths.js b/date/daysInMonths.js new file mode 100644 index 00000000..2ad5657f --- /dev/null +++ b/date/daysInMonths.js @@ -0,0 +1,14 @@ +export default leapYear => [ + 31, + leapYear ? 29 : 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +]; diff --git a/date/daysInYear.js b/date/daysInYear.js new file mode 100644 index 00000000..6df73661 --- /dev/null +++ b/date/daysInYear.js @@ -0,0 +1,5 @@ +import sum from "../array/sum.js"; +import daysInMonths from "./daysInMonths.js"; +import leapYear from "./leapYear.js"; + +export default year => sum(daysInMonths(leapYear(year))); diff --git a/date/displayMonth.js b/date/displayMonth.js new file mode 100644 index 00000000..83a1e0b6 --- /dev/null +++ b/date/displayMonth.js @@ -0,0 +1,3 @@ +import monthNames from "./monthNames"; + +export default monthIndex => monthNames[monthIndex % 12]; diff --git a/date/displayTime.js b/date/displayTime.js new file mode 100644 index 00000000..fc78bc18 --- /dev/null +++ b/date/displayTime.js @@ -0,0 +1,14 @@ +export default (source, showSeconds) => { + const [hours, minutes, seconds] = source.map(_ => _ + ""); + + const padded = [ + hours.padStart(2, "0"), + minutes.padStart(2, "0"), + seconds.padStart(2, "0") + ]; + + const [paddedHours, paddedMinutes] = padded; + const parts = showSeconds ? padded : [paddedHours, paddedMinutes]; + + return parts.join(":"); +}; diff --git a/date/endOfDay.js b/date/endOfDay.js new file mode 100644 index 00000000..475fed53 --- /dev/null +++ b/date/endOfDay.js @@ -0,0 +1,9 @@ +import toLocalDateTime from "./toLocalDateTime.js"; + +export default (date, timezoneOffset = 0, local = true) => { + const newDate = new Date(date); + newDate.setHours(24, 0, 0, 0); + return local + ? toLocalDateTime(newDate, timezoneOffset + newDate.getTimezoneOffset()) + : newDate; +}; diff --git a/date/formatDate.js b/date/formatDate.js new file mode 100644 index 00000000..af16eabc --- /dev/null +++ b/date/formatDate.js @@ -0,0 +1,19 @@ +import toLocalDateTime from "./toLocalDateTime.js"; + +export default (sourceDate, timezoneOffset = 0) => { + const localDate = toLocalDateTime(sourceDate, timezoneOffset); + + const [m, a, y] = [ + localDate.getUTCMonth() + 1, + localDate.getUTCDate(), + localDate.getUTCFullYear() + ].map(_ => _ + ""); + + const date = [ + y.padStart(4, "0"), + m.padStart(2, "0"), + a.padStart(2, "0") + ].join("-"); + + return date; +}; diff --git a/date/formatDateTime.js b/date/formatDateTime.js new file mode 100644 index 00000000..6aeb2266 --- /dev/null +++ b/date/formatDateTime.js @@ -0,0 +1,8 @@ +import formatDate from "./formatDate.js"; +import formatTime from "./formatTime.js"; + +export default (sourceDate, showSeconds = false, timezoneOffset = 0) => { + const date = formatDate(sourceDate, timezoneOffset); + const time = formatTime(sourceDate, showSeconds, timezoneOffset); + return `${date} ${time}`; +}; diff --git a/date/formatDisplayDate.js b/date/formatDisplayDate.js new file mode 100644 index 00000000..c3d06b8a --- /dev/null +++ b/date/formatDisplayDate.js @@ -0,0 +1,13 @@ +import toLocalDateTime from "./toLocalDateTime.js"; +import displayMonth from "./displayMonth.js"; + +export default (sourceDate, showDay = false, timezoneOffset = 0) => { + const localDate = toLocalDateTime(sourceDate, timezoneOffset); + const day = localDate.getDate(); + const monthIndex = localDate.getMonth(); + const year = localDate.getFullYear(); + + return [showDay && day, displayMonth(monthIndex), year] + .filter(Boolean) + .join(" "); +}; diff --git a/date/formatDuration.js b/date/formatDuration.js new file mode 100644 index 00000000..1a8ef6f9 --- /dev/null +++ b/date/formatDuration.js @@ -0,0 +1,16 @@ +import fromHours from "./fromHours.js"; +import fromMinutes from "./fromMinutes.js"; +import toHours from "./toHours.js"; +import toMinutes from "./toMinutes.js"; +import toSeconds from "./toSeconds.js"; +import displayTime from "./displayTime.js"; + +export default (duration, showSeconds = false) => { + const hours = Math.floor(toHours(duration)); + const minutes = Math.floor(toMinutes(duration - fromHours(hours))); + const seconds = Math.floor( + toSeconds(duration - fromHours(hours) - fromMinutes(minutes)) + ); + + return displayTime([hours, minutes, seconds], showSeconds); +}; diff --git a/date/formatTime.js b/date/formatTime.js new file mode 100644 index 00000000..6c01768f --- /dev/null +++ b/date/formatTime.js @@ -0,0 +1,14 @@ +import toLocalDateTime from "./toLocalDateTime.js"; +import displayTime from "./displayTime.js"; + +export default (sourceDate, showSeconds = false, timezoneOffset = 0) => { + const localDate = toLocalDateTime(sourceDate, timezoneOffset); + + const source = [ + localDate.getUTCHours(), + localDate.getUTCMinutes(), + localDate.getUTCSeconds() + ]; + + return displayTime(source, showSeconds); +}; diff --git a/date/fromDays.js b/date/fromDays.js new file mode 100644 index 00000000..7961f32d --- /dev/null +++ b/date/fromDays.js @@ -0,0 +1,3 @@ +import fromHours from "./fromHours.js"; + +export default days => fromHours(days * 24); diff --git a/date/fromHours.js b/date/fromHours.js new file mode 100644 index 00000000..07aba8a0 --- /dev/null +++ b/date/fromHours.js @@ -0,0 +1,3 @@ +import fromMinutes from "./fromMinutes.js"; + +export default hours => fromMinutes(hours * 60); diff --git a/date/fromMinutes.js b/date/fromMinutes.js new file mode 100644 index 00000000..53408542 --- /dev/null +++ b/date/fromMinutes.js @@ -0,0 +1,3 @@ +import fromSeconds from "./fromSeconds.js"; + +export default minutes => fromSeconds(minutes * 60); diff --git a/date/fromSeconds.js b/date/fromSeconds.js new file mode 100644 index 00000000..c0770d78 --- /dev/null +++ b/date/fromSeconds.js @@ -0,0 +1 @@ +export default seconds => seconds * 1000; diff --git a/date/index.js b/date/index.js new file mode 100644 index 00000000..bdaf6cbc --- /dev/null +++ b/date/index.js @@ -0,0 +1,117 @@ +import byDateWithFallback from "./byDateWithFallback.js"; +import clamp from "./clamp.js"; +import dateDiff from "./dateDiff.js"; +import dateInRange from "./dateInRange.js"; +import dayRange from "./dayRange.js"; +import daysInMonths from "./daysInMonths.js"; +import daysInYear from "./daysInYear.js"; +import displayMonth from "./displayMonth.js"; +import displayTime from "./displayTime.js"; +import endOfDay from "./endOfDay.js"; +import formatDate from "./formatDate.js"; +import formatDateTime from "./formatDateTime.js"; +import formatDisplayDate from "./formatDisplayDate.js"; +import formatDuration from "./formatDuration.js"; +import formatTime from "./formatTime.js"; +import fromDays from "./fromDays.js"; +import fromHours from "./fromHours.js"; +import fromMinutes from "./fromMinutes.js"; +import fromSeconds from "./fromSeconds.js"; +import joinDateTime from "./joinDateTime.js"; +import leapYear from "./leapYear.js"; +import monthNames from "./monthNames.js"; +import offsetByBit from "./offsetByBit.js"; +import parseHourMinutePair from "./parseHourMinutePair.js"; +import splitDateTime from "./splitDateTime.js"; +import startOfDay from "./startOfDay.js"; +import subtractDays from "./subtractDays.js"; +import toDate from "./toDate.js"; +import toDates from "./toDates.js"; +import toDays from "./toDays.js"; +import toHours from "./toHours.js"; +import toISO from "./toISO.js"; +import toISOFromLocalDateTime from "./toISOFromLocalDateTime.js"; +import toLocalDateTime from "./toLocalDateTime.js"; +import toMinutes from "./toMinutes.js"; +import toSeconds from "./toSeconds.js"; +import valid from "./valid.js"; + +export { + byDateWithFallback, + clamp, + dateDiff, + dateInRange, + dayRange, + daysInMonths, + daysInYear, + displayMonth, + displayTime, + endOfDay, + formatDate, + formatDateTime, + formatDisplayDate, + formatDuration, + formatTime, + fromDays, + fromHours, + fromMinutes, + fromSeconds, + joinDateTime, + leapYear, + monthNames, + offsetByBit, + parseHourMinutePair, + splitDateTime, + startOfDay, + subtractDays, + toDate, + toDates, + toDays, + toHours, + toISO, + toISOFromLocalDateTime, + toLocalDateTime, + toMinutes, + toSeconds, + valid +}; + +export default { + byDateWithFallback, + clamp, + dateDiff, + dateInRange, + dayRange, + daysInMonths, + daysInYear, + displayMonth, + displayTime, + endOfDay, + formatDate, + formatDateTime, + formatDisplayDate, + formatDuration, + formatTime, + fromDays, + fromHours, + fromMinutes, + fromSeconds, + joinDateTime, + leapYear, + monthNames, + offsetByBit, + parseHourMinutePair, + splitDateTime, + startOfDay, + subtractDays, + toDate, + toDates, + toDays, + toHours, + toISO, + toISOFromLocalDateTime, + toLocalDateTime, + toMinutes, + toSeconds, + valid +}; diff --git a/date/joinDateTime.js b/date/joinDateTime.js new file mode 100644 index 00000000..328ef7c9 --- /dev/null +++ b/date/joinDateTime.js @@ -0,0 +1 @@ +export default (...xs) => xs.join("T"); diff --git a/date/leapYear.js b/date/leapYear.js new file mode 100644 index 00000000..62ab3314 --- /dev/null +++ b/date/leapYear.js @@ -0,0 +1 @@ +export default year => new Date(year, 1, 29).getMonth() === 1; diff --git a/date/monthNames.js b/date/monthNames.js new file mode 100644 index 00000000..c870f165 --- /dev/null +++ b/date/monthNames.js @@ -0,0 +1,14 @@ +export default [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +]; diff --git a/date/offsetByBit.js b/date/offsetByBit.js new file mode 100644 index 00000000..6df9dd14 --- /dev/null +++ b/date/offsetByBit.js @@ -0,0 +1,5 @@ +import fromSeconds from "./fromSeconds"; + +const SECOND = fromSeconds(1); + +export default date => new Date(date - SECOND); diff --git a/date/parseHourMinutePair.js b/date/parseHourMinutePair.js new file mode 100644 index 00000000..5862c201 --- /dev/null +++ b/date/parseHourMinutePair.js @@ -0,0 +1,6 @@ +export default (text = "") => { + const [hoursString, minutesString] = text.split(":"); + const hours = parseInt(hoursString || 0, 10); + const minutes = parseInt(minutesString || 0, 10); + return [hours, minutes]; +}; diff --git a/date/splitDateTime.js b/date/splitDateTime.js new file mode 100644 index 00000000..0b871afb --- /dev/null +++ b/date/splitDateTime.js @@ -0,0 +1 @@ +export default dateTimeString => dateTimeString.split("T"); diff --git a/date/startOfDay.js b/date/startOfDay.js new file mode 100644 index 00000000..0e9d3395 --- /dev/null +++ b/date/startOfDay.js @@ -0,0 +1,9 @@ +import toLocalDateTime from "./toLocalDateTime.js"; + +export default (date, timezoneOffset = 0, local = true) => { + const newDate = new Date(date); + newDate.setHours(0, 0, 0, 0); + return local + ? toLocalDateTime(newDate, timezoneOffset + newDate.getTimezoneOffset()) + : newDate; +}; diff --git a/date/subtractDays.js b/date/subtractDays.js new file mode 100644 index 00000000..3dbbb505 --- /dev/null +++ b/date/subtractDays.js @@ -0,0 +1,7 @@ +export default (sourceDate, numberOfDays) => { + const date = new Date(sourceDate); + + date.setDate(date.getDate() - numberOfDays); + + return date; +}; diff --git a/date/toDate.js b/date/toDate.js new file mode 100644 index 00000000..04b7bb21 --- /dev/null +++ b/date/toDate.js @@ -0,0 +1,11 @@ +export default date => { + const day = date.getDate(); + const month = date.getMonth() + 1; + const year = date.getFullYear(); + + return [ + `${year}`.padStart(4, "0"), + `${month}`.padStart(2, "0"), + `${day}`.padStart(2, "0") + ].join("-"); +}; diff --git a/date/toDates.js b/date/toDates.js new file mode 100644 index 00000000..8fb2e180 --- /dev/null +++ b/date/toDates.js @@ -0,0 +1 @@ +export default xs => xs.map(x => new Date(x)); diff --git a/date/toDays.js b/date/toDays.js new file mode 100644 index 00000000..ba5e7a90 --- /dev/null +++ b/date/toDays.js @@ -0,0 +1,3 @@ +import toHours from "./toHours.js"; + +export default milliseconds => toHours(milliseconds) / 24; diff --git a/date/toHours.js b/date/toHours.js new file mode 100644 index 00000000..5ed4eadf --- /dev/null +++ b/date/toHours.js @@ -0,0 +1,3 @@ +import toMinutes from "./toMinutes.js"; + +export default milliseconds => toMinutes(milliseconds) / 60; diff --git a/date/toISO.js b/date/toISO.js new file mode 100644 index 00000000..9ac56e00 --- /dev/null +++ b/date/toISO.js @@ -0,0 +1 @@ +export default x => x.toISOString(); diff --git a/date/toISOFromLocalDateTime.js b/date/toISOFromLocalDateTime.js new file mode 100644 index 00000000..6d8480d3 --- /dev/null +++ b/date/toISOFromLocalDateTime.js @@ -0,0 +1,6 @@ +import fromMinutes from "./fromMinutes.js"; + +export default date => + new Date( + date.valueOf() - fromMinutes(date.getTimezoneOffset()) + ).toISOString(); diff --git a/date/toLocalDateTime.js b/date/toLocalDateTime.js new file mode 100644 index 00000000..90379f1a --- /dev/null +++ b/date/toLocalDateTime.js @@ -0,0 +1,4 @@ +import fromMinutes from "./fromMinutes.js"; + +export default (date, timezoneOffset = 0) => + new Date(date.valueOf() - fromMinutes(timezoneOffset)); diff --git a/date/toMinutes.js b/date/toMinutes.js new file mode 100644 index 00000000..30cfa8ae --- /dev/null +++ b/date/toMinutes.js @@ -0,0 +1,3 @@ +import toSeconds from "./toSeconds.js"; + +export default milliseconds => toSeconds(milliseconds) / 60; diff --git a/date/toSeconds.js b/date/toSeconds.js new file mode 100644 index 00000000..39f86994 --- /dev/null +++ b/date/toSeconds.js @@ -0,0 +1 @@ +export default milliseconds => milliseconds / 1000; diff --git a/date/valid.js b/date/valid.js new file mode 100644 index 00000000..dcd6e7fe --- /dev/null +++ b/date/valid.js @@ -0,0 +1 @@ +export default date => date && date instanceof Date && !Number.isNaN(date); diff --git a/debug/assert.js b/debug/assert.js new file mode 100644 index 00000000..38668ae7 --- /dev/null +++ b/debug/assert.js @@ -0,0 +1,52 @@ +import isNumber from "../is/number.js"; +import isInteger from "../is/integer.js"; +import isByte from "../is/byte.js"; +import isNormal from "../is/normal.js"; +import isString from "../is/string.js"; +import isDefined from "../is/defined.js"; + +const assert = (condition, callbackOrMessage) => { + if (!condition) { + if (typeof callbackOrMessage === "function") { + callbackOrMessage(); + } else { + throw new TypeError( + typeof callbackOrMessage === "string" + ? callbackOrMessage + : "Assertion failed!" + ); + } + } +}; + +export const throws = f => { + try { + f(); + return undefined; + } catch (error) { + return error; + } +}; + +export const assertNumber = x => + assert(isNumber(x), `Value must be a valid number but it is ${typeof x}.`); + +export const assertInteger = x => + assertNumber(x) && assert(isInteger(x), "Value must be an integer."); + +export const assertByte = x => + assertInteger(x) && assert(isByte(x), "Value must be a byte."); + +export const assertNormal = x => + assertNumber(x) && + assert( + isNormal(x), + `Value must be a number in range of 0 to 1 inclusive but it is ${x}.` + ); + +export const assertString = x => assert(isString(x), "Value must be a string."); + +export const assertIsDefined = (x, message = "Value must be defined.") => + assert(isDefined(x), message); + +export default assert; diff --git a/debug/diff.js b/debug/diff.js new file mode 100644 index 00000000..1aa28eaf --- /dev/null +++ b/debug/diff.js @@ -0,0 +1,85 @@ +import filter from "../object/filter.js"; +import none from "../object/none.js"; + +import isDefined from "../is/defined.js"; +import isArray from "../is/array.js"; +import isDate from "../is/date.js"; +import isFunction from "../is/function.js"; +import isObject from "../is/object.js"; + +export const VALUE_CREATED = "+"; +export const VALUE_DELETED = "-"; +export const VALUE_UNCHANGED = "="; +export const VALUE_UPDATED = "~"; + +const isValue = x => !isObject(x) && !isArray(x); + +const compareValues = (value1, value2) => { + if (value1 === value2) { + return VALUE_UNCHANGED; + } + + if ( + isDate(value1) && + isDate(value2) && + value1.getTime() === value2.getTime() + ) { + return VALUE_UNCHANGED; + } + + if (!isDefined(value1)) { + return VALUE_CREATED; + } + + if (!isDefined(value2)) { + return VALUE_DELETED; + } + + return VALUE_UPDATED; +}; + +const diff = (obj1, obj2) => { + if (isFunction(obj1) || isFunction(obj2)) { + throw "Invalid argument. Function given, object expected."; + } + + if (isValue(obj1) || isValue(obj2)) { + const comparisonResult = compareValues(obj1, obj2); + return comparisonResult !== VALUE_UNCHANGED + ? { + type: comparisonResult, + data: [obj1, obj2] + } + : null; + } + + const result = {}; + + for (const key in obj1) { + if (isFunction(obj1[key])) { + continue; + } + + let value2 = undefined; + + if (isDefined(obj2[key])) { + value2 = obj2[key]; + } + + result[key] = diff(obj1[key], value2); + } + + for (const key in obj2) { + if (isFunction(obj2[key]) || isDefined(result[key])) { + continue; + } + + result[key] = diff(undefined, obj2[key]); + } + + return filter( + value => value !== null && !(value && isObject(value) && none(value)) + )(result); +}; + +export default diff; diff --git a/debug/index.js b/debug/index.js new file mode 100644 index 00000000..f85332b7 --- /dev/null +++ b/debug/index.js @@ -0,0 +1,6 @@ +import assert from "./assert.js"; +import diff from "./diff.js"; + +export { assert, diff }; + +export default { assert, diff }; diff --git a/encoding/base64url.js b/encoding/base64url.js new file mode 100644 index 00000000..b053ef7b --- /dev/null +++ b/encoding/base64url.js @@ -0,0 +1,13 @@ +export const encode = _ => + btoa(_) + .replace(/=/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_"); + +export const toBase64Url = base64 => + base64.replace(/\+/g, "-").replace(/\//g, "_"); + +export const fromBase64Url = base64 => + base64.replace(/-/g, "+").replace(/_/g, "/"); + +export default { encode }; diff --git a/encoding/index.js b/encoding/index.js new file mode 100644 index 00000000..78553ab2 --- /dev/null +++ b/encoding/index.js @@ -0,0 +1,5 @@ +import base64url from "./base64url.js"; + +export { base64url }; + +export default { base64url }; diff --git a/file/index.js b/file/index.js new file mode 100644 index 00000000..bbddc8ea --- /dev/null +++ b/file/index.js @@ -0,0 +1,5 @@ +import validName from "./validName.js"; + +export { validName }; + +export default { validName }; diff --git a/file/validName.js b/file/validName.js new file mode 100644 index 00000000..57046aac --- /dev/null +++ b/file/validName.js @@ -0,0 +1,5 @@ +export default name => { + const forbiddenCharacters = /[<>:"\/\\|?*\x00-\x1F]/g; + const forbiddenNames = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + return !forbiddenCharacters.test(name) && !forbiddenNames.test(name); +}; diff --git a/function/compose.js b/function/compose.js new file mode 100644 index 00000000..5fd7d4cc --- /dev/null +++ b/function/compose.js @@ -0,0 +1 @@ +export default (...fs) => x => fs.reduceRight((x, f) => f(x), x); diff --git a/function/constant.js b/function/constant.js new file mode 100644 index 00000000..3487c0dd --- /dev/null +++ b/function/constant.js @@ -0,0 +1 @@ +export default x => () => x; diff --git a/function/identity.js b/function/identity.js new file mode 100644 index 00000000..9a800599 --- /dev/null +++ b/function/identity.js @@ -0,0 +1 @@ +export default x => x; diff --git a/function/index.js b/function/index.js new file mode 100644 index 00000000..cec9a17c --- /dev/null +++ b/function/index.js @@ -0,0 +1,39 @@ +import compose from "./compose.js"; +import constant from "./constant.js"; +import identity from "./identity.js"; +import memoize from "./memoize.js"; +import memoizeShallow from "./memoizeShallow.js"; +import memoizeWith from "./memoizeWith.js"; +import noOp from "./noOp.js"; +import not from "./not.js"; +import pipe from "./pipe.js"; +import when from "./when.js"; +import whenTrue from "./whenTrue.js"; + +export { + compose, + constant, + identity, + memoize, + memoizeShallow, + memoizeWith, + noOp, + not, + pipe, + when, + whenTrue +}; + +export default { + compose, + constant, + identity, + memoize, + memoizeShallow, + memoizeWith, + noOp, + not, + pipe, + when, + whenTrue +}; diff --git a/function/memoize.js b/function/memoize.js new file mode 100644 index 00000000..4ef07411 --- /dev/null +++ b/function/memoize.js @@ -0,0 +1,4 @@ +import equals from "../object/equals.js"; +import memoizeWith from "./memoizeWith.js"; + +export default memoizeWith(equals); diff --git a/function/memoizeShallow.js b/function/memoizeShallow.js new file mode 100644 index 00000000..31528d8d --- /dev/null +++ b/function/memoizeShallow.js @@ -0,0 +1,6 @@ +import memoizeWith from "./memoizeWith"; + +const equalsShallow = (xs, ys) => + xs.length === ys.length && xs.every((x, index) => x === ys[index]); + +export default memoizeWith(equalsShallow); diff --git a/function/memoizeWith.js b/function/memoizeWith.js new file mode 100644 index 00000000..83481bb0 --- /dev/null +++ b/function/memoizeWith.js @@ -0,0 +1,15 @@ +export default equals => f => { + let memoized = undefined; + let memoizedArgs = undefined; + + return (...args) => { + if (memoized && equals(args, memoizedArgs)) { + return memoized; + } + + memoized = f(...args); + memoizedArgs = args; + + return memoized; + }; +}; diff --git a/function/noOp.js b/function/noOp.js new file mode 100644 index 00000000..2d1ec238 --- /dev/null +++ b/function/noOp.js @@ -0,0 +1 @@ +export default () => {}; diff --git a/function/not.js b/function/not.js new file mode 100644 index 00000000..c3411fce --- /dev/null +++ b/function/not.js @@ -0,0 +1 @@ +export default f => (...args) => !f(...args); diff --git a/function/pipe.js b/function/pipe.js new file mode 100644 index 00000000..57e1e197 --- /dev/null +++ b/function/pipe.js @@ -0,0 +1 @@ +export default (...fs) => x => fs.reduce((x, f) => f(x), x); diff --git a/function/when.js b/function/when.js new file mode 100644 index 00000000..c5212ead --- /dev/null +++ b/function/when.js @@ -0,0 +1,5 @@ +export default predicate => action => (...args) => { + if (predicate(...args)) { + return action(...args); + } +}; diff --git a/function/whenTrue.js b/function/whenTrue.js new file mode 100644 index 00000000..0870c5cb --- /dev/null +++ b/function/whenTrue.js @@ -0,0 +1,3 @@ +import when from "./when.js"; + +export default when(x => x === true); diff --git a/index.js b/index.js new file mode 100644 index 00000000..a908ee7f --- /dev/null +++ b/index.js @@ -0,0 +1,54 @@ +import array from "./array/index.js"; +import async from "./async/index.js"; +import date from "./date/index.js"; +import debug from "./debug/index.js"; +import encoding from "./encoding/index.js"; +import file from "./file/index.js"; +import _function from "./function/index.js"; +import is from "./is/index.js"; +import math from "./math/index.js"; +import object from "./object/index.js"; +import query from "./query/index.js"; +import range from "./range/index.js"; +import regex from "./regex/index.js"; +import string from "./string/index.js"; +import vector2 from "./vector2/index.js"; +import web from "./web/index.js"; + +export { + array, + async, + date, + debug, + encoding, + file, + _function, + is, + math, + object, + query, + range, + regex, + string, + vector2, + web +}; + +export default { + array, + async, + date, + debug, + encoding, + file, + _function, + is, + math, + object, + query, + range, + regex, + string, + vector2, + web +}; diff --git a/is/array.js b/is/array.js new file mode 100644 index 00000000..11c376b1 --- /dev/null +++ b/is/array.js @@ -0,0 +1 @@ +export default x => Array.isArray(x); diff --git a/is/byte.js b/is/byte.js new file mode 100644 index 00000000..b25231df --- /dev/null +++ b/is/byte.js @@ -0,0 +1,3 @@ +import integer from "./integer.js"; + +export default x => integer(x) && x >= 0 && x <= 255; diff --git a/is/date.js b/is/date.js new file mode 100644 index 00000000..7d89cb83 --- /dev/null +++ b/is/date.js @@ -0,0 +1 @@ +export default _ => ({}.toString.apply(x) === "[object Date]"); diff --git a/is/defined.js b/is/defined.js new file mode 100644 index 00000000..23ddccd1 --- /dev/null +++ b/is/defined.js @@ -0,0 +1 @@ +export default x => x !== undefined; diff --git a/is/function.js b/is/function.js new file mode 100644 index 00000000..e136d4cc --- /dev/null +++ b/is/function.js @@ -0,0 +1 @@ +export default x => typeof x === "function"; diff --git a/is/index.js b/is/index.js new file mode 100644 index 00000000..2880557e --- /dev/null +++ b/is/index.js @@ -0,0 +1,36 @@ +import array from "./array.js"; +import byte from "./byte.js"; +import date from "./date.js"; +import defined from "./defined.js"; +import _function from "./function.js"; +import integer from "./integer.js"; +import normal from "./normal.js"; +import number from "./number.js"; +import object from "./object.js"; +import string from "./string.js"; + +export { + array, + byte, + date, + defined, + _function, + integer, + normal, + number, + object, + string +}; + +export default { + array, + byte, + date, + defined, + _function, + integer, + normal, + number, + object, + string +}; diff --git a/is/integer.js b/is/integer.js new file mode 100644 index 00000000..b79db0fd --- /dev/null +++ b/is/integer.js @@ -0,0 +1,3 @@ +import number from "./number.js"; + +export default x => number(x) && Math.floor(x) === x; diff --git a/is/normal.js b/is/normal.js new file mode 100644 index 00000000..284b2b93 --- /dev/null +++ b/is/normal.js @@ -0,0 +1,3 @@ +import number from "./number.js"; + +export default x => number(x) && x >= 0 && x <= 1; diff --git a/is/number.js b/is/number.js new file mode 100644 index 00000000..71557dcf --- /dev/null +++ b/is/number.js @@ -0,0 +1 @@ +export default x => typeof x === "number" && !Number.isNaN(x); diff --git a/is/object.js b/is/object.js new file mode 100644 index 00000000..0f50c776 --- /dev/null +++ b/is/object.js @@ -0,0 +1 @@ +export default x => ({}.toString.apply(x) === "[object Object]"); diff --git a/is/string.js b/is/string.js new file mode 100644 index 00000000..a330f049 --- /dev/null +++ b/is/string.js @@ -0,0 +1 @@ +export default x => typeof x === "string"; diff --git a/math/add.js b/math/add.js new file mode 100644 index 00000000..fcbea415 --- /dev/null +++ b/math/add.js @@ -0,0 +1 @@ +export default (a, b) => a + b; diff --git a/math/average.js b/math/average.js new file mode 100644 index 00000000..a75f2b0f --- /dev/null +++ b/math/average.js @@ -0,0 +1,3 @@ +import sum from "../array/sum.js"; + +export default xs => sum(xs) / xs.length; diff --git a/math/ceilToNearestPowerOfTwo.js b/math/ceilToNearestPowerOfTwo.js new file mode 100644 index 00000000..8f367cf6 --- /dev/null +++ b/math/ceilToNearestPowerOfTwo.js @@ -0,0 +1 @@ +export default x => Math.pow(2, Math.ceil(Math.log(x) / Math.log(2))); diff --git a/math/clamp.js b/math/clamp.js new file mode 100644 index 00000000..a1ca7f90 --- /dev/null +++ b/math/clamp.js @@ -0,0 +1 @@ +export default (min, max) => x => Math.max(min, Math.min(max, x)); diff --git a/math/clampNormal.js b/math/clampNormal.js new file mode 100644 index 00000000..5be86e87 --- /dev/null +++ b/math/clampNormal.js @@ -0,0 +1,3 @@ +import clamp from "./clamp.js"; + +export default clamp(0, 1); diff --git a/math/clampPercentage.js b/math/clampPercentage.js new file mode 100644 index 00000000..d331be17 --- /dev/null +++ b/math/clampPercentage.js @@ -0,0 +1,3 @@ +import clamp from "./clamp.js"; + +export default clamp(0, 100); diff --git a/math/delta.js b/math/delta.js new file mode 100644 index 00000000..0f32a1e9 --- /dev/null +++ b/math/delta.js @@ -0,0 +1 @@ +export default (a, b) => Math.abs(a - b); diff --git a/math/inRectangleRange.js b/math/inRectangleRange.js new file mode 100644 index 00000000..690b6a9d --- /dev/null +++ b/math/inRectangleRange.js @@ -0,0 +1,2 @@ +export default (width, height) => (x, y) => + x >= 0 && x <= width && y >= 0 && y <= height; diff --git a/math/index.js b/math/index.js new file mode 100644 index 00000000..596c8fe1 --- /dev/null +++ b/math/index.js @@ -0,0 +1,54 @@ +import add from "./add.js"; +import average from "./average.js"; +import ceilToNearestPowerOfTwo from "./ceilToNearestPowerOfTwo.js"; +import clamp from "./clamp.js"; +import clampNormal from "./clampNormal.js"; +import clampPercentage from "./clampPercentage.js"; +import delta from "./delta.js"; +import inRectangleRange from "./inRectangleRange.js"; +import lerp from "./lerp.js"; +import maximumBy from "./maximumBy.js"; +import median from "./median.js"; +import minMax from "./minMax.js"; +import safeNormalize from "./safeNormalize.js"; +import sameSign from "./sameSign.js"; +import standardDeviation from "./standardDeviation.js"; +import subtract from "./subtract.js"; + +export { + add, + average, + ceilToNearestPowerOfTwo, + clamp, + clampNormal, + clampPercentage, + delta, + inRectangleRange, + lerp, + maximumBy, + median, + minMax, + safeNormalize, + sameSign, + standardDeviation, + subtract +}; + +export default { + add, + average, + ceilToNearestPowerOfTwo, + clamp, + clampNormal, + clampPercentage, + delta, + inRectangleRange, + lerp, + maximumBy, + median, + minMax, + safeNormalize, + sameSign, + standardDeviation, + subtract +}; diff --git a/math/lerp.js b/math/lerp.js new file mode 100644 index 00000000..82ee6796 --- /dev/null +++ b/math/lerp.js @@ -0,0 +1 @@ +export default t => (a, b) => a * t + b * (1 - t); diff --git a/math/maximumBy.js b/math/maximumBy.js new file mode 100644 index 00000000..00f7e73b --- /dev/null +++ b/math/maximumBy.js @@ -0,0 +1,2 @@ +export default f => xs => + xs.reduce((acc, curr) => (f(curr) > f(acc) ? curr : acc)); diff --git a/math/median.js b/math/median.js new file mode 100644 index 00000000..4a4e6fad --- /dev/null +++ b/math/median.js @@ -0,0 +1,13 @@ +import sort from "../array/sort.js"; +import subtract from "./subtract.js"; + +export default xs => { + const sorted = sort(subtract)(xs); + const middle = Math.floor(sorted.length / 2); + + if (sorted.length % 2 === 0) { + return (sorted[middle - 1] + sorted[middle]) / 2; + } + + return sorted[middle]; +}; diff --git a/math/minMax.js b/math/minMax.js new file mode 100644 index 00000000..e5b943ce --- /dev/null +++ b/math/minMax.js @@ -0,0 +1 @@ +export default ([a, b]) => (a > b ? [b, a] : [a, b]); diff --git a/math/safeNormalize.js b/math/safeNormalize.js new file mode 100644 index 00000000..cc7a2b57 --- /dev/null +++ b/math/safeNormalize.js @@ -0,0 +1 @@ +export default x => (x !== 0 ? x / Math.abs(x) : 0); diff --git a/math/sameSign.js b/math/sameSign.js new file mode 100644 index 00000000..ab69865e --- /dev/null +++ b/math/sameSign.js @@ -0,0 +1,9 @@ +const filterOutZeros = xs => xs.filter(_ => _ !== 0); + +export default xs => { + const filteredXs = filterOutZeros(xs); + + return ( + Math.abs(filteredXs.map(safeNormalize).reduce(add, 0)) === filteredXs.length + ); +}; diff --git a/math/standardDeviation.js b/math/standardDeviation.js new file mode 100644 index 00000000..3202d84b --- /dev/null +++ b/math/standardDeviation.js @@ -0,0 +1,8 @@ +export default (xs, origin = average(data)) => { + const sumOfSquareDifferences = xs.reduce( + (squareDiffs, x) => squareDiffs + Math.pow(x - origin, 2), + 0 + ); + + return Math.sqrt(sumOfSquareDifferences / (xs.length - 1)); +}; diff --git a/math/subtract.js b/math/subtract.js new file mode 100644 index 00000000..ae5f7e86 --- /dev/null +++ b/math/subtract.js @@ -0,0 +1 @@ +export default (a, b) => a - b; diff --git a/object/any.js b/object/any.js new file mode 100644 index 00000000..0d0edf9b --- /dev/null +++ b/object/any.js @@ -0,0 +1,3 @@ +import length from "./length.js"; + +export default xs => xs && length(xs) > 0; diff --git a/object/apply.js b/object/apply.js new file mode 100644 index 00000000..93526e28 --- /dev/null +++ b/object/apply.js @@ -0,0 +1,5 @@ +import entries from "./entries.js"; +import fromEntries from "./fromEntries.js"; + +export default fs => (...xs) => + fromEntries(entries(fs).map(([key, value]) => [key, value(...xs)])); diff --git a/object/empty.js b/object/empty.js new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/object/empty.js @@ -0,0 +1 @@ +export default {}; diff --git a/object/entries.js b/object/entries.js new file mode 100644 index 00000000..dc4dd62e --- /dev/null +++ b/object/entries.js @@ -0,0 +1,2 @@ +export default Object.entries || + (object => Object.keys(object).map(key => [key, object[key]])); diff --git a/object/enumerable.js b/object/enumerable.js new file mode 100644 index 00000000..a1e0d87a --- /dev/null +++ b/object/enumerable.js @@ -0,0 +1,2 @@ +export default (...xs) => + xs.reduce((acc, curr) => ({ ...acc, [curr]: curr }), {}); diff --git a/object/equals.js b/object/equals.js new file mode 100644 index 00000000..b5d64c4e --- /dev/null +++ b/object/equals.js @@ -0,0 +1,29 @@ +import isObject from "../is/object.js"; +import areArrays from "../array/are.js"; +import lengthDiffers from "../array/lengthDiffers.js"; + +const keySet = (a, b) => [...new Set([...keys(a), ...keys(b)])]; + +export const equalsDeep = (a, b) => { + if (areArrays(a, b)) { + return ( + !lengthDiffers(a, b) && a.every(a, (_, key) => equalsDeep(_, b[key])) + ); + } + return isObject(a) && isObject(b) + ? a === b || keySet(a, b).every(key => equalsDeep(a[key], b[key])) + : a === b; +}; + +export const equalsDeepWith = f => (a, b) => { + if (areArrays(a, b)) { + return ( + !lengthDiffers(a, b) && a.every((_, key) => equalsDeepWith(f)(_, b[key])) + ); + } + return isObject(a) && isObject(b) + ? a === b || keySet(a, b).every(key => equalsDeepWith(f)(a[key], b[key])) + : f(a, b); +}; + +export default equalsDeep; diff --git a/object/filter.js b/object/filter.js new file mode 100644 index 00000000..9209dcae --- /dev/null +++ b/object/filter.js @@ -0,0 +1,5 @@ +import entries from "./entries.js"; +import fromEntries from "./fromEntries.js"; + +export default f => xs => + fromEntries(entries(xs).filter(([key, value]) => f(value, key, xs))); diff --git a/object/find.js b/object/find.js new file mode 100644 index 00000000..bbf04ad0 --- /dev/null +++ b/object/find.js @@ -0,0 +1,4 @@ +import entries from "./entries.js"; + +export default predicate => xs => + entries(xs).find(([key, value]) => predicate(value, key, xs)); diff --git a/object/findKey.js b/object/findKey.js new file mode 100644 index 00000000..0947503b --- /dev/null +++ b/object/findKey.js @@ -0,0 +1 @@ +export default predicate => xs => (find(predicate)(xs) || [])[0]; diff --git a/object/findValue.js b/object/findValue.js new file mode 100644 index 00000000..849893a0 --- /dev/null +++ b/object/findValue.js @@ -0,0 +1 @@ +export default predicate => xs => (find(predicate)(xs) || [])[1]; diff --git a/object/first.js b/object/first.js new file mode 100644 index 00000000..9fafa098 --- /dev/null +++ b/object/first.js @@ -0,0 +1 @@ +export default xs => Object.values(xs)[0]; diff --git a/object/flatMapValues.js b/object/flatMapValues.js new file mode 100644 index 00000000..3318507f --- /dev/null +++ b/object/flatMapValues.js @@ -0,0 +1,5 @@ +import flatMap from "../array/flatMap.js"; +import entries from "./entries.js"; + +export default f => xs => + flatMap(([key, value]) => f(value, key, xs))(entries(xs)); diff --git a/object/fromEntries.js b/object/fromEntries.js new file mode 100644 index 00000000..6b566045 --- /dev/null +++ b/object/fromEntries.js @@ -0,0 +1,6 @@ +export default Object.fromEntries || + (keyValuePairs => + keyValuePairs.reduce( + (acc, [key, value]) => ({ ...acc, [key]: value }), + {} + )); diff --git a/object/groupBy.js b/object/groupBy.js new file mode 100644 index 00000000..25eb4b32 --- /dev/null +++ b/object/groupBy.js @@ -0,0 +1,5 @@ +export default selector => xs => + xs.reduce((acc, x) => { + const key = selector(x); + return { ...acc, [key]: [...(acc[key] || []), x] }; + }, {}); diff --git a/object/hasKey.js b/object/hasKey.js new file mode 100644 index 00000000..5d9b2b74 --- /dev/null +++ b/object/hasKey.js @@ -0,0 +1,2 @@ +export default key => xs => + xs ? Object.prototype.hasOwnProperty.call(xs, key) : false; diff --git a/object/index.js b/object/index.js new file mode 100644 index 00000000..ed337bb8 --- /dev/null +++ b/object/index.js @@ -0,0 +1,72 @@ +import any from "./any.js"; +import apply from "./apply.js"; +import empty from "./empty.js"; +import entries from "./entries.js"; +import enumerable from "./enumerable.js"; +import equals from "./equals.js"; +import filter from "./filter.js"; +import find from "./find.js"; +import findKey from "./findKey.js"; +import findValue from "./findValue.js"; +import first from "./first.js"; +import flatMapValues from "./flatMapValues.js"; +import fromEntries from "./fromEntries.js"; +import groupBy from "./groupBy.js"; +import hasKey from "./hasKey.js"; +import length from "./length.js"; +import map from "./map.js"; +import mapEntries from "./mapEntries.js"; +import mapKeys from "./mapKeys.js"; +import mapValues from "./mapValues.js"; +import none from "./none.js"; +import sort from "./sort.js"; + +export { + any, + apply, + empty, + entries, + enumerable, + equals, + filter, + find, + findKey, + findValue, + first, + flatMapValues, + fromEntries, + groupBy, + hasKey, + length, + map, + mapEntries, + mapKeys, + mapValues, + none, + sort +}; + +export default { + any, + apply, + empty, + entries, + enumerable, + equals, + filter, + find, + findKey, + findValue, + first, + flatMapValues, + fromEntries, + groupBy, + hasKey, + length, + map, + mapEntries, + mapKeys, + mapValues, + none, + sort +}; diff --git a/object/length.js b/object/length.js new file mode 100644 index 00000000..186433f4 --- /dev/null +++ b/object/length.js @@ -0,0 +1 @@ +export default xs => Object.keys(xs).length; diff --git a/object/map.js b/object/map.js new file mode 100644 index 00000000..8590e36b --- /dev/null +++ b/object/map.js @@ -0,0 +1,4 @@ +import fromEntries from "./fromEntries.js"; +import mapEntries from "./mapEntries.js"; + +export default f => xs => fromEntries(mapEntries(f)(xs)); diff --git a/object/mapEntries.js b/object/mapEntries.js new file mode 100644 index 00000000..854de72e --- /dev/null +++ b/object/mapEntries.js @@ -0,0 +1,4 @@ +import entries from "./entries.js"; + +export default f => xs => + entries(xs).map(([key, value]) => [key, f(value, key, xs)]); diff --git a/object/mapKeys.js b/object/mapKeys.js new file mode 100644 index 00000000..a8f5465b --- /dev/null +++ b/object/mapKeys.js @@ -0,0 +1,5 @@ +import entries from "./entries.js"; +import fromEntries from "./fromEntries.js"; + +export default f => xs => + fromEntries(entries(xs).map(([key, value]) => [f(value, key, xs), value])); diff --git a/object/mapValues.js b/object/mapValues.js new file mode 100644 index 00000000..8230ab66 --- /dev/null +++ b/object/mapValues.js @@ -0,0 +1,3 @@ +import entries from "./entries.js"; + +export default f => xs => entries(xs).map(([key, value]) => f(value, key, xs)); diff --git a/object/none.js b/object/none.js new file mode 100644 index 00000000..307b11e6 --- /dev/null +++ b/object/none.js @@ -0,0 +1,3 @@ +import any from "./any.js"; + +export default xs => !any(xs); diff --git a/object/sort.js b/object/sort.js new file mode 100644 index 00000000..a52812d3 --- /dev/null +++ b/object/sort.js @@ -0,0 +1,5 @@ +import entries from "./entries.js"; +import fromEntries from "./fromEntries.js"; + +export default f => xs => + fromEntries(sort(([, a], [, b]) => f(a, b))(entries(xs))); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..a56ea7c2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,43 @@ +{ + "name": "@sandstreamdev/std", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "12.7.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.9.tgz", + "integrity": "sha512-P57oKTJ/vYivL2BCfxCC5tQjlS8qW31pbOL6qt99Yrjm95YdHgNZwjrTTjMBh+C2/y6PXIX4oz253+jUzxKKfQ==", + "dev": true + }, + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + }, + "prettier": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", + "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", + "dev": true + }, + "rollup": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.22.0.tgz", + "integrity": "sha512-x4l4ZrV/Mr/x/jvFTmwROdEAhbZjx16yDRTVSKWh/i4oJDuW2dVEbECT853mybYCz7BAitU8ElGlhx7dNjw3qQ==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..44bbcc25 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "@sandstreamdev/std", + "version": "0.1.0", + "description": "", + "type": "module", + "module": "index.js", + "main": "index.cjs.js", + "browser": "index.umd.js", + "sideEffects": false, + "scripts": { + "build": "npm run regenerate && rollup -c", + "prepare": "npm run build", + "prettier:check": "npm run prettier -- --check", + "prettier:fix": "npm run prettier -- --write", + "prettier": "prettier \"**/*.{js,json,md}\"", + "regenerate": "node --experimental-modules regenerate.js && npm run prettier:fix" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sandstreamdev/std.git" + }, + "keywords": [], + "author": { + "name": "Sandstream Development", + "url": "https://www.sandstream.pl/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/sandstreamdev/std/issues" + }, + "homepage": "https://github.com/sandstreamdev/std#readme", + "devDependencies": { + "prettier": "^1.18.2", + "rollup": "^1.22.0" + } +} diff --git a/query/index.js b/query/index.js new file mode 100644 index 00000000..558489ce --- /dev/null +++ b/query/index.js @@ -0,0 +1,8 @@ +import parse from "./parse.js"; +import parsePathname from "./parsePathname.js"; +import read from "./read.js"; +import serialize from "./serialize.js"; + +export { parse, parsePathname, read, serialize }; + +export default { parse, parsePathname, read, serialize }; diff --git a/query/parse.js b/query/parse.js new file mode 100644 index 00000000..3e2fb4f6 --- /dev/null +++ b/query/parse.js @@ -0,0 +1,17 @@ +import fromEntries from "../object/fromEntries.js"; +import startsWith from "../string/startsWith.js"; + +const startsWithQuestionMark = startsWith("?"); + +const queryFromMaybeSearchString = x => + startsWithQuestionMark(x) ? x.substring(1) : x; + +export default (xs = "") => + fromEntries( + queryFromMaybeSearchString(xs) + .split("&") + .map(xs => { + const [key, value] = xs.split("="); + return [key, value !== undefined ? decodeURIComponent(value) : true]; + }) + ); diff --git a/query/parsePathname.js b/query/parsePathname.js new file mode 100644 index 00000000..1924d222 --- /dev/null +++ b/query/parsePathname.js @@ -0,0 +1,32 @@ +const extractPath = url => { + const match = url.match(/^(https?:)?\/\/[^/]*/); + return match == null ? url : url.substring(match[0].length); +}; + +export default url => { + let pathname = extractPath(url); + let search = ""; + let hash = ""; + + const hashIndex = pathname.indexOf("#"); + if (hashIndex !== -1) { + hash = pathname.substring(hashIndex); + pathname = pathname.substring(0, hashIndex); + } + + const searchIndex = pathname.indexOf("?"); + if (searchIndex !== -1) { + search = pathname.substring(searchIndex); + pathname = pathname.substring(0, searchIndex); + } + + if (pathname === "") { + pathname = "/"; + } + + return { + pathname, + search, + hash + }; +}; diff --git a/query/read.js b/query/read.js new file mode 100644 index 00000000..799c7496 --- /dev/null +++ b/query/read.js @@ -0,0 +1,5 @@ +export default source => + [...new URLSearchParams(source).entries()].reduce( + (q, [k, v]) => ({ ...q, ...{ [k]: v } }), + {} + ); diff --git a/query/serialize.js b/query/serialize.js new file mode 100644 index 00000000..12f23f5f --- /dev/null +++ b/query/serialize.js @@ -0,0 +1,8 @@ +import entries from "../object/entries.js"; + +export default (xs = {}) => + entries(xs) + .filter(([, value]) => Boolean(value) || value === 0) + .map(pair => pair.map(encodeURIComponent)) + .reduce((acc, [key, value]) => [...acc, `${key}=${value}`], []) + .join("&"); diff --git a/range/empty.js b/range/empty.js new file mode 100644 index 00000000..71065567 --- /dev/null +++ b/range/empty.js @@ -0,0 +1 @@ +export default ([min, max]) => min === max; diff --git a/range/equals.js b/range/equals.js new file mode 100644 index 00000000..ab5c1860 --- /dev/null +++ b/range/equals.js @@ -0,0 +1 @@ +export default ([a, b], [c, d]) => a === c && b === d; diff --git a/range/index.js b/range/index.js new file mode 100644 index 00000000..a0e7a341 --- /dev/null +++ b/range/index.js @@ -0,0 +1,8 @@ +import empty from "./empty.js"; +import equals from "./equals.js"; +import length from "./length.js"; +import split from "./split.js"; + +export { empty, equals, length, split }; + +export default { empty, equals, length, split }; diff --git a/range/length.js b/range/length.js new file mode 100644 index 00000000..3961ffcd --- /dev/null +++ b/range/length.js @@ -0,0 +1 @@ +export default ([min, max]) => max - min; diff --git a/range/split.js b/range/split.js new file mode 100644 index 00000000..05ed309d --- /dev/null +++ b/range/split.js @@ -0,0 +1,40 @@ +import isNumber from "../is/number.js"; +import clamp from "../math/clamp.js"; +import empty from "./empty.js"; + +const split = (used, sourceRange = [-Infinity, Infinity]) => range => { + if (empty(range) || !range.every(isNumber)) { + return []; + } + + if (!used || used.length === 0) { + return [range]; + } + + const [x, ...xs] = used; + const [usedMin, usedMax] = x; + + if (empty(x)) { + return split(xs, sourceRange)(range); + } + + const [sourceMin, sourceMax] = sourceRange; + const clampToSourceRange = clamp(...sourceRange); + + const freeLeft = [sourceMin, usedMin].map(clampToSourceRange); + const freeRight = [usedMax, sourceMax].map(clampToSourceRange); + + const clampLeft = clamp(...freeLeft); + const clampedLeft = range.map(clampLeft); + + const lefts = split(xs, sourceRange)(clampedLeft); + + const clampRight = clamp(...freeRight); + const clampedRight = range.map(clampRight); + + const rights = split(xs, sourceRange)(clampedRight); + + return [...lefts, ...rights].filter(range => !empty(range)); +}; + +export default split; diff --git a/regenerate.js b/regenerate.js new file mode 100644 index 00000000..a6c3baaf --- /dev/null +++ b/regenerate.js @@ -0,0 +1,88 @@ +import { promises } from "fs"; +import path from "path"; + +const { readdir: readDirectoryAsync, writeFile: writeFileAsync } = promises; + +const [, , cwd = process.cwd()] = process.argv; + +const mapping = { + function: "_function" +}; + +const ignoredFiles = [ + ".all-contributorsrc", + ".gitignore", + ".npmignore", + "index.cjs.js", + "index.js", + "index.umd.js", + "LICENSE", + "package-lock.json", + "package.json", + "README.md", + "regenerate.js", + "rollup.config.js" +]; + +const ignoredDirectories = [".git", "node_modules"]; + +const identifier = name => mapping[name] || name; + +const main = async cwd => { + console.log(`Indexing files in ${cwd}...`); + + const entries = await readDirectoryAsync(cwd, { withFileTypes: true }); + const files = entries + .filter(x => x.isFile()) + .map(x => x.name) + .filter(x => !ignoredFiles.includes(x)); + const directories = entries + .filter(x => x.isDirectory()) + .map(x => x.name) + .filter(x => !ignoredDirectories.includes(x)); + + for (const directory of directories) { + await main(path.join(cwd, directory)); + } + + const submodules = files.map(filePath => { + const { base: fileName } = path.parse(filePath); + const splitted = fileName.split("."); + const id = identifier(splitted.slice(0, splitted.length - 1).join("_")); + const extension = `.${splitted[splitted.length - 1]}`; + + return [fileName, id, extension]; + }); + + const dependencies = [ + ...submodules, + ...directories.map(x => [`${x}/index.js`, identifier(x), ""]) + ]; + + const importDeclarations = dependencies + .map(([fileName, id]) => + id !== "index" ? `import ${id} from './${fileName}'` : "" + ) + .join("\r\n"); + + const exportDeclarationBody = dependencies + .map(([, id]) => (id !== "index" ? id : "")) + .join(", "); + + const exportDeclaration = `export { ${exportDeclarationBody} }`; + const defaultExport = `export default { ${exportDeclarationBody} }`; + + console.log(`Indexed files in ${cwd}:`); + + const moduleContents = [ + importDeclarations, + exportDeclaration, + defaultExport + ].join("\r\n\r\n"); + + console.log(moduleContents); + + await writeFileAsync(path.join(cwd, "index.js"), moduleContents); +}; + +main(cwd); diff --git a/regex/escape.js b/regex/escape.js new file mode 100644 index 00000000..b9233880 --- /dev/null +++ b/regex/escape.js @@ -0,0 +1 @@ +export default string => string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); diff --git a/regex/index.js b/regex/index.js new file mode 100644 index 00000000..231e89cb --- /dev/null +++ b/regex/index.js @@ -0,0 +1,5 @@ +import escape from "./escape.js"; + +export { escape }; + +export default { escape }; diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..3dabf215 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,16 @@ +export default { + input: "index.js", + output: [ + { + exports: "named", + file: "index.cjs.js", + format: "cjs" + }, + { + exports: "named", + file: "index.umd.js", + format: "umd", + name: "@sandstreamdev/std" + } + ] +}; diff --git a/string/containsWhitespace.js b/string/containsWhitespace.js new file mode 100644 index 00000000..a2a288b0 --- /dev/null +++ b/string/containsWhitespace.js @@ -0,0 +1 @@ +export default x => /\s/.test(x); diff --git a/string/empty.js b/string/empty.js new file mode 100644 index 00000000..9cf3c27e --- /dev/null +++ b/string/empty.js @@ -0,0 +1 @@ +export default ""; diff --git a/string/firstToLower.js b/string/firstToLower.js new file mode 100644 index 00000000..0a54fdd4 --- /dev/null +++ b/string/firstToLower.js @@ -0,0 +1 @@ +export default ([first, ...rest]) => [first.toLowerCase(), ...rest].join(""); diff --git a/string/firstToUpper.js b/string/firstToUpper.js new file mode 100644 index 00000000..7b9b4cba --- /dev/null +++ b/string/firstToUpper.js @@ -0,0 +1 @@ +export default ([first, ...rest]) => [first.toUpperCase(), ...rest].join(""); diff --git a/string/includes.js b/string/includes.js new file mode 100644 index 00000000..e9c27210 --- /dev/null +++ b/string/includes.js @@ -0,0 +1 @@ +export default search => xs => xs.indexOf(search) !== -1; diff --git a/string/index.js b/string/index.js new file mode 100644 index 00000000..af2f7705 --- /dev/null +++ b/string/index.js @@ -0,0 +1,30 @@ +import containsWhitespace from "./containsWhitespace.js"; +import empty from "./empty.js"; +import firstToLower from "./firstToLower.js"; +import firstToUpper from "./firstToUpper.js"; +import includes from "./includes.js"; +import nbsp from "./nbsp.js"; +import nonEmpty from "./nonEmpty.js"; +import startsWith from "./startsWith.js"; + +export { + containsWhitespace, + empty, + firstToLower, + firstToUpper, + includes, + nbsp, + nonEmpty, + startsWith +}; + +export default { + containsWhitespace, + empty, + firstToLower, + firstToUpper, + includes, + nbsp, + nonEmpty, + startsWith +}; diff --git a/string/nbsp.js b/string/nbsp.js new file mode 100644 index 00000000..f5632c43 --- /dev/null +++ b/string/nbsp.js @@ -0,0 +1 @@ +export default "\u00A0"; diff --git a/string/nonEmpty.js b/string/nonEmpty.js new file mode 100644 index 00000000..a66c5d72 --- /dev/null +++ b/string/nonEmpty.js @@ -0,0 +1 @@ +export default x => x && x.trim(); diff --git a/string/startsWith.js b/string/startsWith.js new file mode 100644 index 00000000..8997cd33 --- /dev/null +++ b/string/startsWith.js @@ -0,0 +1 @@ +export default prefix => xs => xs.indexOf(prefix) === 0; diff --git a/vector2/add.js b/vector2/add.js new file mode 100644 index 00000000..6995faae --- /dev/null +++ b/vector2/add.js @@ -0,0 +1 @@ +export default ([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2]; diff --git a/vector2/convertSpace.js b/vector2/convertSpace.js new file mode 100644 index 00000000..7d50c019 --- /dev/null +++ b/vector2/convertSpace.js @@ -0,0 +1,6 @@ +import mul from "./mul.js"; + +export default space => ([x, y]) => { + const [outX, outY] = mul(space, [x, y]); + return [outX, outY]; +}; diff --git a/vector2/cross.js b/vector2/cross.js new file mode 100644 index 00000000..327ae4d7 --- /dev/null +++ b/vector2/cross.js @@ -0,0 +1 @@ +export default ([a, b], [c, d]) => a * d - b * c; diff --git a/vector2/dot.js b/vector2/dot.js new file mode 100644 index 00000000..c18e85fe --- /dev/null +++ b/vector2/dot.js @@ -0,0 +1 @@ +export default ([a, b], [c, d]) => a * c + b * d; diff --git a/vector2/index.js b/vector2/index.js new file mode 100644 index 00000000..b88aa8d8 --- /dev/null +++ b/vector2/index.js @@ -0,0 +1,48 @@ +import add from "./add.js"; +import convertSpace from "./convertSpace.js"; +import cross from "./cross.js"; +import dot from "./dot.js"; +import length from "./length.js"; +import mul from "./mul.js"; +import multiply from "./multiply.js"; +import normalize from "./normalize.js"; +import reflect from "./reflect.js"; +import rotate from "./rotate.js"; +import scale from "./scale.js"; +import sub from "./sub.js"; +import transform from "./transform.js"; +import translate from "./translate.js"; + +export { + add, + convertSpace, + cross, + dot, + length, + mul, + multiply, + normalize, + reflect, + rotate, + scale, + sub, + transform, + translate +}; + +export default { + add, + convertSpace, + cross, + dot, + length, + mul, + multiply, + normalize, + reflect, + rotate, + scale, + sub, + transform, + translate +}; diff --git a/vector2/length.js b/vector2/length.js new file mode 100644 index 00000000..ac12d13b --- /dev/null +++ b/vector2/length.js @@ -0,0 +1 @@ +export default ([x, y]) => Math.sqrt(x ** 2 + y ** 2); diff --git a/vector2/mul.js b/vector2/mul.js new file mode 100644 index 00000000..7ce615f1 --- /dev/null +++ b/vector2/mul.js @@ -0,0 +1,4 @@ +export default (matrix, point) => [ + matrix.a * point[0] + matrix.c * point[1] + matrix.e, + matrix.b * point[0] + matrix.d * point[1] + matrix.f +]; diff --git a/vector2/multiply.js b/vector2/multiply.js new file mode 100644 index 00000000..3102465f --- /dev/null +++ b/vector2/multiply.js @@ -0,0 +1,8 @@ +export default (m1, m2) => ({ + a: m1.a * m2.a + m1.c * m2.b, + c: m1.a * m2.c + m1.c * m2.d, + e: m1.a * m2.e + m1.c * m2.f + m1.e, + b: m1.b * m2.a + m1.d * m2.b, + d: m1.b * m2.c + m1.d * m2.d, + f: m1.b * m2.e + m1.d * m2.f + m1.f +}); diff --git a/vector2/normalize.js b/vector2/normalize.js new file mode 100644 index 00000000..c00359af --- /dev/null +++ b/vector2/normalize.js @@ -0,0 +1,6 @@ +import length from "./length.js"; + +export default vector => { + const magnitude = length(vector); + return magnitude !== 0 ? vector.map(_ => _ / magnitude) : vector; +}; diff --git a/vector2/reflect.js b/vector2/reflect.js new file mode 100644 index 00000000..6ee82a82 --- /dev/null +++ b/vector2/reflect.js @@ -0,0 +1,4 @@ +import sub from "./sub.js"; +import dot from "./dot.js"; + +export default (a, v) => sub(v, a.map(_ => (_ * 2 * dot(v, a)) / dot(a, a))); diff --git a/vector2/rotate.js b/vector2/rotate.js new file mode 100644 index 00000000..2d1dc708 --- /dev/null +++ b/vector2/rotate.js @@ -0,0 +1,20 @@ +import transform from "./transform.js"; +import translate from "./translate.js"; + +const { cos, sin } = Math; + +export default (angle = 0, cx = 0, cy = 0) => { + const cosAngle = cos(angle); + const sinAngle = sin(angle); + + const rotationMatrix = { + a: cosAngle, + c: -sinAngle, + e: 0, + b: sinAngle, + d: cosAngle, + f: 0 + }; + + return transform(translate(cx, cy), rotationMatrix, translate(-cx, -cy)); +}; diff --git a/vector2/scale.js b/vector2/scale.js new file mode 100644 index 00000000..d4aa6aba --- /dev/null +++ b/vector2/scale.js @@ -0,0 +1,8 @@ +export default (sx = 1, sy = sx) => ({ + a: sx, + c: 0, + e: 0, + b: 0, + d: sy, + f: 0 +}); diff --git a/vector2/sub.js b/vector2/sub.js new file mode 100644 index 00000000..5f40a0c4 --- /dev/null +++ b/vector2/sub.js @@ -0,0 +1 @@ +export default ([x1, y1], [x2, y2]) => [x1 - x2, y1 - y2]; diff --git a/vector2/transform.js b/vector2/transform.js new file mode 100644 index 00000000..0fc27235 --- /dev/null +++ b/vector2/transform.js @@ -0,0 +1 @@ +export default (...matrices) => matrices.reduce(multiply); diff --git a/vector2/translate.js b/vector2/translate.js new file mode 100644 index 00000000..2c2048ad --- /dev/null +++ b/vector2/translate.js @@ -0,0 +1,8 @@ +export default (tx = 0, ty = 0) => ({ + a: 1, + c: 0, + e: tx, + b: 0, + d: 1, + f: ty +}); diff --git a/web/classNames.js b/web/classNames.js new file mode 100644 index 00000000..190d8ed5 --- /dev/null +++ b/web/classNames.js @@ -0,0 +1,16 @@ +import entries from "../object/entries.js"; +import isString from "../is/string.js"; + +const booleanKeys = x => + entries(x) + .filter(([, value]) => Boolean(value)) + .map(([key]) => key); + +export default (...xs) => + xs + .filter(Boolean) + .reduce((acc, curr) => { + const names = isString(curr) ? [curr] : booleanKeys(curr); + return [...acc, ...names]; + }, []) + .join(" "); diff --git a/web/events/cancel.js b/web/events/cancel.js new file mode 100644 index 00000000..e2c2c023 --- /dev/null +++ b/web/events/cancel.js @@ -0,0 +1,8 @@ +import prevent from "./prevent.js"; +import stop from "./stop.js"; + +export default event => { + prevent(event); + stop(event); + return false; +}; diff --git a/web/events/index.js b/web/events/index.js new file mode 100644 index 00000000..0809d31a --- /dev/null +++ b/web/events/index.js @@ -0,0 +1,8 @@ +import cancel from "./cancel.js"; +import openInNewTabIntent from "./openInNewTabIntent.js"; +import prevent from "./prevent.js"; +import stop from "./stop.js"; + +export { cancel, openInNewTabIntent, prevent, stop }; + +export default { cancel, openInNewTabIntent, prevent, stop }; diff --git a/web/events/openInNewTabIntent.js b/web/events/openInNewTabIntent.js new file mode 100644 index 00000000..bf0f619f --- /dev/null +++ b/web/events/openInNewTabIntent.js @@ -0,0 +1,2 @@ +export default ({ button, ctrlKey, metaKey, shiftKey }) => + ctrlKey || shiftKey || metaKey || button === 1; diff --git a/web/events/prevent.js b/web/events/prevent.js new file mode 100644 index 00000000..f475eecb --- /dev/null +++ b/web/events/prevent.js @@ -0,0 +1,4 @@ +export default event => { + event.preventDefault(); + return false; +}; diff --git a/web/events/stop.js b/web/events/stop.js new file mode 100644 index 00000000..cadf59cc --- /dev/null +++ b/web/events/stop.js @@ -0,0 +1,4 @@ +export default event => { + event.stopPropagation(); + return false; +}; diff --git a/web/index.js b/web/index.js new file mode 100644 index 00000000..e75e558b --- /dev/null +++ b/web/index.js @@ -0,0 +1,6 @@ +import classNames from "./classNames.js"; +import events from "./events/index.js"; + +export { classNames, events }; + +export default { classNames, events };