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

feat(core): allow to export SCSS files

This commit is contained in:
Roberto Redradix 2023-09-07 15:15:30 +02:00
parent c035375084
commit f33dc7a9b8
7 changed files with 99 additions and 3 deletions

View file

@ -3,7 +3,7 @@ import { parseUserConfig } from './userConfig'
// Types
export interface AssetConfig {
output: string
format: 'css' | 'json'
format: 'css' | 'scss' | 'json'
}
export interface ColorsConfig extends AssetConfig {}

View file

@ -33,7 +33,11 @@ const outputPathSchema = z.string({
required_error: '.output is required',
invalid_type_error: '.output must be a string',
})
const outputFormatSchema = z.union([z.literal('css'), z.literal('json')])
const outputFormatSchema = z.union([
z.literal('css'),
z.literal('scss'),
z.literal('json'),
])
const assetConfigSchema = z.object({
output: outputPathSchema,

View file

@ -11,7 +11,7 @@ import {
normalizePenpotExportUserConfig,
AssetConfig,
} from './config'
import { writeJsonFile, writeCssFile } from './outputters'
import { writeJsonFile, writeCssFile, writeScssFile } from './outputters'
import { CSSClassDefinition, CSSCustomPropertyDefinition } from './types'
const processOutput = ({
@ -26,6 +26,9 @@ const processOutput = ({
if (outputFormat === 'css') {
return writeCssFile(outputPath, content)
}
if (outputFormat === 'scss') {
return writeScssFile(outputPath, content)
}
if (outputFormat === 'json') {
return writeJsonFile(outputPath, content)
}

View file

@ -1,2 +1,3 @@
export * from './css'
export * from './scss'
export * from './json'

View file

@ -0,0 +1,68 @@
import fs from 'node:fs'
import path from 'node:path'
import {
CSSClassDefinition,
CSSCustomPropertyDefinition,
isCssClassDefinition,
} from '../../types'
import { camelToKebab } from '../css/syntax'
import { textToScssVariableName } from './syntax'
const areCssCustomPropertiesDefinitions = (
objects: Array<object>,
): objects is Array<CSSCustomPropertyDefinition> => {
return !objects.every(isCssClassDefinition)
}
/**
* From: https://sass-lang.com/documentation/values/maps/
* Most of the time, its a good idea to use quoted strings rather than unquoted strings for map keys. This is because
* some values, such as color names, may look like unquoted strings but actually be other types. To avoid confusing
* problems down the line, just use quotes!
*/
const serializeScssMap = (cssCustomProperty: CSSClassDefinition) => {
const mapName = textToScssVariableName(cssCustomProperty.name)
const mapPairs = Object.entries(cssCustomProperty.cssProps).map(
([key, value]) => ` "${camelToKebab(key)}": ${value},`,
)
return [`${mapName}: (`, ...mapPairs, `);`].join('\n')
}
const serializeScssVariable = (
cssCustomProperty: CSSCustomPropertyDefinition,
): string => {
const { name, value } = cssCustomProperty
const property = textToScssVariableName(name)
return `${property}: ${value};`
}
const serializeScss = (
cssDefinitions: CSSClassDefinition[] | CSSCustomPropertyDefinition[],
): string => {
if (areCssCustomPropertiesDefinitions(cssDefinitions)) {
const cssDeclarations = cssDefinitions.map((customPropertyDefinition) =>
serializeScssVariable(customPropertyDefinition),
)
return cssDeclarations.join('\n')
} else {
return cssDefinitions.map(serializeScssMap).join('\n\n')
}
}
export function writeScssFile(
outputPath: string,
cssDefinitions: CSSClassDefinition[] | CSSCustomPropertyDefinition[],
) {
const css = serializeScss(cssDefinitions)
const dirname = path.dirname(outputPath)
if (!fs.existsSync(dirname)) {
fs.mkdirSync(dirname, { recursive: true })
}
fs.writeFileSync(outputPath, css, 'utf-8')
}

View file

@ -0,0 +1,12 @@
import { textToCssIdentToken } from '../css/syntax'
/**
* From: https://sass-lang.com/documentation/variables/
* Sass variables, like all Sass identifiers, treat hyphens and underscores as identical. This means that $font-size and
* $font_size both refer to the same variable.
*/
export const textToScssVariableName = (str: string) => {
// NOTE We can't avoid name clashing in this case, but at least let's make it explicit for a reader.
const sassIdent = textToCssIdentToken(str.replace(/_/g, '-'))
return '$' + sassIdent
}

View file

@ -32,6 +32,10 @@ const config = {
{
output: 'src/styles/colors.css', // 👈 Path where your CSS file should be generated.
},
{
output: 'src/styles/colors.scss', // 👈 Path where your SCSS file should be generated.
format: 'scss',
},
{
output: 'src/styles/colors.json', // 👈 Path where your JSON file should be generated.
format: 'json',
@ -41,6 +45,10 @@ const config = {
{
output: 'src/styles/typographies.css', // 👈 Path where your CSS file should be generated.
},
{
output: 'src/styles/typographies.scss', // 👈 Path where your SCSS file should be generated.
format: 'scss',
},
{
output: 'src/styles/typographies.json', // 👈 Path where your JSON file should be generated.
format: 'json',