feat: move design tokens CLI commands to paragon CLI (#2609)

* feat: move cli design tokens to paragon cli

* feat: update paths for build-tokens.js

* feat: added descriptions for CLI commands

* refactor: removed commander implementation

* refactor: added build-scss process status

* feat: added ora compilation status

* refactor: code refactoring

* feat: added help description for single command

* refactor: after review

* refactor: refactoring after review

* chore: update docs and cli params parsing

---------

Co-authored-by: monteri <lansevermore>
Co-authored-by: PKulkoRaccoonGang <peter.kulko@raccoongang.com>
Co-authored-by: Viktor Rusakov <vrusakov66@gmail.com>
This commit is contained in:
monteri 2023-10-30 15:05:50 +02:00 committed by GitHub
parent 096ffabacd
commit 75580fdca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 475 additions and 235 deletions

View File

@ -9,7 +9,7 @@ build:
rm -rf dist/**/__snapshots__
rm -rf dist/__mocks__
rm -rf dist/setupTest.js
node build-scss.js
./bin/paragon-scripts.js build-scss
export TRANSIFEX_RESOURCE = paragon
transifex_langs = "ar,ca,es_419,fr,he,id,ko_KR,pl,pt_BR,ru,th,uk,zh_CN,es_AR,es_ES,pt_PT,tr_TR,it_IT"

View File

@ -61,7 +61,10 @@ The Paragon CLI (Command Line Interface) is a tool that provides various utility
### Available Commands
- `paragon install-theme [theme]`: Installs the specific @edx/brand package.
- `paragon install-theme [theme]`: Installs the specific [brand package](https://github.com/openedx/brand-openedx).
- `paragon build-tokens`: Build Paragon's design tokens.
- `paragon replace-variables`: Replace SCSS variables usages or definitions to CSS variables and vice versa in `.scss` files.
- `paragon build-scss`: Compile Paragon's core and themes SCSS into CSS.
Use `paragon help` to see more information.

View File

@ -2,8 +2,9 @@
const chalk = require('chalk');
const themeCommand = require('../lib/install-theme');
const helpCommand = require('../lib/help');
const HELP_COMMAND = 'help';
const buildTokensCommand = require('../lib/build-tokens');
const replaceVariablesCommand = require('../lib/replace-variables');
const buildScssCommand = require('../lib/build-scss');
const COMMANDS = {
/**
@ -25,6 +26,9 @@ const COMMANDS = {
* {
* name: '--optionName',
* description: 'optionDescription',
* choices: 'optionChoices',
* defaultValue: 'optionDefaultValue',
* required: true/false,
* },
* ...
* ],
@ -43,14 +47,130 @@ const COMMANDS = {
},
],
},
'build-tokens': {
executor: buildTokensCommand,
description: 'CLI to build Paragon design tokens.',
options: [
{
name: '-s, --source',
description: 'Specify the source directory for design tokens.',
defaultValue: '\'\'',
},
{
name: '-b, --build-dir',
description: 'Specify the build directory for the generated tokens.',
defaultValue: './build/',
},
{
name: '--source-tokens-only',
description: 'Include only source design tokens in the build.',
defaultValue: false,
},
{
name: '-t, --themes',
description: 'Specify themes to include in the token build.',
defaultValue: 'light',
},
],
},
'replace-variables': {
executor: replaceVariablesCommand,
description: 'CLI to replace SCSS variables usages or definitions to CSS variables and vice versa in .scss files.',
parameters: [
{
name: '-p, --filePath',
description: 'Path to the file or directory where to replace variables.',
defaultValue: '\'\'',
},
],
options: [
{
name: '-s, --source',
description: 'Type of replacement: usage or definition. If set to "definition" the command will only update SCSS variables definitions with CSS variables, if set to "usage" - all occurrences of SCSS variables will we replaced',
defaultValue: '\'\'',
},
{
name: '-t, --replacementType',
description: 'Type of replacement: usage or definition. If set to "definition" the command will only update SCSS variables definitions with CSS variables, if set to "usage" - all occurrences of SCSS variables will we replaced',
choices: '[usage|definition]',
defaultValue: 'definition',
},
{
name: '-d, --direction',
description: 'Map direction: css-to-scss or scss-to-css, if replacement type parameter is set to "definition" this has no effect.',
choices: '[scss-to-css|css-to-scss]',
defaultValue: 'scss-to-css',
},
],
},
'build-scss': {
executor: buildScssCommand,
description: 'CLI to compile Paragon\'s core and themes SCSS into CSS.',
options: [
{
name: '--corePath',
description: 'Path to the theme\'s core SCSS file, defaults to Paragon\'s core.scss.',
defaultValue: 'styles/scss/core/core.scss',
},
{
name: '--themesPath',
description: `Path to the directory that contains themes' files. Expects directory to have following structure:
themes/
light/
index.css
other_css_files
dark/
index.css
other_css_files
some_other_custom_theme/
index.css
other_css_files
...
where index.css has imported all other CSS files in the theme's subdirectory. The script will output
light.css, dark.css and some_other_custom_theme.css files (together with maps and minified versions).
You can provide any amount of themes. Default to paragon's themes.
`,
defaultValue: 'styles/css/themes',
},
{
name: '--outDir',
description: 'Specifies directory where to out resulting CSS files.',
defaultValue: './dist',
},
{
name: '--defaultThemeVariants',
description: `Specifies default theme variants. Defaults to a single 'light' theme variant.
You can provide multiple default theme variants by passing multiple values, for
example: \`--defaultThemeVariants light dark\`
`,
defaultValue: 'light',
},
],
},
help: {
executor: helpCommand,
executor: (args) => helpCommand(COMMANDS, args),
parameters: [
{
name: 'command',
description: 'Specifies command name.',
defaultValue: '\'\'',
choices: '[install-theme|build-tokens|replace-variables|build-scss]',
required: false,
},
],
description: 'Displays help for available commands.',
},
};
/**
* Executes a Paragon CLI command based on the provided command-line arguments.
*
* @async
* @function executeParagonCommand
*/
(async () => {
const [command] = process.argv.slice(2);
const [command, ...commandArgs] = process.argv.slice(2);
const executor = COMMANDS[command];
if (!executor) {
@ -59,13 +179,8 @@ const COMMANDS = {
return;
}
if (command === HELP_COMMAND) {
helpCommand(COMMANDS);
return;
}
try {
await executor.executor();
await executor.executor(commandArgs);
} catch (error) {
// eslint-disable-next-line no-console
console.error(chalk.red.bold('An error occurred:', error.message));

View File

@ -1,4 +1,3 @@
#!/usr/bin/env node
const fs = require('fs');
const sass = require('sass');
const postCSS = require('postcss');
@ -8,7 +7,10 @@ const postCSSMinify = require('postcss-minify');
const combineSelectors = require('postcss-combine-duplicated-selectors');
const { pathToFileURL } = require('url');
const path = require('path');
const { program, Option } = require('commander');
const minimist = require('minimist');
const chalk = require('chalk');
const ora = require('ora');
const { capitalize } = require('./utils');
const paragonThemeOutputFilename = 'theme-urls.json';
@ -92,17 +94,19 @@ const compileAndWriteStyleSheets = ({
},
}],
});
const commonPostCssPlugins = [
postCSSImport(),
postCSSCustomMedia({ preserve: true }),
combineSelectors({ removeDuplicatedProperties: true }),
];
const postCSSCompilation = ora(`Compilation for ${capitalize(name)} stylesheet...`).start();
postCSS(commonPostCssPlugins)
.process(compiledStyleSheet.css, { from: stylesPath, map: false })
.then((result) => {
postCSSCompilation.succeed(`Successfully compiled ${capitalize(name)} theme stylesheet`);
fs.writeFileSync(`${outDir}/${name}.css`, result.css);
postCSS([postCSSMinify()])
.process(result.css, { from: `${name}.css`, map: { inline: false } })
.then((minifiedResult) => {
@ -129,83 +133,56 @@ const compileAndWriteStyleSheets = ({
isDefaultThemeVariant,
});
}
fs.writeFileSync(`${outDir}/${paragonThemeOutputFilename}`, `${JSON.stringify(paragonThemeOutput, null, 2)}\n`);
})
.then(() => {
ora().succeed(chalk.underline.bold.green(`Successfully built stylesheet for ${capitalize(name)} theme!\n`));
})
.catch((error) => {
ora().fail(chalk.bold(`Failed to build stylesheets for ${capitalize(name)}: ${error.message}`));
});
};
program
.version('0.0.1')
.description('CLI to compile Paragon\'s core and themes\' SCSS into CSS.')
.addOption(
new Option(
'--corePath <corePath>',
'Path to the theme\'s core SCSS file, defaults to Paragon\'s core.scss.',
),
)
.addOption(
new Option(
'--themesPath <themesPath>',
`Path to the directory that contains themes' files. Expects directory to have following structure:
themes/
light/
index.css
other_css_files
dark/
index.css
other_css_files
some_other_custom_theme/
index.css
other_css_files
...
where index.css has imported all other CSS files in the theme's subdirectory. The script will output
light.css, dark.css and some_other_custom_theme.css files (together with maps and minified versions).
You can provide any amount of themes. Default to paragon's themes.
`,
),
)
.addOption(
new Option(
'--outDir <outDir>',
'Specifies directory where to out resulting CSS files.',
),
)
.addOption(
new Option(
'--defaultThemeVariants <defaultThemeVariants...>',
`Specifies default theme variants. Defaults to a single 'light' theme variant.
You can provide multiple default theme variants by passing multiple values, for
example: \`--defaultThemeVariants light dark\`
`,
),
);
/**
* Builds SCSS stylesheets based on the provided command arguments.
*
* @param {Array<string>} commandArgs - Command line arguments for building SCSS stylesheets.
*/
function buildScssCommand(commandArgs) {
const defaultArgs = {
corePath: path.resolve(process.cwd(), 'styles/scss/core/core.scss'),
themesPath: path.resolve(process.cwd(), 'styles/css/themes'),
outDir: './dist',
defaultThemeVariants: 'light',
};
program.parse(process.argv);
const {
corePath,
themesPath,
outDir,
defaultThemeVariants,
} = minimist(commandArgs, { default: defaultArgs });
const options = program.opts();
const {
corePath = path.resolve(__dirname, 'styles/scss/core/core.scss'),
themesPath = path.resolve(__dirname, 'styles/css/themes'),
outDir = './dist',
defaultThemeVariants = ['light'],
} = options;
// Core CSS
compileAndWriteStyleSheets({
name: 'core',
stylesPath: corePath,
outDir,
});
// Theme Variants CSS
fs.readdirSync(themesPath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.forEach((themeDir) => {
compileAndWriteStyleSheets({
name: themeDir.name,
stylesPath: `${themesPath}/${themeDir.name}/index.css`,
outDir,
isThemeVariant: true,
isDefaultThemeVariant: defaultThemeVariants.includes(themeDir.name),
});
// Core CSS
compileAndWriteStyleSheets({
name: 'core',
stylesPath: corePath,
outDir,
});
// Theme Variants CSS
fs.readdirSync(themesPath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.forEach((themeDir) => {
compileAndWriteStyleSheets({
name: themeDir.name,
stylesPath: `${themesPath}/${themeDir.name}/index.css`,
outDir,
isThemeVariant: true,
isDefaultThemeVariant: defaultThemeVariants.includes(themeDir.name),
});
});
}
module.exports = buildScssCommand;

119
lib/build-tokens.js Executable file
View File

@ -0,0 +1,119 @@
const path = require('path');
const minimist = require('minimist');
const { StyleDictionary, colorTransform, createCustomCSSVariables } = require('../tokens/style-dictionary');
const { createIndexCssFile } = require('../tokens/utils');
/**
* Builds tokens for CSS styles from JSON source files.
*
* @param {string[]} commandArgs - Command line arguments for building tokens.
* @param {string} [commandArgs.build-dir='./build/'] - The directory where the build output will be placed.
* @param {string} [commandArgs.source] - The source directory containing JSON token files.
* @param {boolean} [commandArgs.source-tokens-only=false] - Indicates whether to include only source tokens.
* @param {string|string[]} [commandArgs.themes=['light']] - The themes (variants) for which to build tokens.
*/
async function buildTokensCommand(commandArgs) {
const defaultParams = {
themes: ['light'],
'build-dir': './build/',
};
const alias = {
'build-dir': 'b',
themes: 't',
};
const {
'build-dir': buildDir,
source: tokensSource,
'source-tokens-only': hasSourceTokensOnly,
themes,
} = minimist(commandArgs, { alias, default: defaultParams, boolean: 'source-tokens-only' });
const coreConfig = {
include: [path.resolve(__dirname, '../tokens/src/core/**/*.json')],
source: tokensSource ? [`${tokensSource}/core/**/*.json`] : [],
platforms: {
css: {
prefix: 'pgn',
transformGroup: 'css',
// NOTE: buildPath must end with a slash
buildPath: buildDir.slice(-1) === '/' ? buildDir : `${buildDir}/`,
files: [
{
format: 'css/custom-variables',
destination: 'core/variables.css',
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
{
format: 'css/custom-media-breakpoints',
destination: 'core/custom-media-breakpoints.css',
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
],
transforms: StyleDictionary.transformGroup.css.filter(item => item !== 'size/rem').concat('color/sass-color-functions', 'str-replace'),
options: {
fileHeader: 'customFileHeader',
},
},
},
};
const getStyleDictionaryConfig = (themeVariant) => ({
...coreConfig,
include: [...coreConfig.include, path.resolve(__dirname, `../tokens/src/themes/${themeVariant}/**/*.json`)],
source: tokensSource ? [`${tokensSource}/themes/${themeVariant}/**/*.json`] : [],
transform: {
'color/sass-color-functions': {
...StyleDictionary.transform['color/sass-color-functions'],
transformer: (token) => colorTransform(token, themeVariant),
},
},
format: {
'css/custom-variables': formatterArgs => createCustomCSSVariables({
formatterArgs,
themeVariant,
}),
},
platforms: {
css: {
...coreConfig.platforms.css,
files: [
{
format: 'css/custom-variables',
destination: `themes/${themeVariant}/variables.css`,
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
{
format: 'css/utility-classes',
destination: `themes/${themeVariant}/utility-classes.css`,
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
],
},
},
});
StyleDictionary.extend(coreConfig).buildAllPlatforms();
createIndexCssFile({ buildDir, isTheme: false });
themes.forEach((themeVariant) => {
const config = getStyleDictionaryConfig(themeVariant);
StyleDictionary.extend(config).buildAllPlatforms();
createIndexCssFile({ buildDir, isTheme: true, themeVariant });
});
}
module.exports = buildTokensCommand;

View File

@ -1,52 +1,65 @@
/* eslint-disable no-console */
const chalk = require('chalk');
const DESCRIPTION_PAD = 20;
/**
* Pads a description string to align with a specified offset string.
* Finds a command based on the given name in the commands object.
*
* @param {string} description - The description to pad.
* @param {string} offsetString - The offset string that the description should align with.
* @returns {string} - The padded description.
* @param {Array} commandName - The name to find the command.
* @param {Object} commands - The object containing commands to search in.
* @returns {Object|null} - The found command or null if the command is not found.
*/
function padLeft(description, offsetString) {
// Calculate the necessary padding based on the offsetString length
const padding = ' '.repeat(Math.max(0, DESCRIPTION_PAD - offsetString.length));
return `${padding}${description}`;
}
const findCommandByName = (commandName, commands) => ((commandName in commands)
? { [commandName]: commands[commandName] } : null);
/**
* Displays a help message for available commands, including descriptions, parameters, and options.
*
* @param {Object} commands - An object containing information about available commands.
* @param {Array} commandArgs - An array containing the command name.
*/
function helpCommand(commands) {
function helpCommand(commands, commandArgs) {
const retrievedCommands = commandArgs.length ? findCommandByName(commandArgs, commands) : commands;
if (!retrievedCommands) {
console.error(chalk.red.bold('Unknown command. Usage: paragon help <command>.'));
return;
}
console.log(chalk.yellow.bold('Paragon Help'));
console.log();
console.log('Available commands:');
if (!commandArgs.length) {
console.log('Available commands:');
}
console.log();
Object.entries(commands).forEach(([command, { parameters, description, options }]) => {
console.log(` ${chalk.green.bold(command)}`);
Object.entries(retrievedCommands).forEach(([command, { parameters, description, options }]) => {
console.log(` ${chalk.green.underline.bold(command)}`);
if (description) {
console.log(` ${description}`);
console.log(` ${description}`);
}
if (parameters && parameters.length > 0) {
console.log(` ${chalk.cyan('Parameters: ')}`);
console.log('');
console.log(` ${chalk.bold.cyan('Parameters: ')}`);
parameters.forEach(parameter => {
const requiredStatus = parameter.required ? 'Required' : 'Optional';
const formattedDescription = padLeft(parameter.description, parameter.name);
console.log(` ${parameter.name}${formattedDescription} (${requiredStatus}, Default: ${parameter.defaultValue || 'None'})`);
console.log(` ${chalk.yellow.bold(parameter.name)} ${chalk.grey(parameter.choices ? `${parameter.choices}, Default: ${parameter.defaultValue || 'None'}` : `Default: ${parameter.defaultValue || 'None'}`)}`);
if (parameter.description) {
console.log(` ${parameter.description}`);
}
console.log('');
});
}
if (options && options.length > 0) {
console.log(` ${chalk.cyan('Options: ')}`);
console.log('');
console.log(` ${chalk.bold.cyan('Options: ')}`);
options.forEach(option => {
const formattedDescription = padLeft(option.description, option.name);
console.log(` ${option.name}${formattedDescription}`);
console.log(` ${chalk.yellow.bold(option.name)} ${chalk.grey(option.choices ? `${option.choices}, Default: ${option.defaultValue || 'None'}` : `Default: ${option.defaultValue}`)}`);
if (option.description) {
console.log(` ${option.description}`);
}
console.log('');
});
}

38
lib/replace-variables.js Executable file
View File

@ -0,0 +1,38 @@
const minimist = require('minimist');
const { transformInPath } = require('../tokens/utils');
const mapSCSStoCSS = require('../tokens/map-scss-to-css');
/**
* Replaces CSS or SASS variables in a file with their corresponding values.
*
* @param {string[]} commandArgs - Command line arguments for replacing variables.
* @param {string} [commandArgs.filePath] - The path to the file in which variables should be replaced.
* @param {string} [commandArgs.source] - The path to the source directory containing variable mappings.
* @param {string} [commandArgs.replacementType] - The type of replacement ('usage' or 'all').
* @param {string} [commandArgs.direction] - The direction of replacement ('forward' or 'backward').
*/
async function replaceVariablesCommand(commandArgs) {
const alias = {
filePath: 'p',
source: 's',
replacementType: 't',
direction: 'd',
};
const {
filePath,
source: sourcePath,
replacementType,
direction,
} = minimist(commandArgs, { alias });
const variablesMap = mapSCSStoCSS(sourcePath);
if (replacementType === 'usage') {
await transformInPath(filePath, variablesMap, 'usage', [], direction);
} else {
await transformInPath(filePath, variablesMap);
}
}
module.exports = replaceVariablesCommand;

9
lib/utils.js Normal file
View File

@ -0,0 +1,9 @@
// eslint-disable-next-line import/prefer-default-export
function capitalize(str) {
if (typeof str !== 'string' || str.length === 0) {
return '';
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
module.exports = { capitalize };

91
package-lock.json generated
View File

@ -22,13 +22,17 @@
"child_process": "^1.0.2",
"chroma-js": "^2.4.2",
"classnames": "^2.3.1",
"cli-progress": "^3.12.0",
"commander": "^9.4.1",
"email-prop-type": "^3.0.0",
"file-selector": "^0.6.0",
"glob": "^8.0.3",
"inquirer": "^8.2.5",
"lodash.uniqby": "^4.7.0",
"log-update": "^4.0.0",
"mailto-link": "^2.0.0",
"minimist": "^1.2.8",
"ora": "^5.4.1",
"postcss": "^8.4.21",
"postcss-combine-duplicated-selectors": "^10.0.3",
"postcss-custom-media": "^9.1.2",
@ -53,10 +57,7 @@
"uuid": "^9.0.0"
},
"bin": {
"build-design-tokens": "tokens/build-tokens.js",
"build-scss": "build-scss.js",
"paragon": "bin/paragon-scripts.js",
"replace-scss-with-css": "tokens/replace-variables.js"
"paragon": "bin/paragon-scripts.js"
},
"devDependencies": {
"@babel/cli": "^7.16.8",
@ -13104,6 +13105,17 @@
"node": ">=8"
}
},
"node_modules/cli-progress": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
"integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
"dependencies": {
"string-width": "^4.2.3"
},
"engines": {
"node": ">=4"
}
},
"node_modules/cli-spinners": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz",
@ -24684,8 +24696,9 @@
},
"node_modules/lodash.capitalize": {
"version": "4.2.1",
"dev": true,
"license": "MIT"
"resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
"integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==",
"dev": true
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
@ -24816,6 +24829,66 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-update": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
"integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
"dependencies": {
"ansi-escapes": "^4.3.0",
"cli-cursor": "^3.1.0",
"slice-ansi": "^4.0.0",
"wrap-ansi": "^6.2.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-update/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/log-update/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/log-update/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/log-update/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/longest-streak": {
"version": "2.0.4",
"license": "MIT",
@ -25469,7 +25542,8 @@
},
"node_modules/minimist": {
"version": "1.2.8",
"license": "MIT",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -36318,7 +36392,8 @@
},
"node_modules/table": {
"version": "6.8.1",
"license": "BSD-3-Clause",
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
"integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==",
"dependencies": {
"ajv": "^8.0.1",
"lodash.truncate": "^4.4.2",

View File

@ -6,9 +6,6 @@
"module": "dist/index.js",
"license": "Apache-2.0",
"bin": {
"build-design-tokens": "./tokens/build-tokens.js",
"replace-scss-with-css": "./tokens/replace-variables.js",
"build-scss": "./build-scss.js",
"paragon": "./bin/paragon-scripts.js"
},
"publishConfig": {
@ -51,9 +48,9 @@
"build-types": "tsc --emitDeclarationOnly",
"playroom:start": "npm run playroom:start --workspace=www",
"playroom:build": "npm run playroom:build --workspace=www",
"build-tokens": "node tokens/build-tokens.js --build-dir ./styles/css",
"replace-variables-usage-with-css": "node tokens/replace-variables.js -p src -t usage",
"replace-variables-definition-with-css": "node tokens/replace-variables.js -p src -t definition"
"build-tokens": "./bin/paragon-scripts.js build-tokens --build-dir ../styles/css",
"replace-variables-usage-with-css": "./bin/paragon-scripts.js replace-variables -p src -t usage",
"replace-variables-definition-with-css": "./bin/paragon-scripts.js replace-variables -p src -t definition"
},
"dependencies": {
"@popperjs/core": "^2.11.4",
@ -62,13 +59,17 @@
"child_process": "^1.0.2",
"chroma-js": "^2.4.2",
"classnames": "^2.3.1",
"cli-progress": "^3.12.0",
"commander": "^9.4.1",
"email-prop-type": "^3.0.0",
"file-selector": "^0.6.0",
"glob": "^8.0.3",
"inquirer": "^8.2.5",
"lodash.uniqby": "^4.7.0",
"log-update": "^4.0.0",
"mailto-link": "^2.0.0",
"minimist": "^1.2.8",
"ora": "^5.4.1",
"postcss": "^8.4.21",
"postcss-combine-duplicated-selectors": "^10.0.3",
"postcss-custom-media": "^9.1.2",

View File

@ -1,110 +0,0 @@
#!/usr/bin/env node
const { program } = require('commander');
const path = require('path');
const { StyleDictionary, colorTransform, createCustomCSSVariables } = require('./style-dictionary');
const { createIndexCssFile } = require('./utils');
program
.version('0.0.1')
.description('CLI to build design tokens for various platforms (currently only CSS is supported) from Paragon Design Tokens.')
.option('--build-dir <char>', 'A path to directory where to put files with built tokens, must end with a /.', './build/')
.option('--source <char>', 'A path where to look for additional tokens that will get merged with Paragon ones, must be a path to root directory of the token files that contains "root" and "themes" subdirectories.')
.option('--source-tokens-only', 'If provided, only tokens from --source will be included in the output; Paragon tokens will be used for references but not included in the output.')
.option('--themes <themes...>', 'A list of theme variants to build. By default, Paragon currently only supports a light theme.')
.parse();
const {
buildDir,
source: tokensSource,
sourceTokensOnly: hasSourceTokensOnly,
themes,
} = program.opts();
const coreConfig = {
include: [path.resolve(__dirname, 'src/core/**/*.json')],
source: tokensSource ? [`${tokensSource}/core/**/*.json`] : [],
platforms: {
css: {
prefix: 'pgn',
transformGroup: 'css',
// NOTE: buildPath must end with a slash
buildPath: buildDir.slice(-1) === '/' ? buildDir : `${buildDir}/`,
files: [
{
format: 'css/custom-variables',
destination: 'core/variables.css',
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
{
format: 'css/custom-media-breakpoints',
destination: 'core/custom-media-breakpoints.css',
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
],
transforms: StyleDictionary.transformGroup.css.filter(item => item !== 'size/rem').concat('color/sass-color-functions', 'str-replace'),
options: {
fileHeader: 'customFileHeader',
},
},
},
};
const getStyleDictionaryConfig = (themeVariant) => ({
...coreConfig,
include: [...coreConfig.include, path.resolve(__dirname, `src/themes/${themeVariant}/**/*.json`)],
source: tokensSource ? [`${tokensSource}/themes/${themeVariant}/**/*.json`] : [],
transform: {
'color/sass-color-functions': {
...StyleDictionary.transform['color/sass-color-functions'],
transformer: (token) => colorTransform(token, themeVariant),
},
},
format: {
'css/custom-variables': formatterArgs => createCustomCSSVariables({
formatterArgs,
themeVariant,
}),
},
platforms: {
css: {
...coreConfig.platforms.css,
files: [
{
format: 'css/custom-variables',
destination: `themes/${themeVariant}/variables.css`,
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
{
format: 'css/utility-classes',
destination: `themes/${themeVariant}/utility-classes.css`,
filter: hasSourceTokensOnly ? 'isSource' : undefined,
options: {
outputReferences: !hasSourceTokensOnly,
},
},
],
},
},
});
StyleDictionary.extend(coreConfig).buildAllPlatforms();
// This line creates the index file for core folder, specially when buildDir is outside Paragon.
createIndexCssFile({ buildDir, isTheme: false });
const THEME_VARIANTS = themes || ['light'];
THEME_VARIANTS.forEach((themeVariant) => {
const config = getStyleDictionaryConfig(themeVariant);
StyleDictionary.extend(config).buildAllPlatforms();
createIndexCssFile({ buildDir, isTheme: true, themeVariant });
});