♻️ remove csso dependency

do a custom css optimization instead
This commit is contained in:
platane
2022-11-03 08:25:35 +01:00
committed by Platane
parent f83b9ab0c3
commit b40f17a02e
10 changed files with 654 additions and 611 deletions

View File

@@ -1,7 +1,6 @@
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { generateContributionSnake } from "./generateContributionSnake";
import { parseOutputsOption } from "./outputsOptions"; import { parseOutputsOption } from "./outputsOptions";
(async () => { (async () => {
@@ -14,6 +13,9 @@ import { parseOutputsOption } from "./outputsOptions";
] ]
); );
const { generateContributionSnake } = await import(
"./generateContributionSnake"
);
const results = await generateContributionSnake(userName, outputs); const results = await generateContributionSnake(userName, outputs);
outputs.forEach((out, i) => { outputs.forEach((out, i) => {

View File

@@ -0,0 +1,26 @@
import { minifyCss } from "../css-utils";
it("should minify css", () => {
expect(
minifyCss(`
.c {
color : red ;
}
`)
).toBe(".c{color:red}");
expect(
minifyCss(`
.c {
top : 0;
color : red ;
}
# {
animation: linear 10;
}
`)
).toBe(".c{top:0;color:red}#{animation:linear 10}");
});

View File

@@ -0,0 +1,38 @@
const percent = (x: number) =>
parseFloat((x * 100).toFixed(2)).toString() + "%";
const mergeKeyFrames = (keyframes: { t: number; style: string }[]) => {
const s = new Map<string, number[]>();
for (const { t, style } of keyframes) {
s.set(style, [...(s.get(style) ?? []), t]);
}
return Array.from(s.entries())
.map(([style, ts]) => ({ style, ts }))
.sort((a, b) => a.ts[0] - b.ts[0]);
};
/**
* generate the keyframe animation from a list of keyframe
*/
export const createAnimation = (
name: string,
keyframes: { t: number; style: string }[]
) =>
`@keyframes ${name}{` +
mergeKeyFrames(keyframes)
.map(({ style, ts }) => ts.map(percent).join(",") + `{${style}}`)
.join("") +
"}";
/**
* remove white spaces
*/
export const minifyCss = (css: string) =>
css
.replace(/\s+/g, " ")
.replace(/.\s+[,;:{}()]/g, (a) => a.replace(/\s+/g, ""))
.replace(/[,;:{}()]\s+./g, (a) => a.replace(/\s+/g, ""))
.replace(/.\s+[,;:{}()]/g, (a) => a.replace(/\s+/g, ""))
.replace(/[,;:{}()]\s+./g, (a) => a.replace(/\s+/g, ""))
.replace(/\;\s*\}/g, "}")
.trim();

View File

@@ -1,6 +1,7 @@
import type { Color, Empty } from "@snk/types/grid"; import type { Color, Empty } from "@snk/types/grid";
import type { Point } from "@snk/types/point"; import type { Point } from "@snk/types/point";
import { h } from "./utils"; import { createAnimation } from "./css-utils";
import { h } from "./xml-utils";
export type Options = { export type Options = {
colorDots: Record<Color, string>; colorDots: Record<Color, string>;
@@ -11,8 +12,6 @@ export type Options = {
sizeDotBorderRadius: number; sizeDotBorderRadius: number;
}; };
const percent = (x: number) => (x * 100).toFixed(2);
export const createGrid = ( export const createGrid = (
cells: (Point & { t: number | null; color: Color | Empty })[], cells: (Point & { t: number | null; color: Color | Empty })[],
{ sizeDotBorderRadius, sizeDot, sizeCell }: Options, { sizeDotBorderRadius, sizeDot, sizeCell }: Options,
@@ -26,38 +25,40 @@ export const createGrid = (
stroke-width: 1px; stroke-width: 1px;
stroke: var(--cb); stroke: var(--cb);
animation: none ${duration}ms linear infinite; animation: none ${duration}ms linear infinite;
width: ${sizeDot}px;
height: ${sizeDot}px;
}`, }`,
]; ];
let i = 0; let i = 0;
for (const { x, y, color, t } of cells) { for (const { x, y, color, t } of cells) {
const id = t && "c" + (i++).toString(36); const id = t && "c" + (i++).toString(36);
const s = sizeCell; const m = (sizeCell - sizeDot) / 2;
const d = sizeDot;
const m = (s - d) / 2;
if (t !== null) { if (t !== null && id) {
const animationName = id; const animationName = id;
styles.push( styles.push(
`@keyframes ${animationName} {` + createAnimation(animationName, [
`${percent(t - 0.0001)}%{fill:var(--c${color})}` + { t: t - 0.0001, style: `fill:var(--c${color})` },
`${percent(t + 0.0001)}%,100%{fill:var(--ce)}` + { t: t + 0.0001, style: `fill:var(--ce)` },
"}", { t: 1, style: `fill:var(--ce)` },
]),
`.c.${id}{fill: var(--c${color}); animation-name: ${animationName}}` `.c.${id}{
fill: var(--c${color});
animation-name: ${animationName}
}`
); );
} }
svgElements.push( svgElements.push(
h("rect", { h("rect", {
class: ["c", id].filter(Boolean).join(" "), class: ["c", id].filter(Boolean).join(" "),
x: x * s + m, x: x * sizeCell + m,
y: y * s + m, y: y * sizeCell + m,
rx: sizeDotBorderRadius, rx: sizeDotBorderRadius,
ry: sizeDotBorderRadius, ry: sizeDotBorderRadius,
width: d,
height: d,
}) })
); );
} }

View File

@@ -9,12 +9,12 @@ import { getHeadX, getHeadY } from "@snk/types/snake";
import type { Snake } from "@snk/types/snake"; import type { Snake } from "@snk/types/snake";
import type { Grid, Color, Empty } from "@snk/types/grid"; import type { Grid, Color, Empty } from "@snk/types/grid";
import type { Point } from "@snk/types/point"; import type { Point } from "@snk/types/point";
import type { AnimationOptions } from "@snk/gif-creator";
import { createSnake } from "./snake"; import { createSnake } from "./snake";
import { createGrid } from "./grid"; import { createGrid } from "./grid";
import { createStack } from "./stack"; import { createStack } from "./stack";
import { h } from "./utils"; import { h } from "./xml-utils";
import * as csso from "csso"; import { minifyCss } from "./css-utils";
import { AnimationOptions } from "@snk/gif-creator";
export type DrawOptions = { export type DrawOptions = {
colorDots: Record<Color, string>; colorDots: Record<Color, string>;
@@ -132,7 +132,7 @@ export const createSvg = (
return optimizeSvg(svg); return optimizeSvg(svg);
}; };
const optimizeCss = (css: string) => csso.minify(css).css; const optimizeCss = (css: string) => minifyCss(css);
const optimizeSvg = (svg: string) => svg; const optimizeSvg = (svg: string) => svg;
const generateColorVar = (drawOptions: DrawOptions) => const generateColorVar = (drawOptions: DrawOptions) =>

View File

@@ -2,10 +2,7 @@
"name": "@snk/svg-creator", "name": "@snk/svg-creator",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@snk/solver": "1.0.0", "@snk/solver": "1.0.0"
"csso": "5.0.3"
}, },
"devDependencies": { "devDependencies": {}
"@types/csso": "5.0.0"
}
} }

View File

@@ -1,7 +1,8 @@
import { getSnakeLength, snakeToCells } from "@snk/types/snake"; import { getSnakeLength, snakeToCells } from "@snk/types/snake";
import type { Snake } from "@snk/types/snake"; import type { Snake } from "@snk/types/snake";
import type { Point } from "@snk/types/point"; import type { Point } from "@snk/types/point";
import { h } from "./utils"; import { h } from "./xml-utils";
import { createAnimation } from "./css-utils";
export type Options = { export type Options = {
colorSnake: string; colorSnake: string;
@@ -9,8 +10,6 @@ export type Options = {
sizeDot: number; sizeDot: number;
}; };
const percent = (x: number) => (x * 100).toFixed(2);
const lerp = (k: number, a: number, b: number) => (1 - k) * a + k * b; const lerp = (k: number, a: number, b: number) => (1 - k) * a + k * b;
export const createSnake = ( export const createSnake = (
@@ -55,8 +54,8 @@ export const createSnake = (
const styles = [ const styles = [
`.s{ `.s{
shape-rendering:geometricPrecision; shape-rendering: geometricPrecision;
fill:var(--cs); fill: var(--cs);
animation: none linear ${duration}ms infinite animation: none linear ${duration}ms infinite
}`, }`,
@@ -64,16 +63,17 @@ export const createSnake = (
const id = `s${i}`; const id = `s${i}`;
const animationName = id; const animationName = id;
return [ const keyframes = removeInterpolatedPositions(
`@keyframes ${animationName} {` + positions.map((tr, i, { length }) => ({ ...tr, t: i / length }))
removeInterpolatedPositions( ).map(({ t, ...p }) => ({ t, style: transform(p) }));
positions.map((tr, i, { length }) => ({ ...tr, t: i / length }))
)
.map((p) => `${percent(p.t)}%{${transform(p)}}`)
.join("") +
"}",
`.s.${id}{${transform(positions[0])};animation-name: ${animationName}}`, return [
createAnimation(animationName, keyframes),
`.s.${id}{
${transform(positions[0])};
animation-name: ${animationName}
}`,
]; ];
}), }),
].flat(); ].flat();

View File

@@ -1,12 +1,11 @@
import type { Color, Empty } from "@snk/types/grid"; import type { Color, Empty } from "@snk/types/grid";
import { h } from "./utils"; import { createAnimation } from "./css-utils";
import { h } from "./xml-utils";
export type Options = { export type Options = {
sizeDot: number; sizeDot: number;
}; };
const percent = (x: number) => (x * 100).toFixed(2);
export const createStack = ( export const createStack = (
cells: { t: number | null; color: Color | Empty }[], cells: { t: number | null; color: Color | Empty }[],
{ sizeDot }: Options, { sizeDot }: Options,
@@ -56,23 +55,28 @@ export const createStack = (
); );
styles.push( styles.push(
`@keyframes ${animationName} {` + createAnimation(
animationName,
[ [
...ts.map((t, i, { length }) => [ ...ts
{ scale: i / length, t: t - 0.0001 }, .map((t, i, { length }) => [
{ scale: (i + 1) / length, t: t + 0.0001 }, { scale: i / length, t: t - 0.0001 },
]), { scale: (i + 1) / length, t: t + 0.0001 },
[{ scale: 1, t: 1 }], ])
] .flat(),
.flat() { scale: 1, t: 1 },
.map( ].map(({ scale, t }) => ({
({ scale, t }) => t,
`${percent(t)}%{transform:scale(${scale.toFixed(2)},1)}` style: `transform:scale(${scale.toFixed(3)},1)`,
) }))
.join("\n") + ),
"}",
`.u.${id}{fill:var(--c${color});animation-name:${animationName};transform-origin:${x}px 0}` `.u.${id} {
fill: var(--c${color});
animation-name: ${animationName};
transform-origin: ${x}px 0
}
`
); );
} }

1081
yarn.lock

File diff suppressed because it is too large Load Diff