Skip to content

Commit

Permalink
✨ Added support for nested CSS in css template tag literal (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Weustenfeld committed May 27, 2022
1 parent f4081e1 commit 66906ec
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ export default Demo() {
};
```

## Acknoledgements
## Acknowledgements

TBD
3 changes: 2 additions & 1 deletion src/css/css.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { CSS, LinariaClassName } from "../types";
import unnest from "./unnest";

const clean = (s) => s.replace(/(\n\s*)/g, "");
const cleanWhitespace = (s) => s.replace(/\s+/g, " ");
Expand All @@ -18,7 +19,7 @@ const css: CSS = function (strings, ...rest) {
// Temporary hack to support css in Next.js
if (typeof window !== "undefined") {
const style = document.createElement("style");
style.innerHTML = `.${id} { ${body} }`;
style.innerHTML = unnest(`.${id}`, body);
document.getElementsByTagName("head")[0].appendChild(style);
}

Expand Down
73 changes: 73 additions & 0 deletions src/css/unnest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Based on: https://codesandbox.io/s/4w0070kpv4?file=/src/index.js:0-5821

/* character codes */
let SEMICOLON = 59; /* ; */
let CLOSEBRACES = 125; /* } */
let OPENBRACES = 123; /* { */
let NEWLINE = 10; /* \n */
let CARRIAGE = 13; /* \r */
let TAB = 9; /* \t */
let AT = 64; /* @ */
let SPACE = 32; /* */
let AND = 38; /* & */

export default function unnest(SELECTOR_PLACEHOLDER, str) {
let i = 0,
out = "",
char = 0,
context = "",
activeSelector = SELECTOR_PLACEHOLDER;

str = activeSelector + "{" + str; // + "}"; // leave this off on purpose so we don't have to remove it

while (i < str.length) {
char = str.charCodeAt(i);
switch (char) {
// replace `&` with the active selector
case AND:
context += context + activeSelector;
break;

// I can't explain this in writing :-/ Anyone?
case SEMICOLON:
out += context + str[i];
context = "";
break;

// whatever is in the context is a selector at this point
case OPENBRACES:
out +=
(out.length ? "}" : "") +
(activeSelector = context.trim()) + // having trouble explaining why this trim works.
str[i];
context = "";
break;

// hit wall and write what we have in context
// we dump our net that we've been dragging behind us
case CLOSEBRACES:
out += context + str[i];
context = "";
break;

case AT:
out += context;
context = str[i];
break;

// minify
case NEWLINE:
case CARRIAGE:
case TAB:
case SPACE:
// ensure we have nothing important
if (context.length === 0) break;
// eslint-disable-no-fallthrough
default:
context += str[i];
break;
}
++i;
}
return out;
}

0 comments on commit 66906ec

Please sign in to comment.