mirror of
https://github.com/penpot/penpot-export.git
synced 2025-03-12 07:21:20 -05:00
feat: allow exporting typographies CSS
This commit is contained in:
parent
028e29f4c0
commit
e19ea35b84
6 changed files with 127 additions and 7 deletions
|
@ -5,6 +5,12 @@ require('dotenv').config()
|
|||
*/
|
||||
const config = {
|
||||
accessToken: process.env.PENPOT_ACCESS_TOKEN,
|
||||
typographies: [
|
||||
{
|
||||
output: 'src/styles/typographies.css', // 👈🏻 Path where your css should be generated.
|
||||
fileId: '4a499800-872e-80e1-8002-fc0b585dc061'
|
||||
},
|
||||
],
|
||||
pages: [
|
||||
{
|
||||
output: 'src/styles/ui.css', // 👈🏻 Path where your css should be generated.
|
||||
|
|
|
@ -12,6 +12,9 @@ import type {
|
|||
FetcherOptions,
|
||||
PenpotGetPageOptions,
|
||||
PenpotApiErrorResponse,
|
||||
PenpotTypography,
|
||||
PenpotGetFileOptions,
|
||||
PenpotFile,
|
||||
} from './types'
|
||||
|
||||
class PenpotApiError extends Error {
|
||||
|
@ -111,4 +114,35 @@ export class Penpot {
|
|||
|
||||
return pickObjectProps(objectCssProps, textCssProps)
|
||||
}
|
||||
|
||||
async getFileTypographies(
|
||||
options: PenpotGetFileOptions,
|
||||
): Promise<{ fileName: string; typographies: PenpotTypography[] }> {
|
||||
const file = await this.fetcher<PenpotFile>({
|
||||
command: 'get-file',
|
||||
body: {
|
||||
id: options.fileId,
|
||||
},
|
||||
})
|
||||
const typographies = Object.values(file.data.typographies)
|
||||
|
||||
return { fileName: file.name, typographies }
|
||||
}
|
||||
|
||||
static getTypographyAssetCssProps(typography: PenpotTypography) {
|
||||
const textCssProps = [
|
||||
'lineHeight',
|
||||
'fontStyle',
|
||||
'textTransform',
|
||||
'fontWeight',
|
||||
'direction',
|
||||
]
|
||||
|
||||
return {
|
||||
...pickObjectProps(typography, textCssProps),
|
||||
fontSize: `${typography.fontSize}px`,
|
||||
letterSpacing: `${typography.letterSpacing}px`,
|
||||
fontFamily: `"${typography.fontFamily}"`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ export interface PenpotGetPageOptions {
|
|||
pageId: string
|
||||
}
|
||||
|
||||
export interface PenpotGetFileOptions {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface PenpotObject {
|
||||
id: string
|
||||
name: string
|
||||
|
@ -21,11 +25,42 @@ export interface PenpotObject {
|
|||
shapes?: string[]
|
||||
}
|
||||
|
||||
type CssTextProperty =
|
||||
| 'lineHeight'
|
||||
| 'fontStyle'
|
||||
| 'textTransform'
|
||||
| 'fontSize'
|
||||
| 'fontWeight'
|
||||
| 'letterSpacing'
|
||||
| 'fontFamily'
|
||||
|
||||
export type PenpotTypography = {
|
||||
id: string
|
||||
name: string
|
||||
modifiedAt: string
|
||||
path: string
|
||||
fontId: string
|
||||
fontVariantId: string
|
||||
} & Record<CssTextProperty, string>
|
||||
|
||||
export interface PenpotPage {
|
||||
name: string
|
||||
objects: Record<string, PenpotObject>
|
||||
}
|
||||
|
||||
export interface PenpotFile {
|
||||
id: string
|
||||
name: string
|
||||
revn: number
|
||||
modifiedAt: string
|
||||
data: {
|
||||
id: string
|
||||
version: number
|
||||
typographies: PenpotTypography[]
|
||||
pages: PenpotPage[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface PenpotApiErrorResponse {
|
||||
type: string
|
||||
code: string
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Config, PagesConfig } from './types'
|
||||
|
||||
const BASE_CONFIG: Omit<Config, 'pages'> = {
|
||||
const BASE_CONFIG: Omit<Config, 'pages' | 'typographies'> = {
|
||||
accessToken: '',
|
||||
}
|
||||
|
||||
|
@ -43,25 +43,42 @@ export function validateAndNormalizePenpotExportConfig(config: Config): Config {
|
|||
...BASE_CONFIG,
|
||||
...config,
|
||||
pages: [],
|
||||
typographies: [],
|
||||
}
|
||||
|
||||
for (const [index, typographiesConfig] of config.typographies.entries()) {
|
||||
const { output, fileId } = typographiesConfig
|
||||
|
||||
for (const [index, page] of config.pages.entries()) {
|
||||
if (!page.output) {
|
||||
if (!output) {
|
||||
throw new MissingOutputPathError(index)
|
||||
}
|
||||
|
||||
if (!page.fileId) {
|
||||
if (!fileId) {
|
||||
throw new MissingFileIdError(index)
|
||||
}
|
||||
|
||||
if (!page.pageId) {
|
||||
normalizedConfig.typographies.push({
|
||||
output,
|
||||
fileId,
|
||||
})
|
||||
}
|
||||
|
||||
for (const [index, pageConfig] of config.pages.entries()) {
|
||||
if (!pageConfig.output) {
|
||||
throw new MissingOutputPathError(index)
|
||||
}
|
||||
|
||||
if (!pageConfig.fileId) {
|
||||
throw new MissingFileIdError(index)
|
||||
}
|
||||
|
||||
if (!pageConfig.pageId) {
|
||||
throw new MissingPageIdError(index)
|
||||
}
|
||||
|
||||
normalizedConfig.pages.push({
|
||||
...PAGES_CONFIG,
|
||||
...page,
|
||||
...pageConfig,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,28 @@ export async function generateCssFromConfig(
|
|||
const validatedConfig = validateAndNormalizePenpotExportConfig(config)
|
||||
const penpot = new Penpot({ accessToken: config.accessToken })
|
||||
|
||||
for (const typographiesConfig of validatedConfig.typographies) {
|
||||
const cssClassDefinitions: CSSClassDefinition[] = []
|
||||
|
||||
const { fileName, typographies } = await penpot.getFileTypographies({
|
||||
fileId: typographiesConfig.fileId,
|
||||
})
|
||||
const fileClassname = textToValidClassname(fileName)
|
||||
|
||||
for (const typography of typographies) {
|
||||
const cssProps = Penpot.getTypographyAssetCssProps(typography)
|
||||
const objectClassname = textToValidClassname(typography.name)
|
||||
const className = `${fileClassname}--${objectClassname}`
|
||||
cssClassDefinitions.push({ className, cssProps })
|
||||
}
|
||||
|
||||
const cssPath = path.resolve(rootProjectPath, typographiesConfig.output)
|
||||
|
||||
writeCssFile(cssPath, cssClassDefinitions)
|
||||
|
||||
console.log('✅ Typographies: %s', typographiesConfig.output)
|
||||
}
|
||||
|
||||
for (const page of validatedConfig.pages) {
|
||||
const cssClassDefinitions: CSSClassDefinition[] = []
|
||||
|
||||
|
@ -36,6 +58,6 @@ export async function generateCssFromConfig(
|
|||
|
||||
writeCssFile(cssPath, cssClassDefinitions)
|
||||
|
||||
console.log('✅ %s', page.output)
|
||||
console.log('✅ Page components: %s', page.output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,14 @@ export interface PagesConfig {
|
|||
pageId: string
|
||||
}
|
||||
|
||||
export interface TypographiesConfig {
|
||||
output: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
accessToken: string
|
||||
typographies: TypographiesConfig[]
|
||||
pages: PagesConfig[]
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue