From 3c7e7f8dfc72ece4796865185f21e644aaf22ea0 Mon Sep 17 00:00:00 2001 From: zallen <47335686+redallen@users.noreply.github.com> Date: Mon, 20 Jul 2020 10:29:10 -0400 Subject: [PATCH] feat(tokens): add componentIndex (#4572) --- packages/react-tokens/scripts/writeTokens.js | 43 ++-- packages/react-tokens/src/variablesByFile.js | 252 ------------------- 2 files changed, 25 insertions(+), 270 deletions(-) delete mode 100644 packages/react-tokens/src/variablesByFile.js diff --git a/packages/react-tokens/scripts/writeTokens.js b/packages/react-tokens/scripts/writeTokens.js index 51b7a942cc4..692b441070a 100644 --- a/packages/react-tokens/scripts/writeTokens.js +++ b/packages/react-tokens/scripts/writeTokens.js @@ -33,7 +33,25 @@ export default ${tokenName}; `.trim() ); -const index = []; +const allIndex = []; +const componentIndex = []; + +const outputIndex = (index, indexFile) => { + const esmIndexString = index.map(file => `export * from './${file}';`).join('\n'); + outputFileSync(join(outDir, 'esm', indexFile), esmIndexString); + outputFileSync(join(outDir, 'js', indexFile.replace('.js', 'd.ts')), esmIndexString); + outputFileSync( + join(outDir, 'js', 'index.js'), + ` +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +exports.__esModule = true; +${index.map(file => `__export(require('./${file}'));`).join('\n')} +`.trim() + ); +}; /** * Writes CJS and ESM tokens to `dist` directory @@ -47,7 +65,8 @@ function writeTokens(tokens) { writeESMExport(tokenName, tokenString); writeCJSExport(tokenName, tokenString); writeDTSExport(tokenName, tokenString); - index.push(tokenName); + allIndex.push(tokenName); + componentIndex.push(tokenName); // Legacy token support -- values may be incorrect. Object.values(tokenValue) @@ -63,28 +82,16 @@ function writeTokens(tokens) { writeESMExport(oldTokenName, oldTokenString); writeCJSExport(oldTokenName, oldTokenString); writeDTSExport(oldTokenName, oldTokenString); - index.push(oldTokenName); + allIndex.push(oldTokenName); }); }); // Index files including legacy tokens - const esmIndexString = index.map(file => `export * from './${file}';`).join('\n'); - outputFileSync(join(outDir, 'esm', 'index.js'), esmIndexString); - outputFileSync(join(outDir, 'js', 'index.d.ts'), esmIndexString); - outputFileSync( - join(outDir, 'js', 'index.js'), - ` -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -exports.__esModule = true; -${index.map(file => `__export(require('./${file}'));`).join('\n')} -`.trim() - ); + outputIndex(allIndex, 'index.js'); + outputIndex(componentIndex, 'componentIndex.js'); // eslint-disable-next-line no-console - console.log('Wrote', index.length * 3 + 3, 'token files'); + console.log('Wrote', allIndex.length * 3 + 3, 'token files'); } writeTokens(generateTokens()); diff --git a/packages/react-tokens/src/variablesByFile.js b/packages/react-tokens/src/variablesByFile.js deleted file mode 100644 index 532836a146f..00000000000 --- a/packages/react-tokens/src/variablesByFile.js +++ /dev/null @@ -1,252 +0,0 @@ -const glob = require('glob'); -const { dirname, resolve, join, basename } = require('path'); -const { parse } = require('css'); -const { readFileSync, readdirSync } = require('fs'); -const { outputFileSync } = require('fs-extra'); - -const outDir = resolve(__dirname, '../dist/variables'); -const pfStylesDir = dirname(require.resolve('@patternfly/patternfly/patternfly.css')); -const templateDir = resolve(__dirname, './templates'); - -const cssFiles = glob.sync('{**/{components,layouts}/**/*.css,**/patternfly-charts.css,**/patternfly-variables.css}', { - cwd: pfStylesDir, - ignore: ['assets/**'] -}); - -const formatCustomPropertyName = key => key.replace('--pf-', '').replace(/-+/g, '_'); - -const getRegexMatches = (string, regex) => { - const res = {}; - let matches; - while ((matches = regex.exec(string))) { - res[matches[1]] = matches[2].trim(); - } - return res; -}; - -// various lookup tables to resolve variables - -const variables = readFileSync(require.resolve('@patternfly/patternfly/_variables.scss'), 'utf8'); -const cssGlobalsToScssVarsMap = getRegexMatches(variables, /(--pf-.*):\s*(?:#{)?(\$?pf-[\w- _]+)}?;/g); - -// contains default values and mappings to colors.scss for color values -const scssVariables = readFileSync( - require.resolve('@patternfly/patternfly/sass-utilities/scss-variables.scss'), - 'utf8' -); -const scssVarsMap = getRegexMatches(scssVariables, /(\$.*):\s*([^;^!]+)/g); - -// contains default values and mappings to colors.scss for color values -const scssColorVariables = readFileSync(require.resolve('@patternfly/patternfly/sass-utilities/colors.scss'), 'utf8'); -const scssColorsMap = getRegexMatches(scssColorVariables, /(\$.*):\s*([^\s]+)\s*(?:!default);/g); - -// contains default values and mappings to colors.scss for color values -const cssGlobalVariables = readFileSync(require.resolve('@patternfly/patternfly/patternfly-variables.css'), 'utf8'); -const cssGlobalVariablesMap = getRegexMatches(cssGlobalVariables, /(--pf-[\w-]*):\s*([\w -_]+);/g); - -const combinedScssVarsColorsMap = { - ...scssVarsMap, - ...scssColorsMap -}; - -const formatFilePathToName = filePath => { - // const filePathArr = filePath.split('/'); - let prefix = ''; - if (filePath.indexOf('components/') > -1) { - prefix = 'c_'; - } else if (filePath.indexOf('layouts/') > -1) { - prefix = 'l_'; - } - return `${prefix}${basename(filePath, '.css').replace(/-+/g, '_')}`; -}; - -// pre-populate the localVarsMap so we can lookup local variables within or across files, e.g. if we have the declaration: -// --pf-c-chip-group--MarginBottom: calc(var(--pf-c-chip-group--c-chip--MarginBottom) * -1); -// then we need to find: -// --pf-c-chip-group--c-chip--MarginBottom: var(--pf-global--spacer--xs); -const localVarsMap = {}; -cssFiles.forEach(filePath => { - const absFilePath = resolve(pfStylesDir, filePath); - const cssAst = parse(readFileSync(absFilePath, 'utf8')); - - cssAst.stylesheet.rules - .filter(node => node.type === 'rule' && node.selectors.indexOf('.pf-t-dark') === -1) - .forEach(node => { - node.declarations - .filter(decl => decl.type === 'declaration') - .forEach(decl => { - const { property, value, parent } = decl; - if (property.startsWith('--pf')) { - localVarsMap[property] = { - ...localVarsMap[property], - [parent.selectors[0]]: value - }; - } - }); - }); -}); - -const getFromLocalMap = (match, selector) => { - if (localVarsMap[match]) { - // have exact selectors match - if (localVarsMap[match][selector]) { - return localVarsMap[match][selector]; - } else if (Object.keys(localVarsMap[match]).length === 1) { - // only one match, return its value - return Object.values(localVarsMap[match])[0]; - } else { - // find the nearest parent selector and return its value - let bestMatch = ''; - let bestValue = ''; - for (const key in localVarsMap[match]) { - if (localVarsMap[match].hasOwnProperty(key)) { - // remove trailing * from key to compare - let sanitizedKey = key.replace(/\*$/, '').trim(); - sanitizedKey = sanitizedKey.replace(/>$/, '').trim(); - sanitizedKey = sanitizedKey.replace(/\[.*\]$/, '').trim(); - // is key a parent of selector? - if (selector.indexOf(sanitizedKey) > -1) { - if (sanitizedKey.length > bestMatch.length) { - // longest matching key is the winner - bestMatch = key; - bestValue = localVarsMap[match][key]; - } - } - } - } - if (!bestMatch) { - // eslint-disable-next-line no-console - console.error(`no matching selector found for ${match} in localVarsMap`); - } - return bestValue; - } - } else { - // eslint-disable-next-line no-console - console.error(`no matching property found for ${match} in localVarsMap`); - } -}; - -const getComputedCssVarValue = (value, selector) => - value.replace(/var\(([\w|-]*)\)/g, (full, match) => { - if (match.startsWith('--pf-global')) { - if (cssGlobalsToScssVarsMap[match]) { - return cssGlobalsToScssVarsMap[match]; - } else { - return full; - } - } else { - if (selector) { - return getFromLocalMap(match, selector); - } - } - }); - -const getFinalValue = (value, selector) => - value.replace(/var\(([\w|-]*)\)/g, (full, match) => { - if (match.startsWith('--pf-global')) { - if (cssGlobalVariablesMap[match]) { - return cssGlobalVariablesMap[match]; - } else { - return full; - } - } else { - if (selector) { - return getFromLocalMap(match, selector); - } - } - }); - -const getComputedScssVarValue = value => - value.replace(/\$pf[^,)\s*/]*/g, match => { - if (combinedScssVarsColorsMap[match]) { - return combinedScssVarsColorsMap[match]; - } else { - return match; - } - }); - -const getVarsMap = (value, selector) => { - // evaluate the value and follow the variable chain - let varsMap = [value]; - - let computedValue = value; - let finalValue = value; - while ( - finalValue.indexOf('var(--pf') > -1 || - computedValue.indexOf('var(--pf') > -1 || - computedValue.indexOf('$pf-') > -1 - ) { - // keep following the variable chain until we get to a value - if (finalValue.indexOf('var(--pf') > -1) { - finalValue = getFinalValue(finalValue, selector); - } - if (computedValue.indexOf('var(--pf') > -1) { - computedValue = getComputedCssVarValue(computedValue, selector); - } else { - computedValue = getComputedScssVarValue(computedValue); - } - varsMap.push(computedValue); - } - const lastElement = varsMap[varsMap.length - 1]; - if (lastElement.indexOf('pf-') > -1) { - varsMap.push(finalValue); - } - // all values should not be boxed by var() - varsMap = varsMap.map(variable => variable.replace(/var\(([\w|-]*)\)/g, (full, match) => match)); - - return varsMap; -}; - -const tokens = {}; -cssFiles.forEach(filePath => { - const absFilePath = resolve(pfStylesDir, filePath); - const cssAst = parse(readFileSync(absFilePath, 'utf8')); - // key is the formatted file name, e.g. c_about_modal_box - const key = formatFilePathToName(filePath); - - cssAst.stylesheet.rules - .filter(node => node.type === 'rule' && node.selectors.indexOf('.pf-t-dark') === -1) - .forEach(node => { - node.declarations - .filter(decl => decl.type === 'declaration') - .forEach(decl => { - const { property, value, parent } = decl; - if (property.startsWith('--pf')) { - const selector = parent.selectors[0]; - - const varsMap = getVarsMap(value, selector); - const propertyObj = { - property, - value: varsMap[varsMap.length - 1], - token: formatCustomPropertyName(property) - }; - if (varsMap.length > 1) { - propertyObj.values = varsMap; - } - - if (tokens[key]) { - if (tokens[key][selector]) { - tokens[key][selector].push(propertyObj); - } else { - tokens[key][selector] = [propertyObj]; - } - } else { - tokens[key] = { - [selector]: [propertyObj] - }; - } - } - }); - }); -}); - -readdirSync(templateDir).forEach(templateFile => { - const template = require(join(templateDir, templateFile)); - outputFileSync(template.getOutputPath({ outDir }), template.getContent({ tokens })); - Object.entries(tokens).forEach(([tokenName, tokenValue]) => { - outputFileSync( - template.getSingleOutputPath({ outDir, tokenName }), - template.getSingleContent({ tokenName, tokenValue }) - ); - }); -});