@patreon/studio
Version:
Patreon Studio Design System
120 lines (94 loc) • 3.53 kB
JavaScript
const fs = require('fs');
const postcss = require('postcss');
const postcssJs = require('postcss-js');
// Define the paths to the CSS files
const textBundleConfig = {
'body-text': 'styles/classNameForBodyText/BodyTextBundle.module.css',
'display-text': 'styles/classNameForDisplayText/DisplayTextBundle.module.css',
'heading-text': 'styles/classNameForHeadingText/HeadingTextBundle.module.css',
'data-body-text': 'styles/classNameForDataBodyText/DataBodyTextBundle.module.css',
'data-display-text': 'styles/classNameForDataDisplayText/DataDisplayTextBundle.module.css',
'data-heading-text': 'styles/classNameForDataHeadingText/DataHeadingTextBundle.module.css',
};
// Transform the CSS files into ASTs which can be added
const textBundles = Object.entries(textBundleConfig).reduce((res, [key, bundleValue]) => {
const fileContents = fs.readFileSync(bundleValue);
// Parse the CSS file into an AST
const root = postcss.parse(fileContents);
// remove the @responsive rules from the AST
root.walkAtRules('responsive', (rule) => {
root.append(rule.nodes);
rule.remove();
});
// convert the CSS AST into JS objects which can be shared
const js = postcssJs.objectify(root);
// extract the shared styles from the JS object
const shared = postcssJs.parse(js['.shared']);
// extract the weight styles from the JS object
const weights = Object.entries(js)
.filter(([type]) => type.startsWith('.weight'))
.reduce((acc, [type, value]) => {
acc[type.replace('.weight', '').toLowerCase()] = postcssJs.parse(value);
return acc;
}, {});
// extract the size styles from the JS object
const sizes = Object.entries(js)
.filter(([type]) => type.startsWith('.size'))
.reduce((acc, [type, value]) => {
acc[type.replace('.size', '').toLowerCase()] = postcssJs.parse(value);
return acc;
}, {});
// return an object with the ASTs for each rule in the bundle
res[key] = {
shared,
weights,
sizes,
};
return res;
}, {});
const plugin = () => {
return {
postcssPlugin: 'embed-typography-at-rule',
AtRule(targetRule) {
if (targetRule.name !== 'embed-typography') {
return;
}
const match = targetRule.params.match(/^([^\(]+)\((.*)\)$/);
if (!match) {
throw new Error(`Invalid embed rule: ${targetRule.params}`);
}
const name = match[1].trim();
const targetBundle = textBundles[name];
if (!targetBundle) {
throw new Error(`Invalid embed name: ${name}`);
}
const params = match[2].split(',').reduce((res, param) => {
const [key, value] = param.split('=').map((p) => p.trim());
res[key] = value;
return res;
}, {});
if (!params.size) {
throw new Error(`Missing size param: ${targetRule.params}`);
}
if (!params.weight) {
throw new Error(`Missing weight param: ${targetRule.params}`);
}
const { size, weight } = params;
const targetSize = targetBundle.sizes[size];
if (!targetSize) {
throw new Error(`Invalid size: ${size}`);
}
const targetWeight = targetBundle.weights[weight];
if (!targetWeight) {
throw new Error(`Invalid weight: ${weight}`);
}
const targetShared = targetBundle.shared;
targetRule.after(targetShared);
targetRule.after(targetSize);
targetRule.after(targetWeight);
targetRule.remove();
},
};
};
plugin.postcss = true;
module.exports = plugin;