Skip to content

Commit

Permalink
add hash
Browse files Browse the repository at this point in the history
  • Loading branch information
naoki-tomita committed Oct 4, 2023
1 parent 87b5672 commit b634925
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 19 deletions.
28 changes: 28 additions & 0 deletions __tests__/__snapshots__/index.spec.ts.snap
@@ -1,5 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`zstyl renderTemplate should render template 1`] = `
"
@media (max-width: 600px) {
display: inline-flex;
&:hover {
color: black;
}
}
display: flex;
justify-content: center;
color: green;
&:hover {
color: red;
}
&:active {
color: blue;
}
.hoge {
width: 100%;
}
"
`;

exports[`zstyl toStyleString should create zheleznaya styled component 1`] = `
"*[data-zstyl='id'] { display: flex; justify-content: center; color: green; }
@media (max-width: 600px) {
Expand Down
18 changes: 18 additions & 0 deletions __tests__/hash.spec.ts
@@ -0,0 +1,18 @@
import { hashString } from "../hash";

describe("hash", () => {
it("should calc hash", () => {
const text = `
*[data-zstyl='id'] { display: flex; justify-content: center; color: green; }
@media (max-width: 600px) {
*[data-zstyl='id'] { display: inline-flex; }
*[data-zstyl='id']:hover { color: black; }
}
*[data-zstyl='id']:hover { color: red; }
*[data-zstyl='id']:active { color: blue; }
*[data-zstyl='id'] .hoge { width: 100%; }
`.trim();
const actual = hashString(text);
expect(actual).toBe("bJIaaS");
});
});
39 changes: 35 additions & 4 deletions __tests__/index.spec.ts
@@ -1,9 +1,9 @@
import { random, styled, toStyleString } from "../index";
import { random, styled, toStyleString, renderTemplate } from "../index";

describe("zstyl", () => {
describe("toStyleString", () => {
it("should create zheleznaya styled component", () => {
const styleString = toStyleString("id", { color: "green" })`
describe("renderTemplate", () => {
it("should render template", () => {
const renderedText = renderTemplate({ color: "green" })`
@media (max-width: 600px) {
display: inline-flex;
Expand All @@ -28,6 +28,37 @@ describe("zstyl", () => {
width: 100%;
}
`;
expect(renderedText).toMatchSnapshot();
});
});

describe("toStyleString", () => {
it("should create zheleznaya styled component", () => {
const styleString = toStyleString("id", `
@media (max-width: 600px) {
display: inline-flex;
&:hover {
color: black;
}
}
display: flex;
justify-content: center;
color: green;
&:hover {
color: red;
}
&:active {
color: blue;
}
.hoge {
width: 100%;
}
`.trim());
expect(styleString).toMatchSnapshot();
});
});
Expand Down
32 changes: 32 additions & 0 deletions hash.ts
@@ -0,0 +1,32 @@
const AD_REPLACER_R = /(a)(d)/gi;

const charsLength = 52;
function getAlphabeticChar(code: number) {
return String.fromCharCode(code + (code > 25 ? 39 : 97));
}

function getAlphabeticString(code: number) {
let name = '';
let x;

for (x = Math.abs(code); x > charsLength; x = (x / charsLength) | 0) {
name = getAlphabeticChar(x % charsLength) + name;
}

return (getAlphabeticChar(x % charsLength) + name).replace(AD_REPLACER_R, "$1-$2");
}

// djb2 algorithm
function phash(h: number, text: string) {
let i = text.length;
while(i) {
h = (h * 33) ^ text.charCodeAt(--i);
}
return h;
}

const SEED = 5381;

export function hashString(text: string) {
return getAlphabeticString(phash(SEED, text));
}
38 changes: 23 additions & 15 deletions index.ts
@@ -1,6 +1,7 @@
import { h, Component, render } from "zheleznaya";
import { h, Component } from "zheleznaya";
import { AstRenderer } from "./AstRenderer";
import { StyleSheetParser } from "./BNFStyledParser";
import { hashString } from "./hash";

const Chars = "abcdefghijklmnopqrstuvwxyz0123456789";
export function random(size: number = 5) {
Expand Down Expand Up @@ -32,44 +33,51 @@ function toClassSelector(className: string) {
return `.${className}`;
}

export function toStyleString<T>(id: string, props: T) {
return (template: TemplateStringsArray, ...values: Array<((props: T) => (string | number)) | string | number>) => {
const renderedStyle = template.map((it, i) => `${it}${expand(props, values[i])}`).join("");
const { ast } = StyleSheetParser.parse(renderedStyle);
return AstRenderer.renderStyleSheetWithId(toSelector(id), ast!);
}
export function toStyleString<T>(id: string, renderedStyle: string) {
const { ast } = StyleSheetParser.parse(renderedStyle);
return AstRenderer.renderStyleSheetWithId(toSelector(id), ast!);
}

function isServerSide(): boolean {
return typeof window === "undefined";
}

export function renderTemplate<T, U extends keyof TagAndHTMLType>(props: T & Partial<TagAndHTMLType[U]>, ) {
return (template: TemplateStringsArray, ...values: Array<((props: T & Partial<TagAndHTMLType[U]>) => (string | number)) | string | number>) => {
return template.map((it, i) => `${it}${expand(props, values[i])}`).join("");
}
}

function updateStyleEl() {
styleEl && (styleEl.innerHTML = Object.values(styles).map((v) => (v)).join("\n"));
}

function generateInnerFunction<U extends typeof tags[number]>(tag: U) {
return function innerFunction<T>(template: TemplateStringsArray, ...values: Array<((props: T & Partial<TagAndHTMLType[U]>) => (string | number)) | string | number>): Component<T & Partial<TagAndHTMLType[U]>> {
if (!isServerSide() && !styleEl) init();

const id = random();
return (props, children) => {
styles[id] = toStyleString(id, props)(template, ...values);
styleEl && (styleEl.innerHTML = Object.values(styles).map((v) => (v)).join("\n"));
const styleText = renderTemplate(props)(template, ...values);
const id = hashString(styleText);
styles[id] = toStyleString(id, styleText);
updateStyleEl();

return h(tag, { ...props, "data-zstyl": id }, ...children) as any;
}
}
}

const renderedStyleWithId = new Map<string, string>();
export function css<U extends typeof tags[number], T>(template: TemplateStringsArray, ...values: Array<((props: T & Partial<TagAndHTMLType[U]>) => (string | number)) | string | number>) {
if (!isServerSide() && !styleEl) init();

const renderedStyle = template.map((it, i) => `${it}${expand({} as any, values[i])}`).join("");
const id = renderedStyleWithId.get(renderedStyle) ?? random();
renderedStyleWithId.set(renderedStyle, id);
const id = hashString(renderedStyle);
const className = `c-zstyl-${id}`;

const { ast } = StyleSheetParser.parse(renderedStyle);
styles[className] = AstRenderer.renderStyleSheetWithId(toClassSelector(className), ast!);
styleEl && (styleEl.innerHTML = Object.values(styles).map((v) => (v)).join("\n"));
styles[id] = AstRenderer.renderStyleSheetWithId(toClassSelector(className), ast!);
updateStyleEl();

return className;
}

Expand Down

0 comments on commit b634925

Please sign in to comment.