跳到主要内容

使用命令行安装 VS Code 插件

· 阅读需 1 分钟
1adybug
子虚伊人
# 在线安装
code --install-extension ms-python.python
# 本地安装
code --install-extension "本地vsix插件路径"
注意

安装时,请先关闭所有 VS Code 窗口,否则某些插件安装可能会卡住。

使用 JavaScript 安装目录下所有插件:

import { spawn } from "child_process"
import { readdir } from "fs/promises"

function spawnAsync(command: string) {
return new Promise<void>((resolve, reject) => {
const child = spawn(command, { shell: true, stdio: "inherit" })

child.on("exit", code => {
if (code !== 0) return reject(new Error(`Command failed with code ${code}`))
resolve()
})
})
}

async function main() {
const dir = await readdir("./")
const exts = dir.filter(name => name.endsWith(".vsix"))

for (const ext of exts) await spawnAsync(`code --install-extension "${ext}"`)
}

main()

清除 Powershell 历史记录

· 阅读需 1 分钟
1adybug
子虚伊人
  • 历史记录保存位置,直接删除不想要的行即可

    echo (Get-PSReadlineOption).HistorySavePath
  • 或者直接删除文件

    Remove-Item (Get-PSReadlineOption).HistorySavePath

需要重启终端才能生效

获取 winget 的软件清单

· 阅读需 4 分钟
1adybug
子虚伊人

winget 的所有软件清单都保存在 manifests 目录中。下一层的目录便是软件作者名称的首字母,比如 Google.Chrome 便是保存在 manifests/g 目录中 接下来使用 GitHub REST API 获取目录即可:

import fetch from "node-fetch"
import YAML from "yaml"

export namespace Winget {
export interface Package {
PackageIdentifier: string
PackageVersion: string
InstallerType: string
InstallModes: string[]
InstallerSwitches: InstallerSwitches
ExpectedReturnCodes: ExpectedReturnCode[]
UpgradeBehavior: string
Protocols: string[]
FileExtensions: string[]
AppsAndFeaturesEntries: AppsAndFeaturesEntry[]
Installers: Installer[]
ManifestType: string
ManifestVersion: string
}

export interface Installer {
Architecture: string
Scope: string
InstallerUrl: string
InstallerSha256: string
InstallerSwitches: InstallerSwitches2
ProductCode: string
}

export interface InstallerSwitches2 {
Custom: string
}

export interface AppsAndFeaturesEntry {
UpgradeCode: string
InstallerType: string
}

export interface ExpectedReturnCode {
InstallerReturnCode: number
ReturnResponse: string
}

export interface InstallerSwitches {
Log: string
}
}

export type WingetDownloadInfo = {
name: string
id: string
dir: string
architecture?: "x64" | "x86" | "all"
}

export interface GithubContent {
name: string
path: string
sha: string
size: number
url: string
html_url: string
git_url: string
download_url?: string | null
type: string
_links: Links
}

export interface Links {
self: string
git: string
html: string
}

export type WingetItem = {
filename: string
version: string
ext: string
architecture: string
}

export async function downloadFromWinget({ name, id, dir, architecture = "x64" }: WingetDownloadInfo) {
const firstLetter = id[0].toLowerCase()
const path = id.replace(/\./g, "/")
const response = await fetch(`https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/${firstLetter}/${path}`, { agent })
const data: GithubContent[] = (await response.json()) as any
const reg2 = /^\d+(\.\d+?)*$/
const stables = data.filter(item => reg2.test(item.name))

stables.sort((a, b) => {
const avs = a.name.split(".")
const bvs = b.name.split(".")
const max = Math.max(avs.length, bvs.length)

for (let i = 0; i < max; i++) {
const av = avs[i] ? parseInt(avs[i]) : 0
const bv = bvs[i] ? parseInt(bvs[i]) : 0
if (av < bv) return 1
if (av > bv) return -1
}

return 0
})

const response2 = await fetch(
`https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/${firstLetter}/${path}/${stables[0].name}/${id}.installer.yaml`,
{ agent },
)
const yaml = await response2.text()
const pkg: Winget.Package = YAML.parse(yaml)

const installers = pkg.Installers.filter((item, index) => {
if (item.Architecture !== "x64" && item.Architecture !== "x86") return false
if (architecture !== "all" && item.Architecture !== architecture) return false
if (!item.InstallerUrl.endsWith(".exe") && !item.InstallerUrl.endsWith(".msi")) return false
if (item.InstallerUrl.endsWith(".msi") && pkg.Installers.some(item2 => item2.Architecture === item.Architecture && item2.InstallerUrl.endsWith(".exe")))
return false
if (pkg.Installers.findIndex(item2 => item2.Architecture === item.Architecture) !== index) return false
return true
})

const result: WingetItem[] = []

for (const { InstallerUrl, Architecture } of installers) {
if (Architecture !== "x64" && Architecture !== "x86") continue
const filename = await download(InstallerUrl, dir)

result.push({
filename,
version: pkg.PackageVersion,
ext: InstallerUrl.endsWith(".exe") ? "exe" : "msi",
architecture: Architecture,
})
}

for (const { version, filename, architecture, ext } of result) {
await sleep(100)
await rename(join(dir, filename), join(dir, `${name}_${version}_${architecture}.${ext}`))
}
}
提示
  • 使用 YAML 来将 YAML 文件格式化为 JSON
  • InstallerScope 字段有两个可能的值:usermachine
    • user:当 Scope 被设置为 user 时,意味着安装操作仅针对当前用户进行。安装的程序或者应用将仅对当前用户可用,安装的结果(如程序文件和快捷方式)将被存储在用户的个人目录下(例如,在 Windows 系统中可能是 C:\Users\[用户名]\ 下的某个目录),并且只有当前用户有权限运行或修改。这种安装方式不需要管理员权限,但安装的程序只能由安装它的用户使用。
    • machine:当 Scope 被设置为 machine 时,表明安装是针对整个系统进行的,安装的程序或应用将对所有用户可用。这通常意味着程序会被安装在系统级的目录下(例如,在 Windows 系统中可能是 C:\Program Files\),并且安装、运行或修改程序可能需要管理员权限。这种安装方式确保了所有使用该机器的用户都可以访问到安装的程序。
注意
  • 由于国内的网络环境限制,可能需要为 fetch 配置代理
  • 示例函数对于版本号使用了 ^\d+(\.\d+?)*$ 正则表达式进行了过滤,也就是只接受 数字.数字.数字... 形式的版本
  • 示例函数的返回结果并不一定严格符合 Winget.Package 类型,只是以 Google.Chrome 为例

使用接口的方式获取 Github 项目的目录

· 阅读需 2 分钟
1adybug
子虚伊人

要使用 API 的方式获取 GitHub 地址中某个特定目录的内容,可以通过 GitHub 的 REST API 实现。下面是一个基本的步骤指南:

获取仓库内容:使用 GitHub REST API 的 /repos/{owner}/{repo}/contents/{path} 可以获取仓库中特定路径的内容。这里的 {owner} 是仓库拥有者的用户名,{repo} 是仓库名,而 {path} 是你想要获取内容的目录路径。

认证:对于公共仓库,你可能不需要认证就可以请求这个 API。但是,对于私有仓库,你需要使用 OAuth token 或者其他形式的认证。

解析响应:API 的响应将以 JSON 格式返回,包含目录下的文件和子目录列表。你可以解析这个 JSON 来获取你需要的信息。

示例:

假设我们想要获取 GitHub 上 octocat/Hello-World 仓库根目录下的 lib 目录的内容。使用 curl 命令,请求看起来可能像这样:

curl https://api.github.com/repos/octocat/Hello-World/contents/lib

如果需要认证,可以在请求中添加 HTTP 头 Authorization: token YOUR_TOKEN,如下所示:

curl -H "Authorization: token YOUR_TOKEN" https://api.github.com/repos/octocat/Hello-World/contents/lib
注意
  • 确保替换 YOUR_TOKEN 为你的实际 GitHub 访问令牌。
  • 如果目录很大或有很多文件,GitHub 的 API 可能会进行分页处理。这种情况下,你可能需要处理分页逻辑,通过检查响应头中的 Link 字段来获取下一页数据。
  • 为了避免过度使用 API 并受到限制,注意检查并遵守 GitHub 的速率限制政策。

通过这种方式,你可以轻松地编程访问 GitHub 上任何公开或私有仓库的目录和文件内容。

正则表达式负向前瞻

· 阅读需 1 分钟
1adybug
子虚伊人

在正则表达式中,有时候我们需要从一个合集中去掉另一个合集(也就是差集),这时可以利用正则表达式的负向前瞻来实现:

/** 从 \w 中去除 \d */
const reg: RegExp = /[(?!\d)\w]/
提示

使用 [] 将两个集合包裹起来

在 Node.js 中为 fetch 配置代理

· 阅读需 1 分钟
1adybug
子虚伊人

Node.js 中,原生的 fetch API 并不直接支持代理功能。可以使用 node-fetch 库和 https-proxy-agent 库来实现通过代理服务器发送请求的功能:

import { HttpsProxyAgent } from "https-proxy-agent"
import fetch from "node-fetch"

// 代理服务器的URL
const proxyUrl = "http://your-proxy-server:port"

// 目标URL,即你想要通过代理服务器访问的URL
const targetUrl = "http://example.com"

// 配置代理服务器
const proxyAgent = new HttpsProxyAgent(proxyUrl)

// 使用fetch发起请求,通过代理服务器访问目标URL
fetch(targetUrl, { agent: proxyAgent })
注意

与原生的 fetch 不同,node-fetchresponse.json() 返回值类型为 Promise<unknown>

在网页中引入字体

· 阅读需 1 分钟
1adybug
子虚伊人
@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-35-Thin.woff2") format("woff2");
font-weight: 100;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-45-Light.woff2") format("woff2");
font-weight: 200;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-55-Regular.woff2") format("woff2");
font-weight: 300;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-65-Medium.woff2") format("woff2");
font-weight: 400;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-75-SemiBold.woff2") format("woff2");
font-weight: 500;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-85-Bold.woff2") format("woff2");
font-weight: 600;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-95-ExtraBold.woff2") format("woff2");
font-weight: 700;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-105-Heavy.woff2") format("woff2");
font-weight: 800;
}

@font-face {
font-family: "AlibabaPuHuiTi";
src: url("./fonts/AlibabaPuHuiTi-3-115-Black.woff2") format("woff2");
font-weight: 900;
}

处理图片错误

· 阅读需 1 分钟
1adybug
子虚伊人

在项目中经常会使用到第三方的图床,这些图床的图片可能会出错,统一处理错误就很有必要:

window.addEventListener(
"error",
e => {
const { target } = e

// 判断是否是图片元素的错误
if (!(target instanceof HTMLImageElement)) return

const url = new URL(target.src)

// 判断是否是第三方的图片
if (url.origin === location.origin) return

// 添加 data-error-image 属性
target.dataset.errorImage = ""
},
true,
)
[data-error-image] {
position: relative;
}

[data-error-image]::before {
content: "视图库服务器";
font-family: "AlibabaPuHuiTi";
position: absolute;
width: 100%;
height: 50%;
background-color: brown;
left: 0;
top: 0;
display: flex;
justify-content: center;
align-items: flex-end;
}

[data-error-image]::after {
content: "传递图片失败";
font-family: "AlibabaPuHuiTi";
position: absolute;
width: 100%;
height: 50%;
background-color: brown;
left: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: flex-start;
}
提示

即使后续图片加载成功了也不用担心,伪元素会被隐藏