mirror of
https://github.com/enso-org/enso.git
synced 2024-11-21 16:36:59 +03:00
Remove download font scripts (#11317)
The fonts were added (by mistake) anyway, but they are rarely changed, and the scripts add unnecessary complexity. This also fixes some post- #11287 problems.
This commit is contained in:
parent
244effde0c
commit
d1ee7fadce
@ -1,5 +1,6 @@
|
||||
# Build Artefacts
|
||||
dist
|
||||
mockDist
|
||||
target
|
||||
|
||||
# Release body template should not be auto-formatted, as the word wrap does not really looks good in the GitHub UI.
|
||||
|
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -1211,8 +1211,6 @@ dependencies = [
|
||||
"dirs",
|
||||
"enso-build-base",
|
||||
"enso-build-macros-lib",
|
||||
"enso-enso-font",
|
||||
"enso-font",
|
||||
"enso-install-config",
|
||||
"futures",
|
||||
"glob",
|
||||
@ -1313,23 +1311,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-enso-font"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"enso-font",
|
||||
"ide-ci",
|
||||
"owned_ttf_parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-font"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"owned_ttf_parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enso-formatter"
|
||||
version = "0.1.0"
|
||||
@ -2858,15 +2839,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb"
|
||||
dependencies = [
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
@ -4349,12 +4321,6 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
||||
|
||||
[[package]]
|
||||
name = "tuikit"
|
||||
version = "0.5.0"
|
||||
|
6
app/gui/.gitignore
vendored
6
app/gui/.gitignore
vendored
@ -26,11 +26,5 @@ mockDist
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
||||
public/font-dejavu/
|
||||
public/font-enso/
|
||||
public/font-mplus1/
|
||||
src/project-view/assets/font-dejavu.css
|
||||
src/project-view/assets/font-enso.css
|
||||
src/project-view/assets/font-mplus1.css
|
||||
src/project-view/util/iconList.json
|
||||
src/project-view/util/iconName.ts
|
||||
|
@ -29,11 +29,10 @@
|
||||
"test-dev:unit": "vitest",
|
||||
"test:e2e": "cross-env NODE_ENV=production playwright test",
|
||||
"test-dev:e2e": "cross-env NODE_ENV=production playwright test --ui",
|
||||
"preinstall": "corepack pnpm run generate-metadata && corepack pnpm run download-fonts",
|
||||
"preinstall": "corepack pnpm run generate-metadata",
|
||||
"postinstall": "playwright install",
|
||||
"build-rust-ffi": "wasm-pack build ./rust-ffi --release --target web && wasm-pack build ./rust-ffi --out-dir node-pkg --target nodejs",
|
||||
"generate-metadata": "node scripts/generateIconMetadata.js",
|
||||
"download-fonts": "node scripts/downloadFonts.js"
|
||||
"generate-metadata": "node scripts/generateIconMetadata.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-amplify/auth": "5.6.5",
|
||||
@ -166,7 +165,6 @@
|
||||
"@types/mapbox-gl": "^2.7.13",
|
||||
"@types/shuffle-seed": "^1.1.0",
|
||||
"@types/tar": "^6.1.4",
|
||||
"@types/unbzip2-stream": "^1.4.3",
|
||||
"@types/wicg-file-system-access": "^2023.10.2",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
@ -188,7 +186,6 @@
|
||||
"sql-formatter": "^13.0.0",
|
||||
"tar": "^6.2.1",
|
||||
"tsx": "^4.7.1",
|
||||
"unbzip2-stream": "^1.4.3",
|
||||
"vite-plugin-vue-devtools": "7.3.7",
|
||||
"vite-plugin-wasm": "^3.3.0",
|
||||
"vue-react-wrapper": "^0.3.1",
|
||||
|
Binary file not shown.
@ -1,217 +0,0 @@
|
||||
/**
|
||||
* @file ⚠️⚠️⚠️ THIS SCRIPT IS PROVIDED ONLY FOR CONVENIENCE. ⚠️⚠️⚠️
|
||||
* The sources of truth are at `build/build/src/project/gui.rs` and
|
||||
* `build/build/src/ide/web/fonts.rs`.
|
||||
*/
|
||||
|
||||
import * as fsSync from 'node:fs'
|
||||
import * as fs from 'node:fs/promises'
|
||||
import * as http from 'node:http'
|
||||
import * as https from 'node:https'
|
||||
import * as process from 'node:process'
|
||||
import tar from 'tar'
|
||||
import bz2 from 'unbzip2-stream'
|
||||
|
||||
if (process.env.CI === '1') process.exit(0)
|
||||
|
||||
const WARNING_MESSAGE =
|
||||
'⚠️⚠️⚠️ Please use the buildscript (`./run`) to download fonts instead. ⚠️⚠️⚠️'
|
||||
let warningMessageAlreadyShown = false
|
||||
let exitCode = 0
|
||||
|
||||
const ENSO_FONT_URL = 'https://github.com/enso-org/font/releases/download/1.0/enso-font-1.0.tar.gz'
|
||||
const MPLUS1_FONT_URL =
|
||||
'https://github.com/coz-m/MPLUS_FONTS/raw/71d438c798d063cc6fdae8d2864bc48f2d3d06ad/fonts/ttf/MPLUS1%5Bwght%5D.ttf'
|
||||
const DEJAVU_SANS_MONO_FONT_URL =
|
||||
'https://sourceforge.net/projects/dejavu/files/dejavu/2.37/dejavu-fonts-ttf-2.37.tar.bz2'
|
||||
|
||||
/**
|
||||
* @param {string | https.RequestOptions | URL} options
|
||||
* @param {((res: import('node:http').IncomingMessage) => void) | undefined} [callback]
|
||||
*/
|
||||
function get(options, callback) {
|
||||
const protocol =
|
||||
typeof options === 'string' ? new URL(options).protocol : options.protocol ?? 'https:'
|
||||
/** @type {{ get: typeof http['get'] }} */
|
||||
let httpModule = https
|
||||
switch (protocol) {
|
||||
case 'http:': {
|
||||
httpModule = http
|
||||
break
|
||||
}
|
||||
}
|
||||
return httpModule.get(options, (response) => {
|
||||
const location = response.headers.location
|
||||
if (location) {
|
||||
get(
|
||||
typeof options === 'string' || options instanceof URL ?
|
||||
location
|
||||
: { ...options, ...new URL(location) },
|
||||
callback,
|
||||
)
|
||||
} else {
|
||||
callback?.(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** @param {unknown} error */
|
||||
function errorCode(error) {
|
||||
return (
|
||||
typeof error === 'object' &&
|
||||
error != null &&
|
||||
'code' in error &&
|
||||
typeof error.code === 'string'
|
||||
) ?
|
||||
error.code
|
||||
: undefined
|
||||
}
|
||||
|
||||
/** @param {unknown} error */
|
||||
function isFileNotFoundError(error) {
|
||||
return errorCode(error) === 'ENOENT'
|
||||
}
|
||||
|
||||
const ENSO_FONT_VARIANTS = [
|
||||
{ variant: 'Thin', weight: 100 },
|
||||
{ variant: 'ExtraLight', weight: 200 },
|
||||
{ variant: 'Light', weight: 300 },
|
||||
{ variant: 'Regular', weight: 400 },
|
||||
{ variant: 'Medium', weight: 500 },
|
||||
{ variant: 'SemiBold', weight: 600 },
|
||||
{ variant: 'Bold', weight: 700 },
|
||||
{ variant: 'ExtraBold', weight: 800 },
|
||||
{ variant: 'Black', weight: 900 },
|
||||
].map((variant) => ({ font: 'Enso', ...variant }))
|
||||
|
||||
const DEJAVU_FONT_VARIANTS = [
|
||||
{ variant: 'DejaVuSansMono', weight: 400 },
|
||||
{ variant: 'DejaVuSansMono-Bold', weight: 700 },
|
||||
].map((variant) => ({ font: 'DejaVu Sans Mono', ...variant }))
|
||||
|
||||
try {
|
||||
await fs.access(`./src/project-view/assets/font-enso.css`)
|
||||
for (const { variant } of ENSO_FONT_VARIANTS) {
|
||||
await fs.access(`./public/font-enso/Enso-${variant}.ttf`)
|
||||
}
|
||||
console.info('Enso font already downloaded, skipping...')
|
||||
} catch (error) {
|
||||
if (!isFileNotFoundError(error)) {
|
||||
console.error('Unexpected error occurred when checking for Enso font:')
|
||||
console.error(error)
|
||||
exitCode = 1
|
||||
} else {
|
||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
||||
warningMessageAlreadyShown = true
|
||||
console.info('Downloading Enso font...')
|
||||
await fs.rm('./public/font-enso/', { recursive: true, force: true })
|
||||
await fs.mkdir('./public/font-enso/', { recursive: true })
|
||||
await new Promise((resolve, reject) => {
|
||||
get(ENSO_FONT_URL, (response) => {
|
||||
response.pipe(
|
||||
tar.extract({
|
||||
cwd: './public/font-enso/',
|
||||
strip: 1,
|
||||
filter(path) {
|
||||
// Reject files starting with `.`.
|
||||
return !/[\\/][.]/.test(path)
|
||||
},
|
||||
}),
|
||||
)
|
||||
response.on('end', resolve)
|
||||
response.on('error', reject)
|
||||
})
|
||||
})
|
||||
/** @type {string[]} */
|
||||
let css = []
|
||||
for (const { font, variant, weight } of ENSO_FONT_VARIANTS) {
|
||||
css.push(`\
|
||||
@font-face {
|
||||
font-family: '${font}';
|
||||
src: url('/font-enso/Enso-${variant}.ttf');
|
||||
font-weight: ${weight};
|
||||
}
|
||||
`)
|
||||
}
|
||||
await fs.writeFile('./src/project-view/assets/font-enso.css', css.join('\n'))
|
||||
}
|
||||
}
|
||||
try {
|
||||
await fs.access(`./src/project-view/assets/font-mplus1.css`)
|
||||
await fs.access(`./public/font-mplus1/MPLUS1[wght].ttf`)
|
||||
console.info('M PLUS 1 font already downloaded, skipping...')
|
||||
} catch (error) {
|
||||
if (!isFileNotFoundError(error)) {
|
||||
console.error('Unexpected error occurred when checking for M PLUS 1 font:')
|
||||
console.error(error)
|
||||
exitCode = 1
|
||||
} else {
|
||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
||||
warningMessageAlreadyShown = true
|
||||
console.info('Downloading M PLUS 1 font...')
|
||||
await fs.rm('./public/font-mplus1/', { recursive: true, force: true })
|
||||
await fs.mkdir('./public/font-mplus1/', { recursive: true })
|
||||
await new Promise((resolve, reject) => {
|
||||
get(MPLUS1_FONT_URL, (response) => {
|
||||
response.pipe(fsSync.createWriteStream('./public/font-mplus1/MPLUS1[wght].ttf'))
|
||||
response.on('end', resolve)
|
||||
response.on('error', reject)
|
||||
})
|
||||
})
|
||||
const css = `\
|
||||
@font-face {
|
||||
font-family: 'M PLUS 1';
|
||||
src: url('/font-mplus1/MPLUS1[wght].ttf');
|
||||
}
|
||||
`
|
||||
await fs.writeFile('./src/project-view/assets/font-mplus1.css', css)
|
||||
}
|
||||
}
|
||||
try {
|
||||
await fs.access(`./src/project-view/assets/font-dejavu.css`)
|
||||
for (const variant of ['', '-Bold']) {
|
||||
await fs.access(`./public/font-dejavu/DejaVuSansMono${variant}.ttf`)
|
||||
}
|
||||
console.info('DejaVu Sans Mono font already downloaded, skipping...')
|
||||
} catch (error) {
|
||||
if (!isFileNotFoundError(error)) {
|
||||
console.error('Unexpected error occurred when checking for DejaVu Sans Mono font:')
|
||||
console.error(error)
|
||||
exitCode = 1
|
||||
} else {
|
||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
||||
warningMessageAlreadyShown = true
|
||||
console.info('Downloading DejaVu Sans Mono font...')
|
||||
await fs.rm('./public/font-dejavu/', { recursive: true, force: true })
|
||||
await fs.mkdir('./public/font-dejavu/', { recursive: true })
|
||||
await new Promise((resolve, reject) => {
|
||||
get(DEJAVU_SANS_MONO_FONT_URL, (response) => {
|
||||
response.pipe(bz2()).pipe(
|
||||
tar.extract({
|
||||
cwd: './public/font-dejavu/',
|
||||
strip: 2,
|
||||
filter(path) {
|
||||
return /[\\/]DejaVuSansMono/.test(path) && !/Oblique[.]ttf$/.test(path)
|
||||
},
|
||||
}),
|
||||
)
|
||||
response.on('end', resolve)
|
||||
response.on('error', reject)
|
||||
})
|
||||
})
|
||||
/** @type {string[]} */
|
||||
let css = []
|
||||
for (const { font, variant, weight } of DEJAVU_FONT_VARIANTS) {
|
||||
css.push(`\
|
||||
@font-face {
|
||||
font-family: '${font}';
|
||||
src: url('/font-dejavu/${variant}.ttf');
|
||||
font-weight: ${weight};
|
||||
}
|
||||
`)
|
||||
}
|
||||
await fs.writeFile('./src/project-view/assets/font-dejavu.css', css.join('\n'))
|
||||
}
|
||||
}
|
||||
console.info('Done.')
|
||||
process.exit(exitCode)
|
12
app/gui/src/project-view/assets/font-dejavu.css
Normal file
12
app/gui/src/project-view/assets/font-dejavu.css
Normal file
@ -0,0 +1,12 @@
|
||||
@font-face {
|
||||
font-family: 'DejaVu Sans Mono';
|
||||
src: url('/font-dejavu/DejaVuSansMono.ttf');
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DejaVu Sans Mono';
|
||||
src: url('/font-dejavu/DejaVuSansMono-Bold.ttf');
|
||||
font-weight: 700;
|
||||
}
|
||||
|
54
app/gui/src/project-view/assets/font-enso.css
Normal file
54
app/gui/src/project-view/assets/font-enso.css
Normal file
@ -0,0 +1,54 @@
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Thin.ttf');
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-ExtraLight.ttf');
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Light.ttf');
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Regular.ttf');
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Medium.ttf');
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-SemiBold.ttf');
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Bold.ttf');
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-ExtraBold.ttf');
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Enso';
|
||||
src: url('/font-enso/Enso-Black.ttf');
|
||||
font-weight: 900;
|
||||
}
|
||||
|
5
app/gui/src/project-view/assets/font-mplus1.css
Normal file
5
app/gui/src/project-view/assets/font-mplus1.css
Normal file
@ -0,0 +1,5 @@
|
||||
@font-face {
|
||||
font-family: 'M PLUS 1';
|
||||
src: url('/font-mplus1/MPLUS1[wght].ttf');
|
||||
}
|
||||
|
@ -1,213 +0,0 @@
|
||||
[
|
||||
"3_dot_menu",
|
||||
"accessed_by_projects",
|
||||
"accessed_data",
|
||||
"add",
|
||||
"add_to_graph_editor",
|
||||
"array_new",
|
||||
"array_new2",
|
||||
"arrow_left",
|
||||
"arrow_right",
|
||||
"arrow_right_head_only",
|
||||
"auto_replay",
|
||||
"bell",
|
||||
"bold",
|
||||
"bookmark",
|
||||
"bottom_panel",
|
||||
"braces",
|
||||
"brush",
|
||||
"bullet-list",
|
||||
"calendar",
|
||||
"camera",
|
||||
"chat",
|
||||
"chat_gpt",
|
||||
"chat_gpt2",
|
||||
"chat_gpt_mod",
|
||||
"chat_gpt_mod2",
|
||||
"check",
|
||||
"check-list",
|
||||
"clone",
|
||||
"close",
|
||||
"cloud",
|
||||
"cloud_from",
|
||||
"code",
|
||||
"collection",
|
||||
"column_add",
|
||||
"columns_increasing",
|
||||
"command",
|
||||
"command2",
|
||||
"command3",
|
||||
"command4",
|
||||
"command5",
|
||||
"comment",
|
||||
"compass",
|
||||
"compliance",
|
||||
"compliance2",
|
||||
"connector",
|
||||
"connector_add",
|
||||
"convert",
|
||||
"copy",
|
||||
"copy2",
|
||||
"credit_card",
|
||||
"data_download",
|
||||
"data_input",
|
||||
"data_output",
|
||||
"data_upload",
|
||||
"datetime",
|
||||
"docs",
|
||||
"drive",
|
||||
"duplicate",
|
||||
"edit",
|
||||
"enso_logo",
|
||||
"enter_node",
|
||||
"error",
|
||||
"exclamation",
|
||||
"exit_fullscreen",
|
||||
"expanded_node",
|
||||
"eye",
|
||||
"find",
|
||||
"folder",
|
||||
"folder_add",
|
||||
"folder_closed",
|
||||
"folder_opened",
|
||||
"fullscreen",
|
||||
"fullscreen2",
|
||||
"fullscreen3",
|
||||
"geo_map_distance",
|
||||
"geo_map_pin",
|
||||
"ghost",
|
||||
"google",
|
||||
"google_color",
|
||||
"graph",
|
||||
"graph2",
|
||||
"graph_editor",
|
||||
"graph_editor",
|
||||
"group",
|
||||
"header1",
|
||||
"header2",
|
||||
"header3",
|
||||
"heart",
|
||||
"heatmap",
|
||||
"help",
|
||||
"home",
|
||||
"home2",
|
||||
"icon/lock",
|
||||
"icon/skip",
|
||||
"image",
|
||||
"in_out",
|
||||
"in_out2",
|
||||
"input_number",
|
||||
"italic",
|
||||
"join",
|
||||
"join-1",
|
||||
"join2",
|
||||
"join2-1",
|
||||
"key",
|
||||
"key_cmd",
|
||||
"key_ctrl",
|
||||
"key_option",
|
||||
"keyboard_shortcuts",
|
||||
"lab",
|
||||
"lab2",
|
||||
"lab3",
|
||||
"libraries",
|
||||
"local_scope",
|
||||
"local_scope2",
|
||||
"local_scope3",
|
||||
"local_scope4",
|
||||
"location",
|
||||
"location2",
|
||||
"log",
|
||||
"log_cloud",
|
||||
"make_node_def_local_copy",
|
||||
"map_row",
|
||||
"marketplace",
|
||||
"math",
|
||||
"metadata",
|
||||
"microphone",
|
||||
"minus",
|
||||
"mixed",
|
||||
"no_auto_replay",
|
||||
"not_cloud",
|
||||
"not_exclamation",
|
||||
"not_paragraph",
|
||||
"numbered-list",
|
||||
"open",
|
||||
"open_count",
|
||||
"operators",
|
||||
"order",
|
||||
"paint",
|
||||
"paint_palette",
|
||||
"panic",
|
||||
"paragraph",
|
||||
"parse",
|
||||
"parse2",
|
||||
"parse3",
|
||||
"parse4",
|
||||
"path2",
|
||||
"people",
|
||||
"people_settings",
|
||||
"points",
|
||||
"predict",
|
||||
"preparation",
|
||||
"quote",
|
||||
"random",
|
||||
"recent",
|
||||
"record",
|
||||
"record_once",
|
||||
"redo",
|
||||
"refresh",
|
||||
"remove-textstyle",
|
||||
"restore",
|
||||
"right_panel",
|
||||
"right_side_panel",
|
||||
"robot",
|
||||
"root",
|
||||
"row_add",
|
||||
"scissors",
|
||||
"select",
|
||||
"select_column",
|
||||
"select_row",
|
||||
"settings",
|
||||
"settings1",
|
||||
"settings2",
|
||||
"shift_key",
|
||||
"shortcut",
|
||||
"shortcut2",
|
||||
"shortcut3",
|
||||
"show_all",
|
||||
"sort",
|
||||
"sort_ascending",
|
||||
"sort_descending",
|
||||
"split",
|
||||
"star",
|
||||
"strike-through",
|
||||
"system",
|
||||
"table",
|
||||
"table_clean",
|
||||
"table_edit",
|
||||
"table_from_columns",
|
||||
"table_from_rows",
|
||||
"tag",
|
||||
"temp",
|
||||
"text",
|
||||
"text2",
|
||||
"text3",
|
||||
"text_input",
|
||||
"time",
|
||||
"time2",
|
||||
"transform",
|
||||
"transform2",
|
||||
"transform3",
|
||||
"transform4",
|
||||
"trash",
|
||||
"trash2",
|
||||
"undo",
|
||||
"union",
|
||||
"unstable",
|
||||
"unstable2",
|
||||
"vector_add",
|
||||
"warning",
|
||||
"workflow_play",
|
||||
"zoom"
|
||||
]
|
@ -21,8 +21,6 @@ futures = { workspace = true }
|
||||
glob = "0.3.0"
|
||||
handlebars = "4.3.5"
|
||||
enso-build-base = { path = "../base" }
|
||||
enso-enso-font = { path = "../../lib/rust/enso-font" }
|
||||
enso-font = { path = "../../lib/rust/font" }
|
||||
enso-install-config = { path = "../install/config" }
|
||||
ide-ci = { path = "../ci_utils" }
|
||||
mime = { workspace = true }
|
||||
|
@ -21,13 +21,6 @@ use tempfile::TempDir;
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub mod dejavu_font;
|
||||
pub mod enso_font;
|
||||
pub mod fonts;
|
||||
pub mod google_font;
|
||||
|
||||
|
||||
|
||||
lazy_static! {
|
||||
/// Path to the file with build information that is consumed by the JS part of the IDE.
|
||||
///
|
||||
|
@ -1,134 +0,0 @@
|
||||
//! Definitions for DejaVu fonts, and functions for downloading and installing them.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_font as font;
|
||||
use enso_font::NonVariableDefinition;
|
||||
use enso_font::NonVariableFaceHeader;
|
||||
use ide_ci::cache::Cache;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
pub const PACKAGE_URL: &str = "https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-fonts-ttf-2.37.zip";
|
||||
|
||||
const FONT_FAMILY: &str = "DejaVu Sans Mono";
|
||||
|
||||
const FILE_PREFIX: &str = "DejaVu";
|
||||
|
||||
const FILE_SANS_MONO_PREFIX: &str = "SansMono";
|
||||
|
||||
const SANS_MONO_FONT_FAMILY_FONTS: &[(&str, font::Weight)] =
|
||||
&[("-Bold", font::Weight::Bold), ("", font::Weight::Normal)];
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === DejaVu Font ===
|
||||
// ===================
|
||||
|
||||
/// Internal helper function to download the DejaVu Sans Mono font. Exposed via thin wrapper
|
||||
/// functions.
|
||||
async fn install_sans_mono_internal(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
output_path: impl AsRef<Path>,
|
||||
css_output_info: Option<(&str, impl AsRef<Path>)>,
|
||||
) -> Result {
|
||||
let output_path = output_path.as_ref();
|
||||
let font = font();
|
||||
let faces = faces();
|
||||
let font = crate::ide::web::fonts::filter_font(&font, &faces);
|
||||
let package = download(cache, octocrab).await?;
|
||||
let get_font_files = extract_fonts(&font, package, output_path);
|
||||
let make_css_file = crate::ide::web::fonts::write_css_file_if_required(
|
||||
FONT_FAMILY,
|
||||
&font,
|
||||
&faces,
|
||||
css_output_info,
|
||||
);
|
||||
try_join!(get_font_files, make_css_file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install DejaVu Sans Mono, without an auto-generated CSS file.
|
||||
pub async fn install_sans_mono(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
output_path: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
install_sans_mono_internal(cache, octocrab, output_path, None::<(&str, &str)>).await
|
||||
}
|
||||
|
||||
/// Install DejaVu Sans Mono, including an auto-generated CSS file.
|
||||
pub async fn install_sans_mono_with_css(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
css_basepath: &str,
|
||||
output_path: impl AsRef<Path>,
|
||||
css_output_path: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
install_sans_mono_internal(cache, octocrab, output_path, Some((css_basepath, css_output_path)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// The DejaVu Sans Mono Font.
|
||||
pub fn font() -> NonVariableDefinition {
|
||||
SANS_MONO_FONT_FAMILY_FONTS
|
||||
.iter()
|
||||
.map(|(name, weight)| {
|
||||
let file = format!("{FILE_PREFIX}{FILE_SANS_MONO_PREFIX}{name}.ttf");
|
||||
let header = NonVariableFaceHeader {
|
||||
weight: *weight,
|
||||
width: font::Width::Normal,
|
||||
style: font::Style::Normal,
|
||||
};
|
||||
(header, file)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// All font faces contained in this font.
|
||||
pub fn faces() -> [NonVariableFaceHeader; 2] {
|
||||
[NonVariableFaceHeader { weight: font::Weight::Normal, ..default() }, NonVariableFaceHeader {
|
||||
weight: font::Weight::Bold,
|
||||
..default()
|
||||
}]
|
||||
}
|
||||
|
||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
||||
pub async fn extract_fonts(
|
||||
fonts: &NonVariableDefinition,
|
||||
package: impl AsRef<Path>,
|
||||
out_dir: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
let mut archive = ide_ci::archive::zip::open(&package)?;
|
||||
crate::ide::web::fonts::extract_fonts(&mut archive, fonts, package, out_dir, &mut |path| {
|
||||
let mut iter = path.iter();
|
||||
for _ in iter.by_ref().take(2) {}
|
||||
Box::from(iter.as_str())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Download the DejaVu Font package, with caching and GitHub authentication.
|
||||
pub async fn download(cache: &Cache, octocrab: &Octocrab) -> Result<Box<Path>> {
|
||||
Ok(cache
|
||||
.get(ide_ci::cache::download::DownloadFile {
|
||||
client: octocrab.client.clone(),
|
||||
key: ide_ci::cache::download::Key {
|
||||
url: PACKAGE_URL.parse().unwrap(),
|
||||
additional_headers: reqwest::header::HeaderMap::from_iter([(
|
||||
reqwest::header::ACCEPT,
|
||||
reqwest::header::HeaderValue::from_static(
|
||||
mime::APPLICATION_OCTET_STREAM.as_ref(),
|
||||
),
|
||||
)]),
|
||||
},
|
||||
})
|
||||
.await?
|
||||
.into_boxed_path())
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
//! Definitions for Enso Font, and functions for downloading and installing them.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_enso_font::ttf;
|
||||
use enso_font::NonVariableFaceHeader;
|
||||
use ide_ci::cache::Cache;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
const FONT_FAMILY: &str = "Enso";
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Enso Font ===
|
||||
// =================
|
||||
|
||||
pub async fn install_for_html(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
output_path: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
let output_path = output_path.as_ref();
|
||||
let html_fonts: HashMap<_, _> = [
|
||||
(NonVariableFaceHeader { weight: ttf::Weight::Normal, ..default() }, "Regular"),
|
||||
(NonVariableFaceHeader { weight: ttf::Weight::ExtraBold, ..default() }, "Bold"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let html_font_definitions = enso_enso_font::font()
|
||||
.variations()
|
||||
.filter(|v| html_fonts.contains_key(&v.header))
|
||||
.collect();
|
||||
let get_font_files = async {
|
||||
let package = download(cache, octocrab).await?;
|
||||
enso_enso_font::extract_fonts(&html_font_definitions, package, output_path).await
|
||||
};
|
||||
let make_css_file = async {
|
||||
let mut css = String::new();
|
||||
let url = ".";
|
||||
for (header, variant) in html_fonts {
|
||||
use std::fmt::Write;
|
||||
let def = html_font_definitions.get(header);
|
||||
let def = def.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Required font not found in Enso Font package. \
|
||||
Expected a font matching: {header:?}."
|
||||
)
|
||||
})?;
|
||||
let file = &def.file;
|
||||
// Note that this cannot use `generate_css_file`, as it specifies a different font
|
||||
// family for each variant.
|
||||
writeln!(&mut css, "@font-face {{")?;
|
||||
writeln!(&mut css, " font-family: '{FONT_FAMILY}{variant}';")?;
|
||||
writeln!(&mut css, " src: url('{url}/{file}');")?;
|
||||
writeln!(&mut css, " font-weight: normal;")?;
|
||||
writeln!(&mut css, " font-style: normal;")?;
|
||||
writeln!(&mut css, "}}")?;
|
||||
}
|
||||
let css_path = output_path.join("ensoFont.css");
|
||||
ide_ci::fs::tokio::write(css_path, css).await?;
|
||||
Ok(())
|
||||
};
|
||||
try_join!(get_font_files, make_css_file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn install_with_css(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
css_basepath: &str,
|
||||
output_path: impl AsRef<Path>,
|
||||
css_output_path: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
let output_path = output_path.as_ref();
|
||||
let font = enso_enso_font::font();
|
||||
let faces = enso_enso_font::faces();
|
||||
let font = crate::ide::web::fonts::filter_font(&font, &faces);
|
||||
let package = download(cache, octocrab).await?;
|
||||
let get_font_files = enso_enso_font::extract_fonts(&font, package, output_path);
|
||||
let css_output_info = Some((css_basepath, css_output_path));
|
||||
let make_css_file = crate::ide::web::fonts::write_css_file_if_required(
|
||||
FONT_FAMILY,
|
||||
&font,
|
||||
&faces,
|
||||
css_output_info,
|
||||
);
|
||||
try_join!(get_font_files, make_css_file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Download the Enso Font package, with caching and GitHub authentication.
|
||||
pub async fn download(cache: &Cache, octocrab: &Octocrab) -> Result<Box<Path>> {
|
||||
Ok(cache
|
||||
.get(ide_ci::cache::download::DownloadFile {
|
||||
client: octocrab.client.clone(),
|
||||
key: ide_ci::cache::download::Key {
|
||||
url: enso_enso_font::PACKAGE_URL.parse().unwrap(),
|
||||
additional_headers: reqwest::header::HeaderMap::from_iter([(
|
||||
reqwest::header::ACCEPT,
|
||||
reqwest::header::HeaderValue::from_static(
|
||||
mime::APPLICATION_OCTET_STREAM.as_ref(),
|
||||
),
|
||||
)]),
|
||||
},
|
||||
})
|
||||
.await?
|
||||
.into_boxed_path())
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_font::NonVariableDefinition;
|
||||
use enso_font::NonVariableFaceHeader;
|
||||
use ide_ci::archive::extract_files::ExtractFiles;
|
||||
use ide_ci::cache::Cache;
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === HTML Font Support ===
|
||||
// =========================
|
||||
|
||||
pub async fn install_html_fonts(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
output_path: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
let output_path = output_path.as_ref();
|
||||
crate::ide::web::google_font::install(cache, octocrab, "mplus1", output_path).await?;
|
||||
crate::ide::web::enso_font::install_for_html(cache, octocrab, output_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A CSS font style that is displayed as a CSS [`@font-face`] [`font-style`] value.
|
||||
///
|
||||
/// [`@font-face`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
||||
/// [`font-style`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-style
|
||||
#[derive(Debug, Display, Copy, Clone)]
|
||||
pub enum FontStyle {
|
||||
#[display("normal")]
|
||||
Normal,
|
||||
#[display("italic")]
|
||||
Italic,
|
||||
#[display("oblique")]
|
||||
Oblique,
|
||||
/// Angle is in degrees, between -90 and 90.
|
||||
#[display("oblique {_0}deg")]
|
||||
ObliqueWithAngle(f64),
|
||||
/// Angles are in degrees, between -90 and 90.
|
||||
#[display("oblique {_0}deg {_1}deg")]
|
||||
ObliqueWithAngleRange(f64, f64),
|
||||
}
|
||||
|
||||
/// A CSS font face that is displayed as a CSS [`@font-face`] declaration.
|
||||
///
|
||||
/// [`@font-face`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FontFace<'a> {
|
||||
family: Cow<'a, str>,
|
||||
path: Cow<'a, str>,
|
||||
weight: Option<u16>,
|
||||
style: Option<FontStyle>,
|
||||
}
|
||||
|
||||
impl Display for FontFace<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let family = &self.family;
|
||||
let path = &self.path;
|
||||
writeln!(f, "@font-face {{")?;
|
||||
writeln!(f, " font-family: '{family}';")?;
|
||||
writeln!(f, " src: url('{path}');")?;
|
||||
if let Some(weight) = self.weight {
|
||||
writeln!(f, " font-weight: {weight};")?;
|
||||
}
|
||||
if let Some(style) = self.style {
|
||||
writeln!(f, " font-style: {style};")?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a CSS file containing the given font files. Does not include font-weight, so use this
|
||||
/// only if the font weights are not known - prefer [`generate_css_file`] in all other cases.
|
||||
pub fn generate_css_file_from_paths<AsRefStr>(
|
||||
basepath: &str,
|
||||
family: &str,
|
||||
paths: impl Iterator<Item = AsRefStr>,
|
||||
) -> Result<String>
|
||||
where
|
||||
AsRefStr: AsRef<str>,
|
||||
{
|
||||
let mut css = String::new();
|
||||
for path in paths {
|
||||
use std::fmt::Write;
|
||||
let path = format!("{basepath}/{}", path.as_ref());
|
||||
let font_face = FontFace {
|
||||
family: Cow::Borrowed(family),
|
||||
path: Cow::Borrowed(path.as_str()),
|
||||
weight: None,
|
||||
style: None,
|
||||
};
|
||||
writeln!(&mut css, "{font_face}")?;
|
||||
}
|
||||
Ok(css)
|
||||
}
|
||||
|
||||
/// Generate a CSS file containing the given font family, including only the given font variations.
|
||||
pub fn generate_css_file<'a>(
|
||||
basepath: &str,
|
||||
family: &str,
|
||||
definitions: &NonVariableDefinition,
|
||||
fonts: impl Iterator<Item = &'a NonVariableFaceHeader>,
|
||||
) -> Result<String> {
|
||||
let mut css = String::new();
|
||||
for header in fonts {
|
||||
use std::fmt::Write;
|
||||
let def = definitions.get(*header);
|
||||
let def = def.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Required font not found in {family} Font package. \
|
||||
Expected a font matching: {header:?}."
|
||||
)
|
||||
})?;
|
||||
let path = format!("{basepath}/{}", def.file);
|
||||
let weight = def.header.weight.to_number();
|
||||
let font_face = FontFace {
|
||||
family: Cow::Borrowed(family),
|
||||
path: Cow::Borrowed(path.as_str()),
|
||||
weight: Some(weight),
|
||||
style: None,
|
||||
};
|
||||
writeln!(&mut css, "{font_face}")?;
|
||||
}
|
||||
Ok(css)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === Filter Font ===
|
||||
// ===================
|
||||
|
||||
pub fn filter_font(
|
||||
font: &NonVariableDefinition,
|
||||
faces: &[NonVariableFaceHeader],
|
||||
) -> NonVariableDefinition {
|
||||
font.variations().filter(|v| faces.contains(&v.header)).collect()
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =====================
|
||||
// === Make CSS File ===
|
||||
// =====================
|
||||
|
||||
pub async fn write_css_file_if_required(
|
||||
font_family: &str,
|
||||
font: &NonVariableDefinition,
|
||||
faces: &[NonVariableFaceHeader],
|
||||
css_output_info: Option<(&str, impl AsRef<Path>)>,
|
||||
) -> Result {
|
||||
if let Some((css_basepath, css_output_path)) = css_output_info {
|
||||
let contents = generate_css_file(css_basepath, font_family, font, faces.iter())?;
|
||||
ide_ci::fs::tokio::write(css_output_path, contents).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =====================
|
||||
// === Extract Fonts ===
|
||||
// =====================
|
||||
|
||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
||||
#[context("Failed to extract fonts from archive {}", package.as_ref().display())]
|
||||
pub async fn extract_fonts(
|
||||
archive: impl ExtractFiles,
|
||||
fonts: &NonVariableDefinition,
|
||||
package: impl AsRef<Path>,
|
||||
out_dir: impl AsRef<Path>,
|
||||
normalize_path: &mut impl FnMut(&Path) -> Box<str>,
|
||||
) -> Result {
|
||||
ide_ci::fs::tokio::create_dir_if_missing(out_dir.as_ref()).await?;
|
||||
let mut files_expected: HashSet<_> = fonts.files().collect();
|
||||
archive
|
||||
.extract_files(|path_in_archive| {
|
||||
let stripped_path = normalize_path(path_in_archive);
|
||||
if files_expected.remove(stripped_path.as_ref()) {
|
||||
Some(out_dir.as_ref().join(stripped_path.as_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
ensure!(files_expected.is_empty(), "Required fonts not found in archive: {files_expected:?}.");
|
||||
Ok(())
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
//! Functions for downloading and installing Google fonts.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use ide_ci::cache::Cache;
|
||||
use ide_ci::cache::Storable;
|
||||
use ide_ci::github;
|
||||
use ide_ci::github::RepoRef;
|
||||
use octocrab::models::repos;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
/// Google Fonts repository.
|
||||
pub const REPOSITORY: RepoRef = RepoRef { owner: "google", name: "fonts" };
|
||||
|
||||
/// Path to the directory on the Google Fonts repository where we get the fonts from.
|
||||
///
|
||||
/// The directory name denotes the license of the fonts. In our case this is SIL OPEN FONT LICENSE
|
||||
/// Version 1.1, commonly known as OFL.
|
||||
pub const DIRECTORY: &str = "ofl";
|
||||
|
||||
/// We keep dependency to a fixed commit, so we can safely cache it.
|
||||
///
|
||||
/// There are no known reasons not to bump this.
|
||||
pub const COMMIT_SHA1: &str = "ea893a43af7c5ab5ccee189fc2720788d99887ed";
|
||||
|
||||
|
||||
// ==============
|
||||
// === Family ===
|
||||
// ==============
|
||||
|
||||
/// Identifies uniquely a source of font family download.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Family {
|
||||
/// Remote repository with fonts.
|
||||
pub repo: github::Repo,
|
||||
/// Which commit we want to be downloaded.
|
||||
pub r#ref: String,
|
||||
/// Font family. It corresponds to the subdirectories names (under the top-level
|
||||
/// license-denoting directories).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Family {
|
||||
/// List content items in the repository that contain TTF files for the given font family.
|
||||
pub async fn list_ttf(
|
||||
&self,
|
||||
handle: github::repo::Handle<impl IsRepo>,
|
||||
) -> Result<Vec<repos::Content>> {
|
||||
let path = format!("{DIRECTORY}/{}", self.name);
|
||||
let files = handle.repos().get_content().r#ref(&self.r#ref).path(path).send().await?;
|
||||
Ok(files.items.into_iter().filter(|file| file.name.ends_with(".ttf")).collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ====================
|
||||
// === DownloadFont ===
|
||||
// ====================
|
||||
|
||||
/// Description of the job to download the fonts.
|
||||
#[derive(Clone)]
|
||||
#[derive_where(Debug)]
|
||||
pub struct DownloadFont {
|
||||
pub family: Family,
|
||||
/// Possible authentication to GitHub (to get bigger rate limit).
|
||||
#[derive_where(skip)]
|
||||
pub octocrab: Octocrab,
|
||||
}
|
||||
|
||||
impl DownloadFont {
|
||||
/// Get a handle to the remote repository with the fonts.
|
||||
pub fn handle(&self) -> github::repo::Handle<impl IsRepo> {
|
||||
self.family.repo.handle(&self.octocrab)
|
||||
}
|
||||
|
||||
/// Download the font family to the given directory. They will be placed in the output
|
||||
/// directory. The function returns relative paths to the downloaded files.
|
||||
pub async fn download(&self, output_path: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
|
||||
let files = self.family.list_ttf(self.handle()).await?;
|
||||
let mut ret = Vec::new();
|
||||
for file in &files {
|
||||
let destination_file = output_path.as_ref().join(&file.name);
|
||||
let url = file.download_url.as_ref().context("Missing 'download_url' in the reply.")?;
|
||||
let reply = ide_ci::io::web::client::download(&self.octocrab.client, url).await?;
|
||||
ide_ci::io::web::stream_to_file(reply, &destination_file).await?;
|
||||
ret.push(file.name.as_str().into());
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for DownloadFont {
|
||||
/// In metadata form we just store paths relative to the store.
|
||||
type Metadata = Vec<PathBuf>;
|
||||
/// Here paths are absolute.
|
||||
type Output = Vec<PathBuf>;
|
||||
type Key = Family;
|
||||
|
||||
fn generate(
|
||||
&self,
|
||||
_cache: Cache,
|
||||
store: PathBuf,
|
||||
) -> BoxFuture<'static, Result<Self::Metadata>> {
|
||||
let this = self.clone();
|
||||
async move {
|
||||
let fonts = this.download(&store).await?;
|
||||
Ok(fonts)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn adapt(
|
||||
&self,
|
||||
cache: PathBuf,
|
||||
mut metadata: Self::Metadata,
|
||||
) -> BoxFuture<'static, Result<Self::Output>> {
|
||||
async move {
|
||||
for font in &mut metadata {
|
||||
*font = cache.join(&*font);
|
||||
}
|
||||
Ok(metadata)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn key(&self) -> Self::Key {
|
||||
self.family.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ===================
|
||||
// === Entry Point ===
|
||||
// ===================
|
||||
|
||||
/// Install a Google font, without an auto-generated CSS file.
|
||||
pub async fn install(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
family: &str,
|
||||
output_path: impl AsRef<Path>,
|
||||
) -> Result<Vec<PathBuf>> {
|
||||
let family =
|
||||
Family { repo: REPOSITORY.into(), r#ref: COMMIT_SHA1.into(), name: family.into() };
|
||||
let font = DownloadFont { family, octocrab: octocrab.clone() };
|
||||
let cached_fonts = cache.get(font).await?;
|
||||
let copy_futures =
|
||||
cached_fonts.into_iter().map(|font| ide_ci::fs::tokio::copy_to(font, &output_path));
|
||||
let result = futures::future::join_all(copy_futures).await.into_iter().try_collect()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Install a Google font, including an auto-generated CSS file.
|
||||
pub async fn install_with_css(
|
||||
cache: &Cache,
|
||||
octocrab: &Octocrab,
|
||||
family: &str,
|
||||
css_family: &str,
|
||||
css_basepath: &str,
|
||||
output_path: impl AsRef<Path>,
|
||||
css_output_path: impl AsRef<Path>,
|
||||
) -> Result<Vec<PathBuf>> {
|
||||
let paths = install(cache, octocrab, family, output_path).await?;
|
||||
let css = crate::ide::web::fonts::generate_css_file_from_paths(
|
||||
css_basepath,
|
||||
css_family,
|
||||
paths.iter().flat_map(|path| path.try_file_name().map(|name| name.as_str())),
|
||||
)?;
|
||||
ide_ci::fs::tokio::write(css_output_path, css).await?;
|
||||
Ok(paths)
|
||||
}
|
||||
|
||||
// =============
|
||||
// === Tests ===
|
||||
// =============
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn new_download() -> Result {
|
||||
setup_logging().ok();
|
||||
let path = r"C:\temp\google_fonts2";
|
||||
let octocrab = github::setup_octocrab().await?;
|
||||
let cache = Cache::new_default().await?;
|
||||
let aaa = install(&cache, &octocrab, "mplus1", path).await?;
|
||||
dbg!(aaa);
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -64,32 +64,6 @@ impl IsTarget for Gui {
|
||||
let WithDestination { inner: _, destination } = job;
|
||||
async move {
|
||||
let repo_root = &context.repo_root;
|
||||
crate::ide::web::google_font::install_with_css(
|
||||
&context.cache,
|
||||
&context.octocrab,
|
||||
"mplus1",
|
||||
"M PLUS 1",
|
||||
"/font-mplus1",
|
||||
&repo_root.app.gui.public.font_mplus_1,
|
||||
&repo_root.app.gui.src.project_view.assets.font_mplus_1_css,
|
||||
)
|
||||
.await?;
|
||||
crate::ide::web::dejavu_font::install_sans_mono_with_css(
|
||||
&context.cache,
|
||||
&context.octocrab,
|
||||
"/font-dejavu",
|
||||
&repo_root.app.gui.public.font_dejavu,
|
||||
&repo_root.app.gui.src.project_view.assets.font_dejavu_css,
|
||||
)
|
||||
.await?;
|
||||
crate::ide::web::enso_font::install_with_css(
|
||||
&context.cache,
|
||||
&context.octocrab,
|
||||
"/font-enso",
|
||||
&repo_root.app.gui.public.font_enso,
|
||||
&repo_root.app.gui.src.project_view.assets.font_enso_css,
|
||||
)
|
||||
.await?;
|
||||
crate::web::install(repo_root).await?;
|
||||
crate::web::run_script(repo_root, crate::web::Script::Build).await?;
|
||||
ide_ci::fs::mirror_directory(
|
||||
|
@ -191,6 +191,7 @@ export default [
|
||||
'**/.cache/**',
|
||||
'**/playwright-report',
|
||||
'**/dist',
|
||||
'**/mockDist',
|
||||
'**/build.mjs',
|
||||
'**/*.timestamp-*.mjs',
|
||||
'**/node_modules',
|
||||
|
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "enso-enso-font"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
enso-font = { path = "../font" }
|
||||
ide-ci = { path = "../../../build/ci_utils" }
|
||||
owned_ttf_parser = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
@ -1,112 +0,0 @@
|
||||
//! The Enso Font. This crate supports downloading and unpacking the font family, as well as
|
||||
//! constructing a reduced font family from a subset of the fonts.
|
||||
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![deny(unconditional_recursion)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
|
||||
use ide_ci::prelude::*;
|
||||
|
||||
use enso_font::NonVariableDefinition;
|
||||
use enso_font::NonVariableFaceHeader;
|
||||
|
||||
|
||||
// ==============
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub use owned_ttf_parser as ttf;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
/// The name of the Enso font family.
|
||||
pub const FONT_FAMILY: &str = "enso";
|
||||
|
||||
const FONTS: &[(&str, ttf::Weight)] = &[
|
||||
("Black", ttf::Weight::Black),
|
||||
("Bold", ttf::Weight::Bold),
|
||||
("ExtraBold", ttf::Weight::ExtraBold),
|
||||
("ExtraLight", ttf::Weight::ExtraLight),
|
||||
("Light", ttf::Weight::Light),
|
||||
("Medium", ttf::Weight::Medium),
|
||||
("Regular", ttf::Weight::Normal),
|
||||
("SemiBold", ttf::Weight::SemiBold),
|
||||
("Thin", ttf::Weight::Thin),
|
||||
];
|
||||
|
||||
/// The URL for the Enso Font package.
|
||||
pub const PACKAGE_URL: &str =
|
||||
"https://github.com/enso-org/font/releases/download/1.0/enso-font-1.0.tar.gz";
|
||||
const PACKAGE_FONTS_PREFIX: &str = "ttf";
|
||||
|
||||
/// Font features.
|
||||
pub mod feature {
|
||||
/// The flag identifying the ligature feature in this font.
|
||||
pub const LIGATURES: &str = "liga";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Enso Font ===
|
||||
// =================
|
||||
|
||||
/// The Enso Font.
|
||||
pub fn font() -> NonVariableDefinition {
|
||||
FONTS
|
||||
.iter()
|
||||
.map(|(name, weight)| {
|
||||
let file = format!("Enso-{name}.ttf");
|
||||
let header = NonVariableFaceHeader {
|
||||
weight: *weight,
|
||||
width: ttf::Width::Normal,
|
||||
style: ttf::Style::Normal,
|
||||
};
|
||||
(header, file)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// All font faces contained in this font.
|
||||
pub fn faces() -> [NonVariableFaceHeader; 9] {
|
||||
[
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Thin, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::ExtraLight, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Light, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Normal, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Medium, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::SemiBold, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Bold, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::ExtraBold, ..default() },
|
||||
NonVariableFaceHeader { weight: ttf::Weight::Black, ..default() },
|
||||
]
|
||||
}
|
||||
|
||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
||||
#[context("Failed to extract fonts from archive {}", package.as_ref().display())]
|
||||
pub async fn extract_fonts(
|
||||
fonts: &NonVariableDefinition,
|
||||
package: impl AsRef<Path>,
|
||||
out_dir: impl AsRef<Path>,
|
||||
) -> Result {
|
||||
use ide_ci::archive::extract_files::ExtractFiles;
|
||||
ide_ci::fs::tokio::create_dir_if_missing(out_dir.as_ref()).await?;
|
||||
let mut files_expected: HashSet<_> = fonts.files().collect();
|
||||
ide_ci::archive::tar::Archive::open_tar_gz(&package)
|
||||
.await?
|
||||
.extract_files(|path_in_archive| {
|
||||
path_in_archive
|
||||
.strip_prefix(PACKAGE_FONTS_PREFIX)
|
||||
.ok()
|
||||
.filter(|path| path.to_str().map_or(false, |path| files_expected.remove(path)))
|
||||
.map(|path| out_dir.as_ref().join(path))
|
||||
})
|
||||
.await?;
|
||||
ensure!(files_expected.is_empty(), "Required fonts not found in archive: {files_expected:?}.");
|
||||
Ok(())
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "enso-font"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
owned_ttf_parser = { workspace = true }
|
||||
derive_more = { workspace = true }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
@ -1,203 +0,0 @@
|
||||
//! Definition of a font family, a set of related font faces.
|
||||
//!
|
||||
//! # One font face per file
|
||||
//! The implementation of this library has an important limitation that should not cause any
|
||||
//! problems, however, it is important to be aware of it. In case of non-variable fonts, only one
|
||||
//! font face is supported per file. A font face is identified by (width, weight, style) triple (see
|
||||
//! [`NonVariableFaceHeader`]) to learn more. If you want to use font faces defined in the same
|
||||
//! file (e.g. ".ttf" file), you have to split them into multiple files first. All major browsers
|
||||
//! have the same limitation. For example, you are unable to define in CSS a new font family by
|
||||
//! loading different faces from the same file with the `@font-face` rule
|
||||
//! (https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face).
|
||||
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![deny(unconditional_recursion)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
use derive_more::Deref;
|
||||
use derive_more::Display;
|
||||
|
||||
|
||||
// ==============
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub use owned_ttf_parser::Style;
|
||||
pub use owned_ttf_parser::Weight;
|
||||
pub use owned_ttf_parser::Width;
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Name ===
|
||||
// ============
|
||||
|
||||
/// A name of a font. The name is normalized to case-insensitive form during construction to
|
||||
/// eliminate accidental mistakes, the same way as it's done in CSS:
|
||||
/// https://stackoverflow.com/questions/17967371/are-property-values-in-css-case-sensitive
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Deref, Display, Hash, PartialEq, Eq)]
|
||||
pub struct Name {
|
||||
pub normalized: String,
|
||||
}
|
||||
|
||||
impl From<&Name> for Name {
|
||||
fn from(name: &Name) -> Self {
|
||||
name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Name {
|
||||
fn from(name: &str) -> Self {
|
||||
let normalized = name.to_lowercase();
|
||||
Name { normalized }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for Name {
|
||||
fn from(name: &String) -> Self {
|
||||
name.as_str().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Name {
|
||||
fn from(name: String) -> Self {
|
||||
(&name).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === Font Family ===
|
||||
// ===================
|
||||
|
||||
/// Definition of a font family. Font family consist of one font face in case of variable fonts or
|
||||
/// multiple font faces in case of non-variable ones.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FontFamily {
|
||||
Variable(VariableDefinition),
|
||||
NonVariable(NonVariableDefinition),
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// === VariableDefinition ===
|
||||
// ==========================
|
||||
|
||||
/// Definition of a variable font family. See the following link to learn more about variable fonts:
|
||||
/// https://docs.microsoft.com/en-us/windows/win32/directwrite/opentype-variable-fonts
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VariableDefinition {
|
||||
/// Name of the file that the font data was read from. It contains the file extension, for
|
||||
/// example `MPLUS1[wght].ttf`.
|
||||
pub file_name: String,
|
||||
}
|
||||
|
||||
impl VariableDefinition {
|
||||
/// Constructor.
|
||||
pub fn new(file_name: impl Into<String>) -> Self {
|
||||
let file_name = file_name.into();
|
||||
Self { file_name }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============================
|
||||
// === NonVariableDefinition ===
|
||||
// =============================
|
||||
|
||||
/// Definition of a non-variable font family. Contains mapping between (width, weight, style) triple
|
||||
/// (see [`NonVariableFaceHeader`]) to learn more) and file names.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NonVariableDefinition {
|
||||
variations: std::collections::HashMap<NonVariableFaceHeader, String>,
|
||||
}
|
||||
|
||||
impl NonVariableDefinition {
|
||||
/// All weights defined in this font family.
|
||||
pub fn possible_weights(&self) -> Vec<Weight> {
|
||||
let mut weights: Vec<_> = self.variations().map(|var| var.header.weight).collect();
|
||||
weights.sort_unstable_by_key(|w| w.to_number());
|
||||
weights.dedup();
|
||||
weights
|
||||
}
|
||||
|
||||
/// Return an iterator over the filenames associated with fonts in this family.
|
||||
pub fn files(&self) -> impl Iterator<Item = &str> {
|
||||
self.variations().map(|v| v.file)
|
||||
}
|
||||
|
||||
/// Return an iterator over the fonts in this family.
|
||||
pub fn variations(&self) -> impl Iterator<Item = NonVariableVariation> {
|
||||
self.variations
|
||||
.iter()
|
||||
.map(|(header, file)| NonVariableVariation { header: *header, file: file.as_str() })
|
||||
}
|
||||
|
||||
/// Return the font in this family that exactly matches the given parameters, if any.
|
||||
pub fn get(&self, header: NonVariableFaceHeader) -> Option<NonVariableVariation> {
|
||||
self.variations.get(&header).map(|file| NonVariableVariation { header, file })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(NonVariableFaceHeader, String)> for NonVariableDefinition {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where T: IntoIterator<Item = (NonVariableFaceHeader, String)> {
|
||||
let map = iter.into_iter().collect();
|
||||
Self { variations: map }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<NonVariableVariation<'a>> for NonVariableDefinition {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where T: IntoIterator<Item = NonVariableVariation<'a>> {
|
||||
let map = iter.into_iter().map(|v| (v.header, v.file.to_owned())).collect();
|
||||
Self { variations: map }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================
|
||||
// === NonVariableVariation ===
|
||||
// ============================
|
||||
|
||||
/// The parameters of a font, and the filename where it should be found.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NonVariableVariation<'a> {
|
||||
/// The parameters of the font.
|
||||
pub header: NonVariableFaceHeader,
|
||||
/// The filename for the font.
|
||||
pub file: &'a str,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============================
|
||||
// === NonVariableFaceHeader ===
|
||||
// =============================
|
||||
|
||||
/// Combination of all information allowing mapping the font face to a font file for non-variable
|
||||
/// fonts. For variable fonts, there is just one definition for any combination of the parameters.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NonVariableFaceHeader {
|
||||
pub width: Width,
|
||||
pub weight: Weight,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl NonVariableFaceHeader {
|
||||
/// Constructor.
|
||||
pub const fn new(width: Width, weight: Weight, style: Style) -> Self {
|
||||
Self { width, weight, style }
|
||||
}
|
||||
}
|
@ -428,9 +428,6 @@ importers:
|
||||
'@types/tar':
|
||||
specifier: ^6.1.4
|
||||
version: 6.1.13
|
||||
'@types/unbzip2-stream':
|
||||
specifier: ^1.4.3
|
||||
version: 1.4.3
|
||||
'@types/validator':
|
||||
specifier: ^13.11.7
|
||||
version: 13.12.0
|
||||
@ -524,9 +521,6 @@ importers:
|
||||
typescript:
|
||||
specifier: ^5.5.3
|
||||
version: 5.5.3
|
||||
unbzip2-stream:
|
||||
specifier: ^1.4.3
|
||||
version: 1.4.3
|
||||
vite:
|
||||
specifier: ^5.3.5
|
||||
version: 5.3.5(@types/node@20.11.21)(lightningcss@1.25.1)
|
||||
@ -3065,18 +3059,12 @@ packages:
|
||||
'@types/tar@6.1.13':
|
||||
resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==}
|
||||
|
||||
'@types/through@0.0.33':
|
||||
resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==}
|
||||
|
||||
'@types/to-ico@1.1.3':
|
||||
resolution: {integrity: sha512-3Ew8Hsz/qiDGzwvz75pjRU+6Ocfvrit6hHfirauEdJXxdro73MTe5XdcANh4GZ2wdJqWw9BIuHwWgKAfjUXoGw==}
|
||||
|
||||
'@types/tough-cookie@4.0.5':
|
||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||
|
||||
'@types/unbzip2-stream@1.4.3':
|
||||
resolution: {integrity: sha512-D8X5uuJRISqc8YtwL8jNW2FpPdUOCYXbfD6zNROCTbVXK9nawucxh10tVXE3MPjnHdRA1LvB0zDxVya/lBsnYw==}
|
||||
|
||||
'@types/validator@13.12.0':
|
||||
resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
|
||||
|
||||
@ -6960,9 +6948,6 @@ packages:
|
||||
thenify@3.3.1:
|
||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||
|
||||
through@2.3.8:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
@ -7121,9 +7106,6 @@ packages:
|
||||
unbox-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||
|
||||
unbzip2-stream@1.4.3:
|
||||
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
|
||||
|
||||
undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
|
||||
@ -10504,20 +10486,12 @@ snapshots:
|
||||
'@types/node': 20.11.21
|
||||
minipass: 4.2.8
|
||||
|
||||
'@types/through@0.0.33':
|
||||
dependencies:
|
||||
'@types/node': 20.11.21
|
||||
|
||||
'@types/to-ico@1.1.3':
|
||||
dependencies:
|
||||
'@types/node': 20.11.21
|
||||
|
||||
'@types/tough-cookie@4.0.5': {}
|
||||
|
||||
'@types/unbzip2-stream@1.4.3':
|
||||
dependencies:
|
||||
'@types/through': 0.0.33
|
||||
|
||||
'@types/validator@13.12.0': {}
|
||||
|
||||
'@types/verror@1.10.10':
|
||||
@ -15079,8 +15053,6 @@ snapshots:
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
|
||||
through@2.3.8: {}
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinybench@2.8.0: {}
|
||||
@ -15238,11 +15210,6 @@ snapshots:
|
||||
has-symbols: 1.0.3
|
||||
which-boxed-primitive: 1.0.2
|
||||
|
||||
unbzip2-stream@1.4.3:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
through: 2.3.8
|
||||
|
||||
undici-types@5.26.5: {}
|
||||
|
||||
undici@6.13.0: {}
|
||||
|
Loading…
Reference in New Issue
Block a user