mirror of https://github.com/openedx/paragon.git
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:
parent
096ffabacd
commit
75580fdca6
2
Makefile
2
Makefile
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
59
lib/help.js
59
lib/help.js
|
|
@ -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('');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
13
package.json
13
package.json
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
});
|
||||
Loading…
Reference in New Issue