fix(font-size): detect and apply padding and margin at 0 (#2737)

This commit is contained in:
Baptiste Augrain
2026-03-07 23:27:25 +01:00
committed by GitHub
parent a06532dfaf
commit 81a5f96569
2 changed files with 124 additions and 52 deletions

View File

@@ -4,7 +4,7 @@ import path from 'node:path';
import process from 'node:process';
import fse from '@zokugun/fs-extra-plus/async';
import { err, OK, type Result, stringifyError, xtry } from '@zokugun/xtry';
import postcss, { type Rule } from 'postcss';
import postcss, { Root, type Rule } from 'postcss';
type Area = {
name: string;
@@ -16,6 +16,7 @@ type Area = {
const PX_REGEX = /(-?\d+(\.\d+)?)px\b/g;
const COEFF_PRECISION = 6;
const HEADER = '/*** Generated for Custom Font Size ***/';
const ZEROS = ['margin', 'padding'];
const AREAS: Record<string, Area> = {
activitybar: {
@@ -27,7 +28,7 @@ const AREAS: Record<string, Area> = {
bottompane: {
name: 'bottompane',
defaultSize: 13,
files: ['src/vs/workbench/browser/parts/panel/media/panelpart.css'],
files: ['src/vs/workbench/browser/parts/panel/media/panelpart.css', 'src/vs/base/browser/ui/actionbar/actionbar.css'],
prefixes: ['.monaco-workbench .part.panel'],
},
statusbar: {
@@ -50,12 +51,12 @@ const AREAS: Record<string, Area> = {
},
};
function formatCoefficient(n: number): string {
function formatCoefficient(n: number): string { // {{{
const fixed = n.toFixed(COEFF_PRECISION);
return fixed.replace(/\.?0+$/, '');
}
} // }}}
function replacePx(area: Area) {
function replacePx(area: Area) { // {{{
return (match: string, numStr: string): string => {
const pxValue = Number.parseFloat(numStr);
@@ -67,13 +68,13 @@ function replacePx(area: Area) {
return `calc(var(--vscode-workbench-${area.name}-font-size) * ${coeff})`;
};
}
} // }}}
function transformPxValue(value: string, area: Area): string {
function transformPxValue(value: string, area: Area): string { // {{{
return value.replaceAll(PX_REGEX, replacePx(area));
}
} // }}}
async function processFile(filePath: string, area: Area): Promise<Result<void, string>> {
async function processFile(filePath: string, areas: Area[]): Promise<Result<void, string>> { // {{{
const readResult = await fse.readFile(filePath, 'utf8');
if(readResult.fails) {
return err(stringifyError(readResult.error));
@@ -88,7 +89,27 @@ async function processFile(filePath: string, area: Area): Promise<Result<void, s
const generatedRoot = postcss.root();
postcssResult.value.walkRules((rule: Rule) => {
for(const area of areas) {
processFileArea(postcssResult.value, generatedRoot, area)
}
if(generatedRoot.nodes && generatedRoot.nodes.length > 0) {
const writeResult = await fse.writeFile(filePath, content + `\n\n\n${HEADER}\n\n` + generatedRoot.toString(), 'utf8');
if(writeResult.fails) {
return err(stringifyError(readResult.error));
}
console.log(`Generated: ${filePath}`);
}
else {
console.log(`No px sizes found in: ${filePath}`);
}
return OK;
} // }}}
function processFileArea(postcssResult: Root, generatedRoot: Root, area: Area): void { // {{{
postcssResult.walkRules((rule: Rule) => {
const declarationsToAdd: Array<{ prop: string; value: string }> = [];
rule.walkDecls((declaration) => {
@@ -100,6 +121,9 @@ async function processFile(filePath: string, area: Area): Promise<Result<void, s
else if(declaration.value === 'auto' && (declaration.prop === 'height' || declaration.prop === 'width')) {
declarationsToAdd.push({ prop: declaration.prop, value: 'auto' });
}
else if(declaration.value === '0' && ZEROS.includes(declaration.prop)) {
declarationsToAdd.push({ prop: declaration.prop, value: '0' });
}
});
if(declarationsToAdd.length > 0) {
@@ -129,23 +153,9 @@ async function processFile(filePath: string, area: Area): Promise<Result<void, s
}
}
});
} // }}}
if(generatedRoot.nodes && generatedRoot.nodes.length > 0) {
const writeResult = await fse.writeFile(filePath, content + `\n\n\n${HEADER}\n\n` + generatedRoot.toString(), 'utf8');
if(writeResult.fails) {
return err(stringifyError(readResult.error));
}
console.log(`Generated: ${filePath}`);
}
else {
console.log(`No px sizes found in: ${filePath}`);
}
return OK;
}
function extractOriginal(content: string): string {
function extractOriginal(content: string): string { // {{{
const index = content.indexOf(HEADER);
if(index === -1) {
@@ -153,15 +163,15 @@ function extractOriginal(content: string): string {
}
return content.slice(0, Math.max(0, index - 3));
}
} // }}}
function extractStyle(selector: string): string {
function extractStyle(selector: string): string { // {{{
const match = /^(\.[\w-]+)/.exec(selector);
return match?.[1] ?? '';
}
} // }}}
function mergeSelector(selectors: string[], prefixes: string[], index: number): void {
function mergeSelector(selectors: string[], prefixes: string[], index: number): void { // {{{
if(index >= prefixes.length) {
return;
}
@@ -186,9 +196,9 @@ function mergeSelector(selectors: string[], prefixes: string[], index: number):
else {
selectors.splice(index + 1, 0, ...prefixes.slice(index));
}
}
} // }}}
function prefixSelector(selector: string, prefixParts: string[]): string {
function prefixSelector(selector: string, prefixParts: string[]): string { // {{{
const parts = selector.split(' ');
if(parts[0] === '.mac' || parts[0] === '.linux' || parts[0] === '.windows') {
@@ -201,27 +211,38 @@ function prefixSelector(selector: string, prefixParts: string[]): string {
}
return parts.join(' ');
}
} // }}}
async function main(): Promise<void> {
async function main(): Promise<void> { // {{{
const name = process.argv[2];
const area = AREAS[name];
if(area) {
for(const file of area.files) {
const result = await processFile(path.join('..', 'vscode', file), area);
const result = await processFile(path.join('..', 'vscode', file), [area]);
if(result.fails) {
console.error(`Error processing ${file}:`, result.error);
}
}
}
else if(name === 'all') {
const files: Record<string, Area[]> = {};
for(const area of Object.values(AREAS)) {
for(const file of area.files) {
const result = await processFile(path.join('..', 'vscode', file), area);
if(result.fails) {
console.error(`Error processing ${file}:`, result.error);
if(files[file]) {
files[file].push(area)
}
else {
files[file] = [area]
}
}
}
for(const [file, areas] of Object.entries(files)) {
const result = await processFile(path.join('..', 'vscode', file), areas);
if(result.fails) {
console.error(`Error processing ${file}:`, result.error);
}
}
}
@@ -230,6 +251,6 @@ async function main(): Promise<void> {
console.log(`\nAvailable areas:\n- ${Object.keys(AREAS).join('\n- ')}`);
return;
}
}
} // }}}
await main();