summaryrefslogtreecommitdiff
path: root/@linaria/packages/babel/src/utils/toCSS.ts
blob: b39608715601dfc3b7d0332dc239fcd714796bee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { unitless } from '../units';
import type { JSONValue } from '../types';
import isSerializable from './isSerializable';
import isBoxedPrimitive from './isBoxedPrimitive';

const hyphenate = (s: string) => {
  if (s.startsWith('--')) {
    // It's a custom property which is already well formatted.
    return s;
  }
  return (
    s
      // Hyphenate CSS property names from camelCase version from JS string
      .replace(/([A-Z])/g, (match, p1) => `-${p1.toLowerCase()}`)
      // Special case for `-ms` because in JS it starts with `ms` unlike `Webkit`
      .replace(/^ms-/, '-ms-')
  );
};

// Some tools such as polished.js output JS objects
// To support them transparently, we convert JS objects to CSS strings
export default function toCSS(o: JSONValue): string {
  if (Array.isArray(o)) {
    return o.map(toCSS).join('\n');
  }

  if (isBoxedPrimitive(o)) {
    return o.valueOf().toString();
  }

  return Object.entries(o)
    .filter(
      ([, value]) =>
        // Ignore all falsy values except numbers
        typeof value === 'number' || value
    )
    .map(([key, value]) => {
      if (isSerializable(value)) {
        return `${key} { ${toCSS(value)} }`;
      }

      return `${hyphenate(key)}: ${
        typeof value === 'number' &&
        value !== 0 &&
        // Strip vendor prefixes when checking if the value is unitless
        !(
          key.replace(
            /^(Webkit|Moz|O|ms)([A-Z])(.+)$/,
            (match, p1, p2, p3) => `${p2.toLowerCase()}${p3}`
          ) in unitless
        )
          ? `${value}px`
          : value
      };`;
    })
    .join(' ');
}