Closed isimmons closed 1 year ago
Hello,
you'll need to build a custom format to achieve that.
Here is how I implemented it. There are some specific stuff in here like prettier, camelCase, that are not mandatory, but this should give you a sense of it.
import { camelCase } from 'change-case';
import { format } from 'prettier';
import StyleDictionary from 'style-dictionary';
import { isPlainObject } from '../utils/isPlainObject.js';
const {
formatHelpers: { fileHeader },
} = StyleDictionary;
export const nestedEs = {
name: 'nested/es',
formatter: ({ dictionary, file, platform }) => {
let { prefix } = platform;
let tokens = prefix ? { [prefix]: dictionary.tokens } : dictionary.tokens;
let ouput = [
fileHeader({ file }),
'export default ',
JSON.stringify(minifyDictionary(tokens), null, 2),
].join('');
return format(ouput, { parser: 'typescript', printWidth: 200 });
},
};
/**
* Output a nested object.
* Strip out everything except values.
*/
function minifyDictionary(node) {
if (!isPlainObject(node)) return node;
if (node.hasOwnProperty('value')) return node.value;
let nextNode = {};
for (let [key, value] of Object.entries(node)) {
nextNode[camelCase(key)] = minifyDictionary(value);
}
return nextNode;
}
Thanks @pascalduez
I just copied the function from style-dictionary for javascript/module and prefixed it with 'export default' instead of 'module.exports' and this made react, storybook, and TS all happy :-)
const StyleDictionary = require('style-dictionary');
const { fileHeader } = StyleDictionary.formatHelpers;
StyleDictionary.registerFormat({
name: 'myCustomFormat',
formatter: function ({ dictionary, file }) {
return (
fileHeader({ file }) +
'export default ' +
JSON.stringify(dictionary.tokens, null, 2) +
';\n'
);
},
});
But same as with the javascript/module format, there is a lot of un-needed info in there for my needs so I'll try out your example and see what I need to do to shrink this
"colors": {
"primary": {
"100": {
"value": "#EBEFFF",
"filePath": "src/tokens/colors.json",
"isSource": true,
"original": {
"value": "#EBEFFF"
},
"name": "ColorsPrimary100",
"attributes": {
"category": "colors",
"type": "primary",
"item": "100"
},
"path": [
"colors",
"primary",
"100"
]
},
down to this
"colors": {
"primary": {
"100": "#EBEFFF"
}
},
Well here is the solution. style-dictionary-utils
const StyleDictionary = require('style-dictionary-utils');
const myStyleDictionary = StyleDictionary.extend({
source: ['src/tokens/**/*.json'],
platforms: {
scss: {
transformGroup: 'scss',
buildPath: 'lib/tokens/scss/',
files: [
{
destination: 'tokens.scss',
format: 'scss/variables',
},
],
},
css: {
transformGroup: 'css',
buildPath: 'lib/tokens/css/',
files: [
{
destination: 'tokens.css',
format: 'css/variables',
},
],
},
'js-src': {
transformGroup: 'js',
buildPath: 'src/tokens/js/',
files: [
{
destination: 'tokens.js',
format: 'javascript/esm',
},
],
},
js: {
transformGroup: 'js',
buildPath: 'lib/tokens/js/',
files: [
{
destination: 'tokens.js',
format: 'javascript/esm',
},
],
},
},
});
myStyleDictionary.buildAllPlatforms();
Produces this beautiful output for the js files
export default {
animations: {
default: "all 400ms cubic-bezier(0.420, 0.000, 0.580, 1.000)",
},
colors: {
primary: {
"100": "#EBEFFF",
"200": "#B9C4FF",
"300": "#8FA0FF",
"400": "#6E82FE",
"500": "#556AEB",
"600": "#1D2F99",
"700": "#1D2F99",
"800": "#0C1A66",
"900": "#020A33",
},
neutral: {
"100": "#F8F9FA",
"200": "#E9ECEF",
"300": "#DEE2E6",
"400": "#CED4DA",
"500": "#ADB5BD",
"600": "#6C757D",
"700": "#495057",
"800": "#343A40",
"900": "#212529",
black: "#000000",
white: "#FFFFFF",
},
warning: {
"100": "#FFF8EB",
"200": "#FFE3B0",
"300": "#FFCC75",
"400": "#FFB23B",
"500": "#FF9500",
"600": "#CC7C00",
"700": "#996000",
"800": "#664200",
"900": "#332200",
},
danger: {
"100": "#FF9680",
"200": "#FF8166",
"300": "#FF6C4C",
"400": "#FF4B24",
"500": "#FF380D",
"600": "#EB2A00",
"700": "#CC2400",
"800": "#B22000",
"900": "#991B00",
},
},
radius: {
small: "8px",
large: "20px",
},
shadows: {
"level-1": "0px 4px 6px rgba(33, 37, 41, 0.2)",
"level-2": "0px 2px 10px rgba(33, 37, 41, 0.15)",
},
spacings: {
"4": "4px",
"8": "8px",
"16": "16px",
"32": "32px",
"40": "40px",
"48": "48px",
"56": "56px",
"64": "64px",
"80": "80px",
"100": "100px",
},
};
I'm gonna go ahead and close. I wonder though if something like this will get added into the next version.
The javascript/module format and javascript/module-flat gives options if I want to flatten objects or not but with javascript/es6 the only option seems to be flat.
Storybook 7 docs complains that the file has no export named 'default' due to it being a cjs module but if I build it as javascript/es6 I get a bunch of single line exports where the objects are flattened like
But in my react app my components need to access them like this which works in react but not when storybook loads my component, due to the above mentioned problem
This is the correct format from using javascript/module but the wrong export for what I need
If I change the first line to
This makes storybook and react happy but of course it would get overwritten any time I re-build tokens.
Is there a way to make javascript/es6 not flat?
Thanks