Skip to content

Commit

Permalink
Convert @emotion/cache's source code to TypeScript (#3277)
Browse files Browse the repository at this point in the history
* Convert `@emotion/cache`'s source code to TypeScript

* remove extra comment types

* tweak pkg json

* fixed pkg.json#imports

* try with this

* use .ts

* try this

* try this

* one more

* try this

* update `@types/stylis`
  • Loading branch information
Andarist authored Dec 3, 2024
1 parent 282b61d commit 8dc1a6d
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 210 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-garlics-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/cache': minor
---

Source code has been migrated to TypeScript. From now on type declarations will be emitted based on that, instead of being hand-written.
20 changes: 10 additions & 10 deletions packages/cache/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "emotion's cache",
"main": "dist/emotion-cache.cjs.js",
"module": "dist/emotion-cache.esm.js",
"types": "dist/emotion-cache.cjs.d.ts",
"exports": {
".": {
"types": {
Expand Down Expand Up @@ -63,18 +64,17 @@
},
"imports": {
"#is-development": {
"development": "./src/conditions/true.js",
"default": "./src/conditions/false.js"
"development": "./src/conditions/true.ts",
"default": "./src/conditions/false.ts"
},
"#is-browser": {
"edge-light": "./src/conditions/false.js",
"workerd": "./src/conditions/false.js",
"worker": "./src/conditions/false.js",
"browser": "./src/conditions/true.js",
"default": "./src/conditions/is-browser.js"
"edge-light": "./src/conditions/false.ts",
"workerd": "./src/conditions/false.ts",
"worker": "./src/conditions/false.ts",
"browser": "./src/conditions/true.ts",
"default": "./src/conditions/is-browser.ts"
}
},
"types": "types/index.d.ts",
"license": "MIT",
"repository": "https://github.com/emotion-js/emotion/tree/main/packages/cache",
"scripts": {
Expand All @@ -90,11 +90,11 @@
"devDependencies": {
"@definitelytyped/dtslint": "0.0.112",
"@emotion/hash": "*",
"@types/stylis": "^4.2.7",
"typescript": "^5.4.5"
},
"files": [
"src",
"dist",
"types/*.d.ts"
"dist"
]
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 0 additions & 2 deletions packages/cache/src/index.d.ts

This file was deleted.

94 changes: 39 additions & 55 deletions packages/cache/src/index.js → packages/cache/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StyleSheet } from '@emotion/sheet'
/* import { type EmotionCache, type SerializedStyles } from '@emotion/utils' */
import type { EmotionCache, SerializedStyles } from '@emotion/utils'
import {
serialize,
compile,
Expand All @@ -8,6 +8,7 @@ import {
stringify,
COMMENT
} from 'stylis'
import type { Element as StylisElement } from 'stylis'
import weakMemoize from '@emotion/weak-memoize'
import memoize from '@emotion/memoize'
import isDevelopment from '#is-development'
Expand All @@ -19,43 +20,37 @@ import {
incorrectImportAlarm
} from './stylis-plugins'
import { prefixer } from './prefixer'
/* import type { StylisPlugin } from './types' */
import { StylisPlugin } from './types'

/*
export type Options = {
nonce?: string,
stylisPlugins?: StylisPlugin[],
key: string,
container?: HTMLElement,
speedy?: boolean,
prepend?: boolean,
export interface Options {
nonce?: string
stylisPlugins?: Array<StylisPlugin>
key: string
container?: Node
speedy?: boolean
/** @deprecate use `insertionPoint` instead */
prepend?: boolean
insertionPoint?: HTMLElement
}
*/

let getServerStylisCache = isBrowser
? undefined
: weakMemoize(() =>
memoize(() => {
let cache = {}
return name => cache[name]
})
)
: weakMemoize(() => memoize<Record<string, string>>(() => ({})))

const defaultStylisPlugins = [prefixer]

let getSourceMap
let getSourceMap: ((styles: string) => string | undefined) | undefined
if (isDevelopment) {
let sourceMapPattern =
/\/\*#\ssourceMappingURL=data:application\/json;\S+\s+\*\//g
getSourceMap = (styles /*: string */) => {
getSourceMap = styles => {
let matches = styles.match(sourceMapPattern)
if (!matches) return
return matches[matches.length - 1]
}
}

let createCache = (options /*: Options */) /*: EmotionCache */ => {
let createCache = (options: Options): EmotionCache => {
let key = options.key

if (isDevelopment && !key) {
Expand All @@ -74,14 +69,14 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
// document.head is a safe place to move them to(though note document.head is not necessarily the last place they will be)
// note this very very intentionally targets all style elements regardless of the key to ensure
// that creating a cache works inside of render of a React component
Array.prototype.forEach.call(ssrStyles, (node /*: HTMLStyleElement */) => {
Array.prototype.forEach.call(ssrStyles, (node: HTMLStyleElement) => {
// we want to only move elements which have a space in the data-emotion attribute value
// because that indicates that it is an Emotion 11 server-side rendered style elements
// while we will already ignore Emotion 11 client-side inserted styles because of the :not([data-s]) part in the selector
// Emotion 10 client-side inserted styles did not have data-s (but importantly did not have a space in their data-emotion attributes)
// so checking for the space ensures that loading Emotion 11 after Emotion 10 has inserted some styles
// will not result in the Emotion 10 styles being destroyed
const dataEmotionAttribute = node.getAttribute('data-emotion')
const dataEmotionAttribute = node.getAttribute('data-emotion')!
if (dataEmotionAttribute.indexOf(' ') === -1) {
return
}
Expand All @@ -100,18 +95,18 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
)
}
}
let inserted = {}
let container /* : Node */
const nodesToHydrate = []
let inserted: EmotionCache['inserted'] = {}
let container: Node
const nodesToHydrate: HTMLStyleElement[] = []
if (isBrowser) {
container = options.container || document.head

Array.prototype.forEach.call(
// this means we will ignore elements which don't have a space in them which
// means that the style elements we're looking at are only Emotion 11 server-rendered style elements
document.querySelectorAll(`style[data-emotion^="${key} "]`),
(node /*: HTMLStyleElement */) => {
const attrib = node.getAttribute(`data-emotion`).split(' ')
(node: HTMLStyleElement) => {
const attrib = node.getAttribute(`data-emotion`)!.split(' ')
for (let i = 1; i < attrib.length; i++) {
inserted[attrib[i]] = true
}
Expand All @@ -120,12 +115,12 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
)
}

let insert /*: (
let insert: (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
) => string | void */
) => string | void
const omnipresentPlugins = [compat, removeLabel]

if (isDevelopment) {
Expand All @@ -139,13 +134,13 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
)
}

if (isBrowser) {
let currentSheet
if (!getServerStylisCache) {
let currentSheet: Pick<StyleSheet, 'insert'>

const finalizingPlugins = [
stringify,
isDevelopment
? element => {
? (element: StylisElement) => {
if (!element.root) {
if (element.return) {
currentSheet.insert(element.return)
Expand All @@ -164,21 +159,16 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
const serializer = middleware(
omnipresentPlugins.concat(stylisPlugins, finalizingPlugins)
)
const stylis = styles => serialize(compile(styles), serializer)
const stylis = (styles: string) => serialize(compile(styles), serializer)

insert = (
selector /*: string */,
serialized /*: SerializedStyles */,
sheet /*: StyleSheet */,
shouldCache /*: boolean */
) /*: void */ => {
insert = (selector, serialized, sheet, shouldCache) => {
currentSheet = sheet

if (isDevelopment) {
if (getSourceMap) {
let sourceMap = getSourceMap(serialized.styles)
if (sourceMap) {
currentSheet = {
insert: (rule /*: string */) => {
insert: rule => {
sheet.insert(rule + sourceMap)
}
}
Expand All @@ -196,13 +186,10 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
const serializer = middleware(
omnipresentPlugins.concat(stylisPlugins, finalizingPlugins)
)
const stylis = styles => serialize(compile(styles), serializer)
const stylis = (styles: string) => serialize(compile(styles), serializer)

let serverStylisCache = getServerStylisCache(stylisPlugins)(key)
let getRules = (
selector /*: string */,
serialized /*: SerializedStyles */
) /*: string */ => {
let getRules = (selector: string, serialized: SerializedStyles): string => {
let name = serialized.name
if (serverStylisCache[name] === undefined) {
serverStylisCache[name] = stylis(
Expand All @@ -211,12 +198,7 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
}
return serverStylisCache[name]
}
insert = (
selector /*: string */,
serialized /*: SerializedStyles */,
sheet /*: StyleSheet */,
shouldCache /*: boolean */
) /*: string | void */ => {
insert = (selector, serialized, sheet, shouldCache) => {
let name = serialized.name
let rules = getRules(selector, serialized)
if (cache.compat === undefined) {
Expand All @@ -226,7 +208,7 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
if (shouldCache) {
cache.inserted[name] = true
}
if (isDevelopment) {
if (getSourceMap) {
let sourceMap = getSourceMap(serialized.styles)
if (sourceMap) {
return rules + sourceMap
Expand All @@ -251,11 +233,11 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
}
}

const cache /*: EmotionCache */ = {
const cache: EmotionCache = {
key,
sheet: new StyleSheet({
key,
container,
container: container!,
nonce: options.nonce,
speedy: options.speedy,
prepend: options.prepend,
Expand All @@ -273,3 +255,5 @@ let createCache = (options /*: Options */) /*: EmotionCache */ => {
}

export default createCache
export type { EmotionCache }
export type { StylisElement, StylisPlugin, StylisPluginCallback } from './types'
15 changes: 11 additions & 4 deletions packages/cache/src/prefixer.js → packages/cache/src/prefixer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import {
RULESET,
serialize,
strlen,
WEBKIT
WEBKIT,
Element,
Middleware
} from 'stylis'

// this is a copy of stylis@4.0.13 prefixer, the latter version introduced grid prefixing which we don't want

function prefix(value, length) {
function prefix(value: string, length: number): string {
switch (hash(value, length)) {
// color-adjust
case 5103:
Expand Down Expand Up @@ -279,7 +281,12 @@ function prefix(value, length) {
return value
}

export let prefixer = (element, index, children, callback) => {
export let prefixer = (
element: Element,
index: number,
children: Element[],
callback: Middleware
) => {
if (element.length > -1)
if (!element.return)
switch (element.type) {
Expand All @@ -297,7 +304,7 @@ export let prefixer = (element, index, children, callback) => {
)
case RULESET:
if (element.length)
return combine(element.props, function (value) {
return combine(element.props as string[], function (value) {
switch (match(value, /(::plac\w+|:read-\w+)/)) {
// :read-(only|write)
case ':read-only':
Expand Down
Loading

0 comments on commit 8dc1a6d

Please sign in to comment.