JavaScript
js 相关
youch ---> 在控制台和 Web 打印错误
配合 hono 框架的一个例子
import { Hono } from 'hono'
import { Youch } from 'youch'
const app = new Hono()
const IN_DEV = process.env.NODE_ENV === 'development'
app.onError(async (error, c) => {
if (IN_DEV) {
const youch = new Youch()
const html = await youch.toHTML(error)
return c.html(html)
}
return c.text(error.message)
})minisearch ---> 轻量级搜索引擎
支持自动补全建议,模糊查询,自定义分词逻辑,字段提取
// A collection of documents for our examples
const documents = [
{
id: 1,
title: 'Moby Dick',
text: 'Call me Ishmael. Some years ago...',
category: 'fiction'
},
// ...and more
]
let miniSearch = new MiniSearch({
fields: ['title', 'text'], // fields to index for full-text search
storeFields: ['title', 'category'] // fields to return with search results
})
// Index all documents
miniSearch.addAll(documents)
// Search with default options
let results = miniSearch.search('zen art motorcycle')
// => [
// { id: 2, title: 'Zen and the Art of Motorcycle Maintenance', category: 'fiction', score: 2.77258, match: { ... } },
// { id: 4, title: 'Zen and the Art of Archery', category: 'non-fiction', score: 1.38629, match: { ... } }
// ]es-toolkit ---> JS 方法库
相比于 lodash-es,有着更好的性能
// import from '@es-toolkit/es-toolkit' in jsr.
import { debounce, chunk } from 'es-toolkit';
const debouncedLog = debounce(message => {
console.log(message);
}, 300);
// This call will be debounced
debouncedLog('Hello, world!');
const array = [1, 2, 3, 4, 5, 6];
const chunkedArray = chunk(array, 2);
console.log(chunkedArray);
// Output: [[1, 2], [3, 4], [5, 6]]pretty-bytes ---> 字节格式化处理
import prettyBytes from 'pretty-bytes';
prettyBytes(1337);
//=> '1.34 kB'
prettyBytes(100);
//=> '100 B'obfuscator ---> javascript 混淆器
var JavaScriptObfuscator = require('javascript-obfuscator');
var obfuscationResult = JavaScriptObfuscator.obfuscate(`
(function(){
var variable1 = '5' - 3;
var variable2 = '5' + 3;
console.log(variable3);
console.log(variable4);
console.log(variable5);
})();
`);
console.log(obfuscationResult.getObfuscatedCode());flexsearch ---> 搜索引擎
可以搜索文本、对象、数组等df
const FlexSearch = require('flexsearch');
// 创建 FlexSearch 实例
const index = new FlexSearch({
encode: 'advanced',
tokenize: 'forward',
threshold: 0,
resolution: 9,dsfa
});
// 准备搜索的数据
const data = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Doe', email: 'jane@example.com' },
];
// 向索引中添加数据
data.forEach(item => {
index.add(item.id, [item.name, item.email]);
});
// 执行搜索操作
console.log(index.search('jane')); // [2]flatted ---> JSON 解析器
可以用来解析循环的 JS 对象
import {toJSON, fromJSON} from 'flatted';
class RecursiveMap extends Map {
static fromJSON(any) {
return new this(fromJSON(any));
}
toJSON() {
return toJSON([...this.entries()]);
}
}
const recursive = new RecursiveMap;
const same = {};
same.same = same;
recursive.set('same', same);
const asString = JSON.stringify(recursive);
const asMap = RecursiveMap.fromJSON(JSON.parse(asString));
asMap.get('same') === asMap.get('same').same;
// truepublic-ip ---> 获得当前 IP 地址
浏览器和 NodeJs 都适用
import {publicIp, publicIpv4, publicIpv6} from 'public-ip';
console.log(await publicIp()); // Falls back to IPv4
//=> 'fe80::200:f8ff:fe21:67cf'
console.log(await publicIpv6());
//=> 'fe80::200:f8ff:fe21:67cf'
console.log(await publicIpv4());
//=> '46.5.21.123'mitt ---> 发布订阅模式库
import mitt from 'mitt'
const emitter = mitt()
// listen to an event
emitter.on('foo', e => console.log('foo', e) )
// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )
// fire an event
emitter.emit('foo', { a: 'b' })
// clearing all events
emitter.all.clear()
// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo) // listen
emitter.off('foo', onFoo) // unlisten@preact/signals-core ---> reactive 库
preact 的 reactive 库,可集成到任何的框架当中
import { signal } from "@preact/signals-core";
const counter = signal(0);
// Read value from signal, logs: 0
console.log(counter.value);
// Write to a signal
counter.value = 1;devalue ---> JSON 序列化器
序列化 JSON 中不存在的 JS 数据结构、防 XSS、执行序列化循环对象
import * as devalue from 'devalue';
let obj = { message: 'hello' };
let stringified = devalue.stringify(obj); // '[{"message":1},"hello"]'
devalue.parse(stringified); // { message: 'hello' }
obj.self = obj;
stringified = devalue.stringify(obj); // '[{"message":1,"self":0},"hello"]'
devalue.parse(stringified); // { message: 'hello', self: [Circular] }类似的项目:blitz-js/superjson
terser ---> js 压缩器
import { minify } from 'terser'
minify(code)rss-parser ---> RSS 解析器
import Parser from 'rss-parser';
type CustomFeed = {foo: string};
type CustomItem = {bar: number};
const parser: Parser<CustomFeed, CustomItem> = new Parser({
customFields: {
feed: ['foo', 'baz'],
// ^ will error because `baz` is not a key of CustomFeed
item: ['bar']
}
});
(async () => {
const feed = await parser.parseURL('https://www.reddit.com/.rss');
console.log(feed.title); // feed will have a `foo` property, type as a string
feed.items.forEach(item => {
console.log(item.title + ':' + item.link) // item will have a `bar` property type as a number
});
})();js-yaml ---> YAML 解析器
const yaml = require('js-yaml');
const fs = require('fs');
// Get document, or throw exception on error
try {
const doc = yaml.load(fs.readFileSync('/home/ixti/example.yml', 'utf8'));
console.log(doc);
} catch (e) {
console.log(e);
}sucrase ----> Babel 替代品
动机:我们不再需要 Babel 生产低级产物来兼容 IE,所以无需做太多步骤的转换
由于 Sucrase 的工作层级较低,并针对其用例使用定制的解析器,因此比 Babel 快得多
import {transform} from "sucrase";
const compiledCode = transform(code, {transforms: ["typescript", "imports"]}).code;tinyld ----> 识别语言
判断一段文字是什么语言
import { detect, detectAll } from 'tinyld'
// Detect
detect('これは日本語です.') // ja
detect('and this is english.') // en
// DetectAll
detectAll('ceci est un text en francais.')
// [ { lang: 'fr', accuracy: 0.5238 }, { lang: 'ro', accuracy: 0.3802 }, ... ]periscopic ---> ESTree 程序分析工具
import { analyze } from 'periscopic';
const ast = acorn.parse(`
const a = b;
console.log(a);
`);
const { map, globals, scope } = analyze(ast);acorn ---> ECMAScript parser
ECMAScript 解析器
const acorn = require("acorn")
const walk = require("acorn-walk")
walk.simple(acorn.parse("let x = 10"), {
Literal(node) {
console.log(`Found a literal: ${node.value}`)
}
})其他 parser: eslint/espree
其他 walker: Rich-Harris/estree-walker
Numeraljs ---> 数字标准化
可以解析不同格式的数字以及转化成想要的格式
var string = numeral(1000).format('0,0');
// '1,000'
var number = numeral(1000),
value = 100;
var difference = number.difference(value);
// 900introJs ---> lightweight intro lib
新手引导
另一个库类似的也极其推荐:kamranahmedse/driver.js,而且免费商用
p-queue ---> promise queue
promise queue with concurrency control
import PQueue from 'p-queue';
import got from 'got';
const queue = new PQueue({concurrency: 1});
(async () => {
await queue.add(() => got('https://sindresorhus.com'));
console.log('Done: sindresorhus.com');
})();
(async () => {
await queue.add(() => got('https://avajs.dev'));
console.log('Done: avajs.dev');
})();
(async () => {
const task = await getUnicornTask();
await queue.add(task);
console.log('Done: Unicorn task');
})();markdownlint-cli ---> markdown 文档 lint
可以和 case-police 一起使用
{
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md'",
"lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' --fix",
}sentry-javascript ---> 监控库
import { init, captureMessage } from '@sentry/browser';
init({
dsn: '__DSN__',
// ...
});
captureMessage('Hello, world!');clipboardy ---> 剪切板工具
import clipboard from 'clipboardy';
clipboard.writeSync('🦄');
clipboard.readSync();
//=> '🦄'anymatch ---> 模块路径匹配
import anymatch from 'anymatch'
const matchers = [ 'path/to/file.js', 'path/anyjs/**/*.js', /foo.js$/, string => string.includes('bar') && string.length > 10 ] ;
anymatch(matchers, 'path/to/file.js'); // true
anymatch(matchers, 'bar.js'); // false
// returnIndex = true
anymatch(matchers, 'foo.js', {returnIndex: true}); // 2
anymatch(matchers, 'path/anyjs/foo.js', {returnIndex: true}); // 1
// any picomatc
// using globs to match directories and their children
anymatch('node_modules', 'node_modules'); // true
anymatch('node_modules', 'node_modules/somelib/index.js'); // false
anymatch('**/node_modules/**', '/absolute/path/to/node_modules/somelib/index.js'); // true
const matcher = anymatch(matchers);
['foo.js', 'bar.js'].filter(matcher); // [ 'foo.js' ]类似:micromatch/micromatch、micromatch/picomatch
| Feature | minimatch | micromatch | picomatch | nanomatch | extglob | braces | expand-brackets |
|---|---|---|---|---|---|---|---|
| Wildcard matching (*?+) | ✔ | ✔ | ✔ | ✔ | - | - | - |
| Advancing globbing | ✔ | ✔ | ✔ | - | - | - | - |
| Brace matching | ✔ | ✔ | ✔ | - | - | ✔ | - |
| Brace expansion | ✔ | ✔ | - | - | - | ✔ | - |
| Extglobs | partial | ✔ | ✔ | - | ✔ | - | - |
| Posix brackets | - | ✔ | ✔ | - | - | - | ✔ |
| Regular expression syntax | - | ✔ | ✔ | ✔ | ✔ | - | ✔ |
| File system operations | - | - | - | - | - | - | - |
acorn ---> JS 语法解析
相比用 Babel 做语法解析,更加轻便好用,得到 AST
import { Parser } from "acorn";
const tree = Parser.parse(`
function add(a, b) {
console.log(a + b)
}
add(1, 2)
`, {
ecmaVersion: 'latest'
})
console.log(tree)nanoid ---> Hash 生成
相比于 UUID v4,特点:
- 使用更大的 alphabet,默认情况使用 URL-friendly symbols
- 包体积更小
import { nanoid, customAlphabet } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"dot-prop ---> 点路径操作
使用点路径从嵌套对象获取、设置或删除属性
import {getProperty, setProperty, hasProperty, deleteProperty} from 'dot-prop';
// Getter
getProperty({foo: {bar: 'unicorn'}}, 'foo.bar');
//=> 'unicorn'
getProperty({foo: {bar: 'a'}}, 'foo.notDefined.deep');
//=> undefined
// Setter
const object = {foo: {bar: 'a'}};
setProperty(object, 'foo.bar', 'b');
console.log(object);
//=> {foo: {bar: 'b'}}
// Deleter
const object = {foo: {bar: 'a'}};
deleteProperty(object, 'foo.bar');
console.log(object);
//=> {foo: {}}klona ---> 多种数据结构深度拷贝
import { klona } from 'klona'
const input = {
foo: 1,
bar: {
baz: 2,
bat: {
hello: 'world'
}
}
};
const output = klona(input);
// exact copy of original
assert.deepStrictEqual(input, output);