0
Fork 0
mirror of https://github.com/penpot/penpot-export.git synced 2025-01-19 21:22:28 -05:00

refactor(core): delegate CSS tokenization and serialization to the outputter

This commit is contained in:
Roberto Redradix 2023-09-07 14:53:34 +02:00
parent b76d4d57ef
commit 9a39da7a65
9 changed files with 37 additions and 26 deletions

View file

@ -1,5 +1,3 @@
import { textToCssIdentToken } from '../../css/helpers'
import { PenpotApiFile } from '../../api'
import { CSSCustomPropertyDefinition } from '../../types'
@ -17,13 +15,13 @@ const toRgbaCssValue = (hex: string, alpha: number = 1) => {
export const adaptColorsToCssVariables = (
penpotFile: PenpotApiFile,
): CSSCustomPropertyDefinition[] => {
const fileName = penpotFile.name
const colors = Object.values(penpotFile.data.colors ?? {})
const cssPropsEntries = colors.map((color) => {
const objectClassName = textToCssIdentToken(color.name)
return {
name: objectClassName,
scope: fileName,
name: color.name,
value: toRgbaCssValue(color.color, color.opacity),
}
})

View file

@ -3,7 +3,6 @@ import {
isComponent,
pickObjectProps,
} from '../../api/helpers'
import { textToCssClassSelector } from '../../css/helpers'
import { PenpotApiFile, PenpotApiObject } from '../../api'
import { CSSClassDefinition } from '../../types'
@ -51,8 +50,11 @@ export const adaptPageComponentsToCssClassDefinitions = (
const object = component.objects[objectId]
if (object.type === 'text') {
const cssProps = getTextObjectCssProps(object)
const selector = textToCssClassSelector(`${page.name}--${object.name}`)
cssClassDefinitions.push({ selector, cssProps })
cssClassDefinitions.push({
scope: page.name,
name: object.name,
cssProps,
})
}
}
}

View file

@ -1,5 +1,3 @@
import { textToCssClassSelector } from '../../css/helpers'
import { PenpotApiTypography, CssTextProperty, PenpotApiFile } from '../../api'
import { CSSClassDefinition } from '../../types'
@ -25,9 +23,12 @@ export const adaptTypographiesToCssClassDefinitions = (
const cssClassDefinitions = typographies.map((typography) => {
const cssProps = getTypographyAssetCssProps(typography)
const selector = textToCssClassSelector(`${fileName}--${typography.name}`)
return { selector, cssProps }
return {
scope: fileName,
name: typography.name,
cssProps,
}
})
return cssClassDefinitions

View file

@ -1,6 +1,6 @@
import path from 'node:path'
import { adaptTypographiesToCssClassDefinitions } from './adapters/inbound/typographyToCssClasses'
import { adaptTypographiesToCssClassDefinitions } from './adapters/inbound/typographiesToCssClasses'
import { adaptColorsToCssVariables } from './adapters/inbound/colorsToCssVariables'
import { adaptPageComponentsToCssClassDefinitions } from './adapters/inbound/pageComponentsToCssClasses'

View file

@ -1,14 +1,17 @@
import fs from 'node:fs'
import path from 'node:path'
import { textToCssCustomProperyName } from '../css/helpers'
import {
camelToKebab,
textToCssClassSelector,
textToCssCustomPropertyName,
} from './syntax'
import { camelToKebab } from '../string'
import {
CSSClassDefinition,
CSSCustomPropertyDefinition,
isCssClassDefinition,
} from '../types'
} from '../../types'
const areCssCustomPropertiesDefinitions = (
objects: Array<object>,
@ -17,11 +20,14 @@ const areCssCustomPropertiesDefinitions = (
}
const serializeCssClass = (cssClassDefinition: CSSClassDefinition): string => {
const selector = textToCssClassSelector(
`${cssClassDefinition.scope}--${cssClassDefinition.name}`,
)
const cssValidProps = Object.keys(cssClassDefinition.cssProps).map(
(key) => ` ${camelToKebab(key)}: ${cssClassDefinition.cssProps[key]};`,
)
return [`${cssClassDefinition.selector} {`, ...cssValidProps, '}'].join('\n')
return [`${selector} {`, ...cssValidProps, '}'].join('\n')
}
const serializeCssCustomProperty = (
@ -29,9 +35,10 @@ const serializeCssCustomProperty = (
pad: number,
): string => {
const { name, value } = cssCustomProperty
const key = textToCssCustomPropertyName(name)
const padding = ' '.repeat(pad)
return `${padding}${textToCssCustomProperyName(name)}: ${value};`
return `${padding}${key}: ${value};`
}
const serializeCss = (

View file

@ -1,9 +1,9 @@
import assert from 'node:assert'
import { describe, it } from 'node:test'
import { textToCssIdentToken } from './helpers'
import { textToCssIdentToken } from './syntax'
describe('CSS helpers', () => {
describe('CSS syntax helpers', () => {
describe('Transforming text to CSS ident tokens', () => {
describe('when input text includes ASCII printable characters', () => {
it('escapes all ASCII printable characters except ident code points (-, 0-9, A-Z, _, a-z)', () => {

View file

@ -1,3 +1,7 @@
export function camelToKebab(str: string) {
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
}
/** From: https://www.w3.org/TR/css-syntax-3/#escaping
* Any Unicode code point can be included in an ident sequence or quoted string by escaping it. CSS escape sequences
* start with a backslash (\), and continue with:
@ -70,7 +74,7 @@ export function textToCssClassSelector(str: string) {
* case-sensitive (meaning theyre compared using the "identical to" operation), even in the ASCII range (e.g. example
* and EXAMPLE are two different, unrelated user-defined identifiers).
*/
export function textToCssCustomProperyName(str: string) {
export function textToCssCustomPropertyName(str: string) {
const unescapedDashedIdentifier = '--' + str.trimStart()
return textToCssIdentToken(unescapedDashedIdentifier)
}

View file

@ -1,3 +0,0 @@
export function camelToKebab(str: string) {
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
}

View file

@ -1,15 +1,17 @@
export interface CSSClassDefinition {
selector: string
scope: string
name: string
cssProps: Record<string, string>
}
export const isCssClassDefinition = (
object: object,
): object is CSSClassDefinition => {
return 'selector' in object
return 'cssProps' in object
}
export interface CSSCustomPropertyDefinition {
scope: string
name: string
value: string
}