-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat(exporters): Introduce variables-scss exporter #DS-1412
- Loading branch information
Showing
19 changed files
with
694 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Generated files used by Supernova | ||
generated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
module.exports = { | ||
extends: [ | ||
'../../.eslintrc', | ||
'plugin:@typescript-eslint/recommended', | ||
'prettier', | ||
'plugin:prettier/recommended', | ||
'@lmc-eu/eslint-config-jest', | ||
], | ||
|
||
parser: '@typescript-eslint/parser', // the TypeScript parser we installed earlier | ||
|
||
parserOptions: { | ||
ecmaVersion: 'latest', | ||
project: './tsconfig.eslint.json', | ||
}, | ||
|
||
settings: { | ||
'import/resolver': { | ||
node: { | ||
extensions: ['.js', '.ts'], | ||
}, | ||
}, | ||
}, | ||
|
||
plugins: ['promise', '@typescript-eslint', 'prettier'], | ||
rules: { | ||
// disable for `scripts` and `config` | ||
'@typescript-eslint/no-var-requires': 'off', | ||
// allow ++ in for loops | ||
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], | ||
// disabled due to typescript | ||
'no-shadow': 'off', | ||
'@typescript-eslint/no-shadow': ['error', { allow: ['resolve', 'reject', 'done', 'next', 'error'] }], | ||
// disabled due to typescript | ||
'no-use-before-define': 'off', | ||
'@typescript-eslint/no-use-before-define': 'warn', | ||
// We are using typescript, disable jsdoc rules | ||
'jsdoc/require-jsdoc': 'off', | ||
'jsdoc/require-returns': 'off', | ||
'jsdoc/require-param-type': 'off', | ||
// allow reassign in properties | ||
'no-param-reassign': ['warn', { props: false }], | ||
// support monorepos | ||
'import/no-extraneous-dependencies': ['error', { packageDir: ['./', '../../'] }], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.build | ||
.coverage |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Exporter Spirit Variables SCSS | ||
|
||
[Supernova][supernova-studio] SCSS exporter made for Spirit Design System developed by [Alma Career (formerly LMC)][alma-career]. | ||
|
||
## Token operations | ||
|
||
This exported does several operations with tokens: | ||
|
||
- All token groups except [Typography](#typography) are processed using a simple generate function. | ||
- The first step is sorting. Measures are sorted by number in the token name, Generic Tokens (Other) by value, and the rest by its name. | ||
- Next, each token is grouped and its value is prepared to print. Grouping is made using actual groups in Supernova and if these are not present, a common name prefix is used. Separate token values are printed as separate SCSS variables. | ||
- Groups are used for printing SCSS maps with references to separate tokens and pluralized names. Borders groups are skipped and colors have their group name suffixed. These maps are printed into SCSS. | ||
- Shadows are grouped if same name. | ||
- If Gradient names start with `gradients/gradient`, they are not used from Figma, but from Supernova | ||
|
||
### Typography | ||
|
||
As typography in Figma and Supernova are stored in named text style groups, these groups are used to generate SCSS maps with all the values from Supernova. They are grouped by breakpoints. | ||
⚠️ We do not generate `link` typography tokens (styles that include `-link` in their name). | ||
|
||
#### Ebony Font Weight Exception | ||
|
||
Font Family Ebony has a different font weight mapping in Figma and in Adobe Fonts. To match these we set its own font weight numeric-name conversion. | ||
|
||
### Sorting | ||
|
||
Tokens are sorted alphabetically by origin (Figma) name or by name (Supernova). Except Measures - sorted by name number and Other - sorted by value. | ||
|
||
## Outputs: | ||
|
||
- \_borders.scss | ||
- \_colors.scss | ||
- \_gradients.scss | ||
- \_measures.scss | ||
- \_other.scss | ||
- \_radii.scss | ||
- \_shadows.scss | ||
- \_typography.scss | ||
- index.scss | ||
|
||
The index file contains SCSS forwards of all other outputs. | ||
|
||
[supernova-studio]: https://github.com/Supernova-Studio | ||
[alma-career]: https://github.com/lmc-eu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[ | ||
{ | ||
"key": "generateDisclaimer", | ||
"type": "boolean", | ||
"default": false, | ||
"title": "Show Generated File Disclaimer", | ||
"description": "When enabled, a disclaimer showing the fact that the file was generated automatically and should not be changed manually will appear in all style styles" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"generateDisclaimer": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* Main configuration of the exporter - type interface. | ||
* Default values for it can be set through `config.json` | ||
* Users can override the behavior when creating the pipelines or by creating `config.local.json` file specifying actual values. | ||
*/ | ||
export type ExporterConfiguration = { | ||
/** When enabled, generate a disclaimer showing the fact that the file was generated automatically and should not be changed manually will appear in all files */ | ||
generateDisclaimer: boolean; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"id": "almacareer.exporter-spirit-variables-scss", | ||
"name": "Spirit Varibles SCSS Exporter", | ||
"description": "Spirit SCSS Exporter", | ||
"author": "Spirit Team", | ||
"organization": "Alma Career", | ||
"version": "1.0.0", | ||
"usesBrands": true, | ||
"usesThemes": true, | ||
"executable": "/generated/exporter.js", | ||
"engine": "latest", | ||
"tags": ["spirit", "scss", "variables", "exporter"] | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const config = { | ||
// The root directory that Jest should scan for tests and modules within. | ||
// https://jestjs.io/docs/configuration#rootdir-string | ||
rootDir: './', | ||
|
||
// This option tells Jest that all imported modules in your tests should be mocked automatically. | ||
// https://jestjs.io/docs/configuration#automock-boolean | ||
automock: false, | ||
|
||
// Indicates whether each individual test should be reported during the run. | ||
// https://jestjs.io/docs/configuration#verbose-boolean | ||
verbose: false, | ||
|
||
// A map from regular expressions to paths to transformers | ||
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object | ||
transform: { | ||
'^.+\\.(t|j)s?$': ['<rootDir>/../../node_modules/@swc/jest'], | ||
}, | ||
|
||
// The test environment that will be used for testing. | ||
// https://jestjs.io/docs/configuration#testenvironment-string | ||
// testEnvironment: 'jsdom', | ||
|
||
// An array of regexp pattern strings that are matched against all test paths before executing the test | ||
// https://jestjs.io/docs/configuration#testpathignorepatterns-arraystring | ||
testPathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/node_modules/', '.*__tests__/.*DataProvider.ts'], | ||
|
||
// The directory where Jest should output its coverage files. | ||
// https://jestjs.io/docs/configuration#coveragedirectory-string | ||
coverageDirectory: './.coverage', | ||
|
||
// An array of glob patterns indicating a set of files for which coverage information should be collected. | ||
// https://jestjs.io/docs/configuration#collectcoveragefrom-array | ||
collectCoverageFrom: ['<rootDir>/src/**/*.{js,ts}', '!<rootDir>/src/**/*.d.ts'], | ||
|
||
// An array of regexp pattern strings that are matched against all file paths before executing the test. | ||
// https://jestjs.io/docs/configuration#coveragepathignorepatterns-arraystring | ||
coveragePathIgnorePatterns: ['__fixtures__'], | ||
|
||
// A list of reporter names that Jest uses when writing coverage reports. Any istanbul reporter can be used. | ||
// https://jestjs.io/docs/configuration#coveragereporters-arraystring--string-options | ||
coverageReporters: ['text', 'text-summary', ['lcov', { projectRoot: '../../' }]], | ||
|
||
// A list of paths to modules that run some code to configure or set up the testing framework before each test. | ||
// https://jestjs.io/docs/configuration#setupfilesafterenv-array | ||
// setupFilesAfterEnv: ['@testing-library/jest-dom'], | ||
}; | ||
|
||
module.exports = config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "@almacareer/spirit-exporters-variables-scss", | ||
"version": "1.0.0", | ||
"description": "Spirit SCSS Exporter for Supernova", | ||
"author": "Spirit Team", | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "vite watch", | ||
"build": "vite build", | ||
"lint": "eslint ./", | ||
"lint:fix": "yarn lint --fix", | ||
"test": "npm-run-all lint test:unit:coverage types", | ||
"test:unit": "jest || true", | ||
"test:unit:watch": "yarn test:unit --watchAll", | ||
"test:unit:coverage": "yarn test:unit --coverage", | ||
"types": "true" | ||
}, | ||
"dependencies": { | ||
"@supernovaio/export-helpers": "^1.0.9", | ||
"@supernovaio/sdk-exporters": "^2.0.17" | ||
}, | ||
"devDependencies": { | ||
"@swc/core": "1.6.5", | ||
"@swc/jest": "0.2.36", | ||
"eslint": "8.57.0", | ||
"jest": "29.7.0", | ||
"npm-run-all": "4.1.5", | ||
"typescript": "5.5.2", | ||
"vite": "5.3.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { ColorFormat, CSSHelper, NamingHelper, StringCase } from '@supernovaio/export-helpers'; | ||
import { ColorToken, Token, TokenGroup } from '@supernovaio/sdk-exporters'; | ||
|
||
export function colorTokenToCSS( | ||
token: ColorToken, | ||
mappedTokens: Map<string, Token>, | ||
tokenGroups: Array<TokenGroup>, | ||
): string { | ||
// First creating the name of the token, using helper function which turns any token name / path into a valid variable name | ||
const name = tokenVariableName(token, tokenGroups); | ||
|
||
// Then creating the value of the token, using another helper function | ||
const value = CSSHelper.colorTokenValueToCSS(token.value, mappedTokens, { | ||
allowReferences: true, | ||
decimals: 3, | ||
colorFormat: ColorFormat.smartHashHex, | ||
tokenToVariableRef: (t) => { | ||
return `var(--${tokenVariableName(t, tokenGroups)})`; | ||
}, | ||
}); | ||
|
||
return `$${name}: ${value};`; | ||
} | ||
|
||
function tokenVariableName(token: Token, tokenGroups: Array<TokenGroup>): string { | ||
const parent = tokenGroups.find((group) => group.id === token.parentGroupId)!; | ||
|
||
return NamingHelper.codeSafeVariableNameForToken(token, StringCase.paramCase, parent, ``); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Token } from '@supernovaio/sdk-exporters'; | ||
|
||
export const generateSimple = (allTokens: Token[]) => { | ||
const sortedTokens = allTokens.sort((a, b) => a.name.localeCompare(b.name)); | ||
|
||
return sortedTokens.map((token) => token); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { FileHelper } from '@supernovaio/export-helpers'; | ||
import { | ||
AnyOutputFile, | ||
ColorToken, | ||
PulsarContext, | ||
RemoteVersionIdentifier, | ||
Supernova, | ||
TokenType, | ||
} from '@supernovaio/sdk-exporters'; | ||
import { ExporterConfiguration } from '../config'; | ||
import { colorTokenToCSS } from './content/token'; | ||
import { generateSimple } from './generators/simple'; | ||
|
||
/** | ||
* Export entrypoint. | ||
* When running `export` through extensions or pipelines, this function will be called. | ||
* Context contains information about the design system and version that is currently being exported. | ||
*/ | ||
Pulsar.export(async (sdk: Supernova, context: PulsarContext): Promise<Array<AnyOutputFile>> => { | ||
// Fetch data from design system that is currently being exported (context) | ||
const remoteVersionIdentifier: RemoteVersionIdentifier = { | ||
designSystemId: context.dsId, | ||
versionId: context.versionId, | ||
}; | ||
|
||
// Fetch the necessary data | ||
let tokens = await sdk.tokens.getTokens(remoteVersionIdentifier); | ||
let tokenGroups = await sdk.tokens.getTokenGroups(remoteVersionIdentifier); | ||
|
||
// Filter by brand, if specified by the VSCode extension or pipeline configuration | ||
if (context.brandId) { | ||
tokens = tokens.filter((token) => token.brandId === context.brandId); | ||
tokenGroups = tokenGroups.filter((tokenGroup) => tokenGroup.brandId === context.brandId); | ||
} | ||
|
||
// Apply theme, if specified by the VSCode extension or pipeline configuration | ||
if (context.themeId) { | ||
const themes = await sdk.tokens.getTokenThemes(remoteVersionIdentifier); | ||
const currentTheme = themes.find((theme) => theme.id === context.themeId); | ||
if (currentTheme) { | ||
tokens = await sdk.tokens.computeTokensByApplyingThemes(tokens, [currentTheme]); | ||
} else { | ||
// Don't allow applying theme which doesn't exist in the system | ||
throw new Error("Unable to apply theme which doesn't exist in the system."); | ||
} | ||
} | ||
|
||
// Convert all color tokens to CSS variables | ||
const simpleTokens = generateSimple(tokens); | ||
const mappedTokens = new Map(simpleTokens.map((token) => [token.id, token])); | ||
|
||
const cssVariables = tokens | ||
.filter((t) => t.tokenType === TokenType.color) | ||
.map((token) => colorTokenToCSS(token as ColorToken, mappedTokens, tokenGroups)) | ||
.join('\n'); | ||
|
||
// Create CSS file content | ||
let content = cssVariables; | ||
if (exportConfiguration.generateDisclaimer) { | ||
// Add disclaimer to every file if enabled | ||
content = `/* This file was generated by Supernova, don't change by hand */\n${content}`; | ||
} | ||
|
||
// Create output file and return it | ||
return [ | ||
FileHelper.createTextFile({ | ||
relativePath: './', | ||
fileName: '_colors.scss', | ||
content, | ||
}), | ||
FileHelper.createTextFile({ | ||
relativePath: './original-data/', | ||
fileName: '_colors.json', | ||
content: JSON.stringify(tokens, null, 2), | ||
}), | ||
]; | ||
}); | ||
|
||
/** Exporter configuration. Adheres to the `ExporterConfiguration` interface and its content comes from the resolved default configuration + user overrides of various configuration keys */ | ||
export const exportConfiguration = Pulsar.exportConfig<ExporterConfiguration>(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"include": ["./", "./.eslintrc.js"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"compileOnSave": true, | ||
"compilerOptions": { | ||
"baseUrl": ".", | ||
"outDir": "./dist", | ||
"sourceMap": true, | ||
"declaration": true, | ||
"moduleResolution": "node", | ||
"emitDecoratorMetadata": true, | ||
"experimentalDecorators": true, | ||
"module": "es2015", | ||
"target": "es2015", | ||
"noEmit": true, | ||
"strictNullChecks": true, | ||
"strictPropertyInitialization": true, | ||
"resolveJsonModule": true, | ||
"noImplicitReturns": true, | ||
"allowUnreachableCode": false, | ||
"allowSyntheticDefaultImports": true, | ||
"alwaysStrict": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"noUnusedParameters": false, | ||
"noUnusedLocals": true, | ||
"strictFunctionTypes": true, | ||
"noImplicitAny": true, | ||
"esModuleInterop": true, | ||
"typeRoots": ["../../node_modules/@types"], | ||
"lib": ["es2015", "dom", "dom.iterable"], | ||
"types": ["node", "jest", "@testing-library/jest-dom"] | ||
}, | ||
"include": ["./src/**/*"], | ||
"exclude": ["./node_modules", "./dist/**/*"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { resolve } from 'path'; | ||
import { defineConfig } from 'vite'; | ||
|
||
export default defineConfig({ | ||
build: { | ||
lib: { | ||
entry: resolve(__dirname, 'src/index.ts'), | ||
name: 'Exporter', | ||
fileName: 'exporter', | ||
formats: ['cjs'], | ||
}, | ||
rollupOptions: { | ||
output: { | ||
dir: resolve(__dirname, './generated'), | ||
banner: | ||
'/**\n * THIS FILE IS GENERATED USING `build` SCRIPT\n * DO NOT EDIT MANUALLY\n * SEE CONTRIBUTING.md\n*/', | ||
}, | ||
}, | ||
}, | ||
}); |
Oops, something went wrong.