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:
parent
c035375084
commit
f33dc7a9b8
7 changed files with 99 additions and 3 deletions
|
@ -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 {}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './css'
|
||||
export * from './scss'
|
||||
export * from './json'
|
||||
|
|
68
packages/core/src/lib/outputters/scss/index.ts
Normal file
68
packages/core/src/lib/outputters/scss/index.ts
Normal 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, it’s 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')
|
||||
}
|
12
packages/core/src/lib/outputters/scss/syntax.ts
Normal file
12
packages/core/src/lib/outputters/scss/syntax.ts
Normal 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
|
||||
}
|
|
@ -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',
|
||||
|
|
Loading…
Add table
Reference in a new issue