aboutsummaryrefslogtreecommitdiffstats
path: root/src/scripts/build-theme-info.js
blob: 4058be9427a18b48ebcb034744aca0593e88709b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
 * Script to get information on which selectors use the brand color.
 * This is needed to provide the accent color feature - the feature will create CSS rules
 * to overwrite the color of these selectors.
 */
const css = require('css');
const fs = require('fs-extra');
const path = require('path');
const theme = require('@meetfranz/theme');

// Colors that should be replaced with the accent color
const accentColors = [
  theme.DEFAULT_ACCENT_COLOR.toLowerCase(),
  '#7367f0',
  '#5e50ee',
];

const cssFile = path.join(__dirname, '..', '..', 'build', 'styles', 'main.css');
const outputFile = path.join(__dirname, '..', 'assets', 'themeInfo.json');

// Parse and extract the rules from a CSS stylesheet file
async function getRulesFromCssFile(file) {
  const cssSrc = (await fs.readFile(file)).toString();
  const cssTree = css.parse(cssSrc);

  return cssTree.stylesheet.rules;
}

/**
 * Get all selectors from a list of parsed CSS rules that set any property to one of the specified
 * values.
 *
 * This function will output an object in this format:
 * {
 *  'property-name': [ array of selectors ]
 * }
 *
 * e.g.
 * {
 *  'background-color': [
 *   '.background',
 *   '.input-dark'
 *  ]
 * }
 *
 * @param {Array} rules Rules as outputted by the `css` module
 * @param {Array} values Array of values that should be searched for
 */
function getSelectorsDeclaringValues(rules, values) {
  const output = {};

  rules.forEach((rule) => {
    if (rule.declarations) {
      rule.declarations.forEach((declaration) => {
        if (declaration.type === 'declaration'
            && values.includes(declaration.value.toLowerCase())) {
          if (!output[declaration.property]) {
            output[declaration.property] = [];
          }
          output[declaration.property] = output[declaration.property].concat(rule.selectors);
        }
      });
    }
  });

  return output;
}

async function generateThemeInfo() {
  if (!await fs.pathExists(cssFile)) {
    console.log('Please make sure to build the project first.');
    return;
  }

  // Read and parse css bundle
  const rules = await getRulesFromCssFile(cssFile);

  console.log(`Found ${rules.length} rules`);

  // Get rules specifying the brand colors
  const brandRules = getSelectorsDeclaringValues(rules, accentColors);

  console.log(`Found ${Object.keys(brandRules).join(', ')} properties that set color to brand color`);

  // Join array of declarations into a single string
  Object.keys(brandRules).forEach((rule) => {
    brandRules[rule] = brandRules[rule].join(', ');
  });

  // Write object with theme info to file
  fs.writeFile(
    outputFile,
    JSON.stringify(brandRules),
  );
}

generateThemeInfo();