Skip to content

Commit

Permalink
add theme import
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-rogerson committed Jul 21, 2020
1 parent 7c0659f commit debfab3
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 2 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ ml-8 [2rem] / ml-10 [2.5rem] / ml-12 [3rem] / ml-16 [4rem] / ml-20 [5rem] / ml-2
ml-40 [10rem] / ml-48 [12rem] / ml-56 [14rem] / ml-64 [16rem] / ml-auto [auto] / ml-px [1px]
```

**🖌️ Use the theme import to add values from your tailwind config**

```js
import { theme, css } from 'twin.macro'

const Input = () => <input css={css({ color: theme`colors.purple.500` })} />
```

**💥 Go important with a bang** - Add important to any class with a trailing bang!

```js
Expand Down
39 changes: 39 additions & 0 deletions src/logging.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,43 @@ const errorSuggestions = properties => {
return spaced(`${textNotFound}\n\n${suggestionText}`)
}

const themeErrorNotString = ({ themeValue, input }) => {
const textNotFound = warning(
`${color.errorLight(input)} didn’t bring back a string theme value`
)
const suggestionText = `Try adding one of these values after a dot:\n${formatSuggestions(
Object.entries(themeValue).map(([k, v]) => ({
target: k,
value: typeof v === 'string' ? v : '...',
}))
)}`

return spaced(`${textNotFound}\n\n${suggestionText}`)
}

const themeErrorNotFound = ({ theme, input, trimInput }) => {
if (typeof theme === 'string') {
return spaced(logBadGood(input, trimInput))
}

const textNotFound = warning(
`${color.errorLight(input)} was not found in your theme`
)

if (!theme) {
return spaced(textNotFound)
}

const suggestionText = `Try one of these values:\n${formatSuggestions(
Object.entries(theme).map(([k, v]) => ({
target: k,
value: typeof v === 'string' ? v : '...',
}))
)}`

return spaced(`${textNotFound}\n\n${suggestionText}`)
}

export {
logNoVariant,
logNoClass,
Expand All @@ -152,4 +189,6 @@ export {
debugPlugins,
inOutPlugins,
errorSuggestions,
themeErrorNotString,
themeErrorNotFound,
}
4 changes: 4 additions & 0 deletions src/macro.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
updateStyledReferences,
addStyledImport,
} from './macro/styled'
import { handleThemeFunction } from './macro/theme'
import { handleTwProperty, handleTwFunction } from './macro/tw'
import getUserPluginData from './utils/getUserPluginData'
import { debugPlugins } from './logging'
Expand Down Expand Up @@ -104,6 +105,9 @@ const twinMacro = ({ babel: { types: t }, references, state, config }) => {
addStyledImport({ program, t, styledImport, state })
}

// Theme import
handleThemeFunction({ references, t, state })

// Auto add css prop for styled components
if (
state.hasTwProp &&
Expand Down
83 changes: 83 additions & 0 deletions src/macro/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import dlv from 'dlv'
import { replaceWithLocation, astify } from './../macroHelpers'
import { getTheme, assert } from './../utils'
import {
logGeneralError,
themeErrorNotString,
themeErrorNotFound,
} from './../logging'

const getFunctionValue = path => {
if (path.parent.type !== 'CallExpression') return

const parent = path.findParent(x => x.isCallExpression())
if (!parent) return

const argument = parent.get('arguments')[0] || ''
return { parent, input: argument.evaluate().value }
}

const getTaggedTemplateValue = path => {
if (path.parent.type !== 'TaggedTemplateExpression') return

const parent = path.findParent(x => x.isTaggedTemplateExpression())
if (!parent) return
if (parent.node.tag.type !== 'Identifier') return

return { parent, input: parent.get('quasi').evaluate().value }
}

const normalizeThemeValue = foundValue =>
Array.isArray(foundValue)
? foundValue.join(', ')
: typeof foundValue === 'string'
? foundValue.trim()
: foundValue

const trimInput = themeValue => {
const arrayValues = themeValue.split('.').filter(Boolean)
if (arrayValues.length === 1) {
return arrayValues[0]
}

return arrayValues.slice(0, -1).join('.')
}

const handleThemeFunction = ({ references, t, state }) => {
if (!references.theme) return

const theme = getTheme(state.config.theme)

references.theme.forEach(path => {
const { input, parent } =
getTaggedTemplateValue(path) || getFunctionValue(path)

if (input === '') {
return replaceWithLocation(parent, astify('', t))
}

assert(!parent || !input, () =>
logGeneralError(
"The theme value doesn’t look right\n\nTry using it like this: theme`colors.black` or theme('colors.black')"
)
)

const themeValue = dlv(theme(), input)
assert(!themeValue, () =>
themeErrorNotFound({
theme: input.includes('.') ? dlv(theme(), trimInput(input)) : theme(),
input,
trimInput: trimInput(input),
})
)

const normalizedValue = normalizeThemeValue(themeValue)
assert(typeof normalizedValue !== 'string', () =>
themeErrorNotString({ themeValue, input })
)

return replaceWithLocation(parent, astify(normalizedValue, t))
})
}

export { handleThemeFunction }
11 changes: 9 additions & 2 deletions src/macroHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,14 @@ function replaceWithLocation(path, replacement) {
return newPaths
}

const validImports = new Set(['default', 'styled', 'css', 'TwStyle'])
const validImports = new Set([
'default',
'styled',
'css',
'theme',
'TwStyle',
'ThemeStyle',
])
const validateImports = imports => {
const unsupportedImport = Object.keys(imports).find(
reference => !validImports.has(reference)
Expand All @@ -206,7 +213,7 @@ const validateImports = imports => {
})
assert(unsupportedImport, () =>
logGeneralError(
`Twin doesn't recognize { ${unsupportedImport} }\n\nTry one of these imports:\nimport tw, { styled, css } from 'twin.macro'`
`Twin doesn't recognize { ${unsupportedImport} }\n\nTry one of these imports:\nimport tw, { styled, css, theme } from 'twin.macro'`
)
)
}
Expand Down
15 changes: 15 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,25 @@ export interface TwStyle {
[key: string]: string | number | TwStyle
}

export interface ThemeStyle {
[key: string]: string | number | ThemeStyle
}

export type TemplateFn<R> = (
strings: Readonly<TemplateStringsArray>,
...values: readonly string[]
) => R

export type TwFn = TemplateFn<TwStyle>

export type ThemeSearchFn<R> = (...values: readonly string[]) => R
export type ThemeSearchTaggedFn<R> = (
strings: Readonly<TemplateStringsArray>
) => R

export type ThemeFn = ThemeSearchFn<ThemeStyle> &
ThemeSearchTaggedFn<ThemeStyle>

export type TwComponent<K extends keyof JSX.IntrinsicElements> = (
props: JSX.IntrinsicElements[K]
) => JSX.Element
Expand All @@ -36,3 +48,6 @@ declare global {
}
}
}

declare const theme: ThemeFn
export { theme }

0 comments on commit debfab3

Please sign in to comment.