Skip to content

Commit

Permalink
Refactor: reuse more common logic
Browse files Browse the repository at this point in the history
  • Loading branch information
metonym committed Mar 19, 2024
1 parent 8772abb commit a168c82
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 68 deletions.
26 changes: 10 additions & 16 deletions src/plugins/OptimizeCssPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import type { Compiler } from "webpack";
import { RE_EXT_CSS } from "../constants";
import type { OptimizeCssOptions } from "./utils";
import {
getComponentClasses,
getCssAllowlist,
isCarbonSvelteComponent,
logComparison,
postcssOptimizeCarbon,
stringSizeInKB,
} from "./utils";

class OptimizeCssPlugin {
Expand All @@ -31,13 +28,13 @@ class OptimizeCssPlugin {
OptimizeCssPlugin.name,
(compilation) => {
const hooks = NormalModule.getCompilationHooks(compilation);
const css_allowlist = getCssAllowlist();
const ids: string[] = [];

hooks.beforeSnapshot.tap(OptimizeCssPlugin.name, (module) => {
if (module.buildInfo?.fileDependencies) {
for (const id of module.buildInfo.fileDependencies) {
if (isCarbonSvelteComponent(id)) {
css_allowlist.push(...getComponentClasses(id));
ids.push(id);
}
}
}
Expand All @@ -49,23 +46,20 @@ class OptimizeCssPlugin {
stage: Compilation.PROCESS_ASSETS_STAGE_DERIVED,
},
(assets) => {
if (ids.length === 0) return;

for (const [id] of Object.entries(assets)) {
if (RE_EXT_CSS.test(id)) {
const source = assets[id].source();
const original_css = assets[id].source();
const optimized_css = postcss([
postcssOptimizeCarbon({ ...this.options, css_allowlist }),
postcssOptimizeCarbon({ ...this.options, ids }),
discardEmpty(),
]).process(source).css;

const original_size = stringSizeInKB(source.toString());
const optimized_size = stringSizeInKB(optimized_css);
]).process(original_css).css;

if (optimized_size < original_size) {
compilation.updateAsset(id, new RawSource(optimized_css));
compilation.updateAsset(id, new RawSource(optimized_css));

if (this.options.verbose) {
logComparison({ original_size, optimized_size, id });
}
if (this.options.verbose) {
logComparison({ original_css, optimized_css, id });
}
}
}
Expand Down
25 changes: 10 additions & 15 deletions src/plugins/optimize-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,42 @@ import type { Plugin } from "vite";
import { RE_EXT_CSS } from "../constants";
import type { OptimizeCssOptions } from "./utils";
import {
getComponentClasses,
getCssAllowlist,
isCarbonSvelteComponent,
logComparison,
postcssOptimizeCarbon,
stringSizeInKB,
} from "./utils";

export const optimizeCss = (options?: OptimizeCssOptions): Plugin => {
const verbose = options?.verbose !== false;
const preserveAllIBMFonts = options?.preserveAllIBMFonts === true;
const css_allowlist = getCssAllowlist();
const ids: string[] = [];

return {
name: "vite:carbon:optimize-css",
apply: "build",
enforce: "post",
transform(_, id) {
if (isCarbonSvelteComponent(id)) {
css_allowlist.push(...getComponentClasses(id));
ids.push(id);
}
},
async generateBundle(_, bundle) {
if (ids.length === 0) return;

for (const id in bundle) {
const file = bundle[id];

if (file.type === "asset" && RE_EXT_CSS.test(id)) {
const original_css = file.source;
const optimized_css = postcss([
postcssOptimizeCarbon({ preserveAllIBMFonts, css_allowlist }),
postcssOptimizeCarbon({ preserveAllIBMFonts, ids }),
discardEmpty(),
]).process(file.source).css;

const original_size = stringSizeInKB(file.source.toString());
const optimized_size = stringSizeInKB(optimized_css);
]).process(original_css).css;

if (optimized_size < original_size) {
file.source = optimized_css;
file.source = optimized_css;

if (verbose) {
logComparison({ original_size, optimized_size, id });
}
if (verbose) {
logComparison({ original_css, optimized_css, id });
}
}
}
Expand Down
79 changes: 42 additions & 37 deletions src/plugins/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,6 @@ import type { Plugin } from "postcss";
import { components } from "../component-index";
import { BITS_DENOM, CarbonSvelte, RE_EXT_SVELTE } from "../constants";

export type OptimizeCssOptions = {
/**
* By default, the plugin will log the size diff
* between the original and optimized CSS.
*
* Set to `false` to disable verbose logging.
* @default true
*/
verbose?: boolean;

/**
* By default, pre-compiled Carbon StyleSheets ship `@font-face`
* rules for all available IBM Plex fonts, many of which are
* not actually used in Carbon Svelte components.
*
* The recommended optimization is to only preserve IBM Plex
* fonts with 400/600-weight and normal-font-style rules.
*
* Set to `true` to disable this behavior.
* @default false
*/
preserveAllIBMFonts?: boolean;
};

const formatter = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });

export function toHumanReadableSize(size_in_kb: number) {
Expand All @@ -51,11 +27,14 @@ function padIfNeeded(a: string, b: string) {
}

export function logComparison(props: {
original_size: number;
optimized_size: number;
original_css: Uint8Array | Buffer | string;
optimized_css: string;
id: string;
}) {
const { original_size, optimized_size, id } = props;
const { original_css, optimized_css, id } = props;

const original_size = stringSizeInKB(original_css.toString());
const optimized_size = stringSizeInKB(optimized_css);
const original = toHumanReadableSize(original_size);
const optimized = toHumanReadableSize(optimized_size);
const original_display = padIfNeeded(original, optimized);
Expand All @@ -82,20 +61,46 @@ export function getComponentClasses(id: string) {
return [];
}

export function getCssAllowlist() {
// `.bx--body` needs to be explicitly included,
// or the class will be inadvertently removed.
return [".bx--body"].slice();
}
export type OptimizeCssOptions = {
/**
* By default, the plugin will log the size diff
* between the original and optimized CSS.
*
* Set to `false` to disable verbose logging.
* @default true
*/
verbose?: boolean;

/**
* By default, pre-compiled Carbon StyleSheets ship `@font-face`
* rules for all available IBM Plex fonts, many of which are
* not actually used in Carbon Svelte components.
*
* The recommended optimization is to only preserve IBM Plex
* fonts with 400/600-weight and normal-font-style rules.
*
* Set to `true` to disable this behavior.
* @default false
*/
preserveAllIBMFonts?: boolean;
};

type PostcssOptimizeCarbonOptions = OptimizeCssOptions & {
css_allowlist: ReturnType<typeof getCssAllowlist>;
ids: string[];
};

export function postcssOptimizeCarbon(
options: PostcssOptimizeCarbonOptions,
): Plugin {
const { preserveAllIBMFonts, css_allowlist } = options;
const { preserveAllIBMFonts, ids } = options;

// `.bx--body` needs to be explicitly included,
// or the class will be inadvertently removed.
const css_allowlist = [".bx--body"];

for (const id of ids) {
css_allowlist.push(...getComponentClasses(id));
}

return {
postcssPlugin: "postcss-plugin:carbon:optimize-css",
Expand All @@ -105,11 +110,11 @@ export function postcssOptimizeCarbon(
// Ensure that the selector contains a class.
if (selector.includes(".")) {
// Selectors may contain multiple classes, separated by a comma.
const classes = selector.split(",").filter((c) => {
const v = c.trim() ?? "";
const classes = selector.split(",").filter((selectee) => {
const value = selectee.trim() ?? "";
// Some Carbon classes may be prefixed with a tag for higher specificity.
// E.g., a.bx--header
const [, rest] = v.split(".");
const [, rest] = value.split(".");
return Boolean(rest);
});

Expand Down

0 comments on commit a168c82

Please sign in to comment.