init
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@renovamen/utils",
|
||||
"version": "0.1.1",
|
||||
"description": "Useful utils, zero dependencies.",
|
||||
"homepage": "https://github.com/Renovamen/oh-my-cv/tree/main/packages/utils",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Renovamen/oh-my-cv"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Renovamen/oh-my-cv.git",
|
||||
"directory": "packages/utils"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Renovamen <renovamenzxh@gmail.com>",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"require": "./dist/index.js",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build-fast:pkg": "tsup src/index.ts --format cjs,esm",
|
||||
"build:pkg": "pnpm run build-fast:pkg --dts"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { isObject } from "./is";
|
||||
|
||||
// Copied from https://github.com/meteorlxy/vscode-slugify
|
||||
export const slugify = (str: string) =>
|
||||
encodeURI(
|
||||
str
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-") // Replace whitespace with -
|
||||
.replace(
|
||||
/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g,
|
||||
""
|
||||
) // Remove known punctuators
|
||||
.replace(/^\-+/, "") // Remove leading -
|
||||
.replace(/\-+$/, "") // Remove trailing -
|
||||
);
|
||||
|
||||
export const htmlEscape = (str: string) => {
|
||||
const escapeMap: Record<string, string> = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"'": "'",
|
||||
'"': """
|
||||
};
|
||||
|
||||
return str.replace(/[&<>'"]/g, (char) => escapeMap[char]);
|
||||
};
|
||||
|
||||
export const copy = <T>(obj: T): T => {
|
||||
if (isObject(obj)) return JSON.parse(JSON.stringify(obj));
|
||||
throw new Error("Input must be a non-null object.");
|
||||
};
|
||||
|
||||
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export const now = () => Date.now();
|
||||
|
||||
export const arrayify = <T>(value: T | T[]): T[] =>
|
||||
Array.isArray(value) ? value : [value];
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import type { Callback } from "./types";
|
||||
|
||||
export const fetchFile = async (url: string): Promise<string> => {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Request error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return await res.text();
|
||||
} catch (error) {
|
||||
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open file dialog with ease. This hook differs from vueuse's useFileDialog in that it
|
||||
* doesn't require Vue.
|
||||
*
|
||||
* @param accept File types to accept
|
||||
* @returns
|
||||
*/
|
||||
export const useFileDialog = (accept?: string) => {
|
||||
let callback: Callback<File> | null = null;
|
||||
|
||||
let input: HTMLInputElement | undefined;
|
||||
|
||||
if (document) {
|
||||
input = document.createElement("input");
|
||||
|
||||
input.type = "file";
|
||||
input.style.display = "none";
|
||||
if (accept) input.accept = accept;
|
||||
|
||||
input.onchange = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
|
||||
if (file && callback) callback(file);
|
||||
};
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
if (!input) return;
|
||||
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
document.body.removeChild(input);
|
||||
};
|
||||
|
||||
const onChange = (cb: Callback<File>) => {
|
||||
callback = cb;
|
||||
};
|
||||
|
||||
return {
|
||||
open,
|
||||
onChange
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Read file content as text.
|
||||
*
|
||||
* @param file File object
|
||||
* @returns Promise containing file content as string
|
||||
*/
|
||||
export const readFile = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = () => reject(new Error("Failed to read file"));
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
};
|
||||
|
||||
export const downloadFile = (filename: string, content: string) => {
|
||||
const element = document.createElement("a");
|
||||
|
||||
element.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
|
||||
element.download = filename;
|
||||
element.style.display = "none";
|
||||
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export * from "./file";
|
||||
export * from "./common";
|
||||
export * from "./types";
|
||||
export * from "./is";
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
export const isClient = typeof window !== "undefined" && typeof document !== "undefined";
|
||||
|
||||
export const isMac =
|
||||
isClient && typeof navigator !== "undefined" && /Macintosh/.test(navigator.userAgent);
|
||||
|
||||
export const isExternal = (path: string) => {
|
||||
const outboundRE = /^(https?:|mailto:|tel:)/;
|
||||
return outboundRE.test(path);
|
||||
};
|
||||
|
||||
export const isObject = (v: any) => toString.call(v) === "[object Object]";
|
||||
|
||||
export const isInteger = (v: any, { allowString = false } = {}): boolean => {
|
||||
return typeof v === "number"
|
||||
? Number.isInteger(v)
|
||||
: allowString && typeof v === "string" && Number.isInteger(Number(v));
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
|
||||
|
||||
export type PartialWithRequired<T, K extends keyof T> = Pick<T, K> & Partial<T>;
|
||||
|
||||
// https://stackoverflow.com/questions/55541275/typescript-check-for-the-any-type
|
||||
// https://github.com/vueuse/vueuse/blob/main/packages/shared/utils/types.ts
|
||||
export type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
|
||||
export type IsAny<T> = IfAny<T, true, false>;
|
||||
|
||||
export type Callback<T> =
|
||||
IsAny<T> extends true
|
||||
? (param: any) => void
|
||||
: [T] extends [void]
|
||||
? () => void
|
||||
: (param: T) => void;
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["./src"]
|
||||
}
|
||||
Reference in New Issue
Block a user