Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(editor): upgrading twind to support new tailwind 3 syntax #6024

Merged
merged 4 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@
"@stitches/react": "1.2.8",
"@svgr/plugin-jsx": "5.5.0",
"@tippyjs/react": "4.1.0",
"@twind/core": "1.1.3",
"@twind/preset-autoprefix": "1.0.7",
"@twind/preset-tailwind": "1.1.4",
"@types/fontfaceobserver": "0.0.6",
"@types/lodash.findlastindex": "4.6.7",
"@types/react-syntax-highlighter": "11.0.4",
Expand Down Expand Up @@ -271,7 +274,6 @@
"string-hash": "1.1.3",
"strip-ansi": "6.0.0",
"tippy.js": "6.2.6",
"twind": "0.16.16",
"typescript": "5.2.2",
"typescript-for-the-editor": "npm:[email protected]",
"url-join": "4.0.1",
Expand Down
73 changes: 54 additions & 19 deletions editor/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 50 additions & 24 deletions editor/src/core/tailwind/tailwind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import { isRight, left, right } from '../shared/either'
import type { RequireFn } from '../shared/npm-dependency-types'
import type { ProjectFile } from '../shared/project-file-types'
import { isTextFile } from '../shared/project-file-types'
import type { Configuration, Sheet } from 'twind'
import { silent } from 'twind'
import type { TwindObserver } from 'twind/observe'
import { create, observe, cssomSheet } from 'twind/observe'
import type { Sheet, Twind } from '@twind/core'
import { cssom, observe, defineConfig, tw } from '@twind/core'
import presetAutoprefix from '@twind/preset-autoprefix'
import presetTailwind from '@twind/preset-tailwind'
import React from 'react'
import { includesDependency } from '../../components/editor/npm-dependency/npm-dependency'
import { propOrNull } from '../shared/object-utils'
import { memoize } from '../shared/memoize'
import { importDefault } from '../es-modules/commonjs-interop'
import { PostCSSPath, TailwindConfigPath } from './tailwind-config'
import { useKeepReferenceEqualityIfPossible } from '../../utils/react-performance'
import { twind } from '@twind/core'

type TwindConfigType = ReturnType<typeof defineConfig>

function hasRequiredDependenciesForTailwind(packageJsonFile: ProjectFile): boolean {
const hasTailwindDependency = includesDependency(packageJsonFile, 'tailwindcss')
Expand Down Expand Up @@ -86,19 +89,30 @@ function enablesPreflight(tailwindConfig: any): boolean {
return true
}

function convertTailwindToTwindConfig(tailwindConfig: any): Configuration {
function convertTailwindToTwindConfig(tailwindConfig: any): TwindConfigType {
const preflightEnabled = enablesPreflight(tailwindConfig)

return {
...tailwindConfig,
preflight: preflightEnabled,
}
const twindConfig = defineConfig({
presets: [
presetTailwind({
disablePreflight: !preflightEnabled,
}),
presetAutoprefix(),
],
// force pushing tailwind's config to twind
theme: tailwindConfig.theme,
darkMode: tailwindConfig.darkMode,
variants: tailwindConfig.variants,
preflight: tailwindConfig.preflight,
})

return twindConfig
}

function getTailwindConfig(
tailwindFile: ProjectFile | null,
requireFn: RequireFn,
): Either<any, Configuration> {
): Either<any, TwindConfigType> {
if (tailwindFile != null && isTextFile(tailwindFile)) {
try {
const requireResult = requireFn('/', TailwindConfigPath)
Expand Down Expand Up @@ -127,22 +141,22 @@ function useGetTailwindConfigFile(projectContents: ProjectContentTreeRoot): Proj
function useGetTailwindConfig(
projectContents: ProjectContentTreeRoot,
requireFn: RequireFn,
): Configuration {
): TwindConfigType {
const tailwindConfigFile = useGetTailwindConfigFile(projectContents)
const tailwindConfig = React.useMemo(() => {
const maybeConfig = getTailwindConfig(tailwindConfigFile, requireFn)
if (isRight(maybeConfig)) {
return maybeConfig.value
} else {
return {}
return defineConfig({})
}
}, [tailwindConfigFile, requireFn])
return useKeepReferenceEqualityIfPossible(tailwindConfig)
}

interface TwindInstance {
element: HTMLStyleElement
observer: TwindObserver
instance: Twind
}

let twindInstance: TwindInstance | null = null
Expand All @@ -153,7 +167,8 @@ export function isTwindEnabled(): boolean {

function clearTwind() {
if (twindInstance != null) {
twindInstance.observer.disconnect()
twindInstance.instance.clear()
twindInstance.instance.destroy()
twindInstance.element.parentNode?.removeChild(twindInstance.element)
}
}
Expand Down Expand Up @@ -197,29 +212,40 @@ const adjustRuleScope = memoize(adjustRuleScopeImpl, {
matchesArg: (a, b) => a === b,
})

function updateTwind(config: Configuration, prefixSelector: string | null) {
function updateTwind(config: TwindConfigType, prefixSelector: string | null) {
const element = document.head.appendChild(document.createElement('style'))
element.appendChild(document.createTextNode('')) // Avoid Edge bug where empty style elements doesn't create sheets
element.setAttribute('id', `twind-styles-${Math.random().toString(36).slice(2)}`)

const sheet = cssomSheet({ target: element.sheet ?? undefined })
const sheet = cssom(element)
const customSheet: Sheet = {
...sheet,
target: sheet.target,
insert: (rule, index) => {
insert: (rule, index, sheetRule) => {
const scopedRule = adjustRuleScope(rule, prefixSelector)
sheet.insert(scopedRule, index)
sheet.insert(scopedRule, index, sheetRule)
},
}

clearTwind()

const observer = observe(
document.documentElement,
create({ ...config, sheet: customSheet, mode: silent }),
)
if (twindInstance == null) {
const prefixes = ['TWIND_', 'TAILWIND_']
window.addEventListener('warning', (event: any | { detail: { code: string } }) => {
const isTwindWarning: boolean = prefixes.some((prefix) =>
event?.detail?.code?.startsWith?.(prefix),
)
if (isTwindWarning) {
event.preventDefault()
}
})
}

const instance = observe(twind(config, customSheet), document.documentElement)

twindInstance = {
element: element,
observer: observer,
instance: instance,
}
}

Expand Down Expand Up @@ -255,7 +281,7 @@ export function injectTwind(
const shouldUseTwind = hasDependencies && hasPostCSSPlugin
const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath)
const maybeTailwindConfig = getTailwindConfig(tailwindConfigFile, requireFn)
const tailwindConfig = isRight(maybeTailwindConfig) ? maybeTailwindConfig.value : {}
const tailwindConfig = isRight(maybeTailwindConfig) ? maybeTailwindConfig.value : defineConfig({})
if (shouldUseTwind) {
updateTwind(tailwindConfig, prefixSelector)
} else {
Expand Down
Loading