@@ -0,0 +1,11 @@ | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
end_of_line = lf | |||||
indent_size = 2 | |||||
indent_style = space | |||||
insert_final_newline = true | |||||
max_line_length = 120 | |||||
tab_width = 8 | |||||
trim_trailing_whitespace = true |
@@ -0,0 +1,5 @@ | |||||
*.log | |||||
.DS_Store | |||||
node_modules | |||||
dist | |||||
.idea/ |
@@ -0,0 +1,6 @@ | |||||
{ | |||||
"jsxSingleQuote": false, | |||||
"singleQuote": true, | |||||
"semi": false, | |||||
"trailingComma": "es5" | |||||
} |
@@ -0,0 +1,21 @@ | |||||
MIT License | |||||
Copyright (c) 2020 TheoryOfNekomata | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. |
@@ -0,0 +1,7 @@ | |||||
# TSDX Bootstrap | |||||
Useful methods for file-related functions. | |||||
## License | |||||
[MIT](./LICENSE) |
@@ -0,0 +1,45 @@ | |||||
{ | |||||
"version": "0.1.0", | |||||
"license": "MIT", | |||||
"main": "dist/index.js", | |||||
"typings": "dist/index.d.ts", | |||||
"files": [ | |||||
"dist", | |||||
"src" | |||||
], | |||||
"engines": { | |||||
"node": ">=10" | |||||
}, | |||||
"scripts": { | |||||
"start": "tsdx watch", | |||||
"build": "tsdx build", | |||||
"test": "tsdx test", | |||||
"lint": "tsdx lint", | |||||
"prepare": "tsdx build" | |||||
}, | |||||
"peerDependencies": { | |||||
"crypto-js": "^4.0.0", | |||||
"numeral": "^2.0.6" | |||||
}, | |||||
"husky": { | |||||
"hooks": { | |||||
"pre-commit": "tsdx lint" | |||||
} | |||||
}, | |||||
"name": "file-commons", | |||||
"author": "Allan Crisostomo", | |||||
"module": "dist/file-commons.esm.js", | |||||
"devDependencies": { | |||||
"@types/crypto-js": "^3.1.47", | |||||
"@types/numeral": "^0.0.28", | |||||
"fast-check": "^2.3.0", | |||||
"husky": "^4.3.0", | |||||
"tsdx": "^0.13.3", | |||||
"tslib": "^2.0.1", | |||||
"typescript": "^4.0.2" | |||||
}, | |||||
"dependencies": { | |||||
"crypto-js": "^4.0.0", | |||||
"numeral": "^2.0.6" | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
import {LibWordArray} from 'crypto-js' | |||||
import sha512 from 'crypto-js/sha512' | |||||
import Hex from 'crypto-js/enc-hex' | |||||
type CalculateHash = (b: string | LibWordArray) => string | |||||
const calculateHash: CalculateHash = b => sha512(b).toString(Hex) | |||||
export default calculateHash |
@@ -0,0 +1,20 @@ | |||||
type DataUriToBlob = (dataUri: string, name?: string) => Blob | |||||
const dataUriToBlob: DataUriToBlob = (dataUri, name) => { | |||||
const [encoding, base64] = dataUri | |||||
.slice('data:'.length) | |||||
.split(',') | |||||
const binary = atob(base64) | |||||
const [type,] = encoding.split(';') | |||||
const ab = new ArrayBuffer(binary.length) | |||||
const ia = new Uint8Array(ab) | |||||
for (let i = 0; i < binary.length; i++) { | |||||
ia[i] = binary.charCodeAt(i) | |||||
} | |||||
if (typeof name! === 'string') { | |||||
return new File([ab], name, { type, }) | |||||
} | |||||
return new Blob([ab], { type, }) | |||||
} | |||||
export default dataUriToBlob |
@@ -0,0 +1,55 @@ | |||||
import * as fc from 'fast-check' | |||||
import formatFileSize from './formatFileSize' | |||||
it('should exist', () => { | |||||
expect(formatFileSize).toBeDefined() | |||||
}) | |||||
it('should be a function', () => { | |||||
expect(typeof formatFileSize).toBe('function') | |||||
}) | |||||
it('should take 1 argument', () => { | |||||
expect(formatFileSize).toHaveLength(1) | |||||
}) | |||||
describe('on numeric arguments', () => { | |||||
it('should format values of |x| < 1000', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.integer().filter(v => Math.abs(v) < 1000), | |||||
v => { | |||||
expect(formatFileSize(v)).toMatch(/^-?\d+ B$/) | |||||
} | |||||
) | |||||
) | |||||
}) | |||||
it('should format values of |x| >= 1000', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.integer().filter(v => Math.abs(v) >= 1000), | |||||
v => { | |||||
expect(formatFileSize(v)).toMatch(/^-?\d+\.\d\d .B$/) | |||||
} | |||||
) | |||||
) | |||||
}) | |||||
it('should throw an error on NaN', () => { | |||||
expect(() => formatFileSize(NaN)).toThrow(RangeError) | |||||
}) | |||||
}) | |||||
describe('on non-numeric arguments', () => { | |||||
it('should throw an error', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.anything().filter(v => typeof v !== 'number'), | |||||
v => { | |||||
expect(() => formatFileSize(v)).toThrow(TypeError) | |||||
} | |||||
) | |||||
) | |||||
}) | |||||
}) |
@@ -0,0 +1,20 @@ | |||||
import numeral from 'numeral' | |||||
type FormatFileSize = (maybeNumber: unknown) => string | |||||
const formatFileSize: FormatFileSize = maybeNumber => { | |||||
if (typeof maybeNumber! !== 'number') { | |||||
throw TypeError('Argument should be a number.') | |||||
} | |||||
const n = maybeNumber as number | |||||
if (isNaN(n)) { | |||||
throw RangeError('Cannot format NaN.') | |||||
} | |||||
const base = numeral(Math.abs(n)).format(Math.abs(n) < 1000 ? '0 b' : '0.00 b') | |||||
return `${n < 0 ? '-' : ''}${base}` | |||||
} | |||||
export default formatFileSize |
@@ -0,0 +1,51 @@ | |||||
import * as fc from 'fast-check' | |||||
import formatRawFileSize from './formatRawFileSize' | |||||
it('should exist', () => { | |||||
expect(formatRawFileSize).toBeDefined() | |||||
}) | |||||
it('should be a function', () => { | |||||
expect(typeof formatRawFileSize).toBe('function') | |||||
}) | |||||
it('should take 1 argument', () => { | |||||
expect(formatRawFileSize).toHaveLength(1) | |||||
}) | |||||
it('should throw an error on non-numeric arguments', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.anything().filter(v => typeof v !== 'number'), | |||||
v => { | |||||
expect(() => formatRawFileSize(v)).toThrow(TypeError) | |||||
} | |||||
) | |||||
) | |||||
}) | |||||
it('should throw an error on NaN', () => { | |||||
expect(() => formatRawFileSize(NaN)).toThrow(RangeError) | |||||
}) | |||||
it('should return string on numeric values', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.integer(), | |||||
v => { | |||||
expect(typeof formatRawFileSize(v)).toBe('string') | |||||
} | |||||
) | |||||
) | |||||
}) | |||||
it('should format numeric values', () => { | |||||
fc.assert( | |||||
fc.property( | |||||
fc.integer(), | |||||
v => { | |||||
expect(formatRawFileSize(v)).toMatch(/^-?\d[,\d]* .?B$/) | |||||
} | |||||
) | |||||
) | |||||
}) |
@@ -0,0 +1,22 @@ | |||||
import numeral from 'numeral' | |||||
type FormatRawFileSize = (maybeNumber: unknown) => string | |||||
const formatRawFileSize: FormatRawFileSize = maybeNumber => { | |||||
if (typeof maybeNumber! !== 'number') { | |||||
throw TypeError('Argument should be a number.') | |||||
} | |||||
const n = maybeNumber as number | |||||
if (isNaN(n)) { | |||||
throw RangeError('Cannot format NaN.') | |||||
} | |||||
const absValue = Math.abs(n) | |||||
const base = numeral(absValue < 1000 ? absValue : 999).format('0 b') | |||||
const suffix = base.slice(base.indexOf(' ') + ' '.length) | |||||
return `${n < 0 ? '-' : ''}${numeral(absValue).format('0,0')} ${suffix}` | |||||
} | |||||
export default formatRawFileSize |
@@ -0,0 +1,11 @@ | |||||
import calculateHash from './calculateHash' | |||||
import dataUriToBlob from './dataUriToBlob' | |||||
import formatFileSize from './formatFileSize' | |||||
import formatRawFileSize from './formatRawFileSize' | |||||
export { | |||||
calculateHash, | |||||
dataUriToBlob, | |||||
formatFileSize, | |||||
formatRawFileSize, | |||||
} |
@@ -0,0 +1,22 @@ | |||||
{ | |||||
"include": ["src", "types"], | |||||
"compilerOptions": { | |||||
"module": "esnext", | |||||
"lib": ["dom", "esnext"], | |||||
"importHelpers": true, | |||||
"declaration": true, | |||||
"sourceMap": true, | |||||
"rootDir": "./src", | |||||
"strict": true, | |||||
"noUnusedLocals": true, | |||||
"noUnusedParameters": true, | |||||
"noImplicitReturns": true, | |||||
"noImplicitAny": true, | |||||
"noFallthroughCasesInSwitch": true, | |||||
"allowSyntheticDefaultImports": true, | |||||
"moduleResolution": "node", | |||||
"baseUrl": "./", | |||||
"jsx": "react", | |||||
"esModuleInterop": true | |||||
} | |||||
} |