UNPKG

@patreon/studio

Version:

Patreon Studio Design System

120 lines (94 loc) 3.53 kB
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;