diff --git a/package-lock.json b/package-lock.json index ce87ed447..9a8708f36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "@stencil/core": "^4.0.1" }, "devDependencies": { - "@babel/cli": "^7.22.9", "@babel/core": "^7.22.9", "@babel/preset-env": "^7.22.9", "@babel/preset-react": "^7.22.5", @@ -44,7 +43,7 @@ "@storybook/web-components-webpack5": "^7.0.27", "@testing-library/jest-dom": "^5.16.5", "@types/jest": "^27.0.3", - "@types/node": "^20.4.2", + "@types/node": "^20.4.5", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@typescript-eslint/eslint-plugin": "^6.1.0", @@ -87,6 +86,7 @@ "stylelint-order": "^6.0.3", "stylelint-scss": "^5.0.1", "ts-jest": "^29.1.1", + "tslib": "^2.6.1", "typescript": "^5.1.6", "vue": "^3.3.4" }, @@ -158,35 +158,6 @@ "x-default-browser": "bin/x-default-browser.js" } }, - "node_modules/@babel/cli": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.9.tgz", - "integrity": "sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - }, - "bin": { - "babel": "bin/babel.js", - "babel-external-helpers": "bin/babel-external-helpers.js" - }, - "engines": { - "node": ">=6.9.0" - }, - "optionalDependencies": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -5060,13 +5031,6 @@ "tar-stream": "^2.1.4" } }, - "node_modules/@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9487,9 +9451,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.4.tgz", - "integrity": "sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew==", + "version": "20.4.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", + "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==", "dev": true }, "node_modules/@types/node-fetch": { @@ -13001,15 +12965,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -16742,12 +16697,6 @@ "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", "dev": true }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -28528,15 +28477,6 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -30933,9 +30873,9 @@ } }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", "dev": true }, "node_modules/tsutils": { @@ -32394,7 +32334,7 @@ }, "packages/core": { "name": "@juntossomosmais/atomium", - "version": "0.1.0-alpha.23" + "version": "0.1.0-alpha.25" }, "packages/icons": { "name": "@juntossomosmais/atomium-icons" diff --git a/package.json b/package.json index 201e2343c..28321e01d 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "@stencil/core": "^4.0.1" }, "devDependencies": { - "@babel/cli": "^7.22.9", "@babel/core": "^7.22.9", "@babel/preset-env": "^7.22.9", "@babel/preset-react": "^7.22.5", @@ -64,7 +63,7 @@ "@storybook/web-components-webpack5": "^7.0.27", "@testing-library/jest-dom": "^5.16.5", "@types/jest": "^27.0.3", - "@types/node": "^20.4.2", + "@types/node": "^20.4.5", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@typescript-eslint/eslint-plugin": "^6.1.0", @@ -107,6 +106,7 @@ "stylelint-order": "^6.0.3", "stylelint-scss": "^5.0.1", "ts-jest": "^29.1.1", + "tslib": "^2.6.1", "typescript": "^5.1.6", "vue": "^3.3.4" }, diff --git a/packages/tokens/README.md b/packages/tokens/README.md index 51cb3fca0..c966ce1bb 100644 --- a/packages/tokens/README.md +++ b/packages/tokens/README.md @@ -2,6 +2,8 @@ This is the tokens of the Atomium design system using CSS Variables to be used in any Framework or Vanilla JS +You should see all the tokens in the [Tokens section of the Storybook](https://juntossomosmais.github.io/atomium/?path=/docs/tokens-colors--docs) + ## Getting Started ### Installation @@ -12,7 +14,9 @@ npm i @juntossomosmais/atomium-tokens ### Basic Usage -The variables can be used in CSS +#### CSS + +The variables can be used in **CSS** ```js import '@juntossomosmais/atomium-tokens/tokens.css' @@ -24,7 +28,9 @@ import '@juntossomosmais/atomium-tokens/tokens.css' } ``` -Or in Javascript +#### JavaScript + +You can also use the variables in **JavaScript** ```js @@ -35,6 +41,10 @@ import * as tokens from '@juntossomosmais/atomium-tokens' primary: tokens.colorBrandPrimaryDark1; ``` -You should see all the tokens in the [Tokens section of the Storybook](https://juntossomosmais.github.io/atomium/?path=/docs/tokens-colors--docs) +#### Json +We also provide a **JSON** file with tokens. It is useful for using to compare tokens in Stylelint, for example. +```js +import tokens from '@juntossomosmais/atomium-tokens/tokens.json' +``` diff --git a/packages/tokens/jest.config.js b/packages/tokens/jest.config.js index 1cf5a06ee..50f308e70 100644 --- a/packages/tokens/jest.config.js +++ b/packages/tokens/jest.config.js @@ -9,8 +9,8 @@ module.exports = { coverageReporters: ['lcov'], coveragePathIgnorePatterns: ['/node_modules/'], collectCoverageFrom: [ - 'scripts/*.ts', - '!scripts/*.spec.ts', + 'scripts/**/*.ts', + '!scripts/**/*.spec.ts', '!src/**/stories/**', '!src/**/*.mock.ts', '!src/**/*.spec.ts', diff --git a/packages/tokens/package.json b/packages/tokens/package.json index b91555f3f..55288dbaf 100644 --- a/packages/tokens/package.json +++ b/packages/tokens/package.json @@ -25,8 +25,7 @@ "scripts": { "start": "rollup -c -w", "build:css": "rollup -c", - "build:gen-tokens": "npx babel ./scripts/generate-tokens.ts --out-file ./dist/scripts/generate-tokens.js && node ./dist/scripts/generate-tokens.js", - "build": "npm run build:css && npm run build:gen-tokens && rollup -c rollup.tokens.config.mjs && tsc --module commonjs && rimraf dist/scripts", + "build": "npm run build:css && ts-node ./scripts/generate-tokens/index.ts && rollup -c rollup.tokens.config.mjs && tsc --module commonjs", "publish-library": "npm publish --access public", "test:ci": "jest --coverage --no-cache --updateSnapshot --ci", "test": "jest" diff --git a/packages/tokens/scripts/generate-tokens.spec.ts b/packages/tokens/scripts/generate-tokens/__tests__/generate-javascript-tokens.spec.ts similarity index 71% rename from packages/tokens/scripts/generate-tokens.spec.ts rename to packages/tokens/scripts/generate-tokens/__tests__/generate-javascript-tokens.spec.ts index e2d1b8723..9716a1189 100644 --- a/packages/tokens/scripts/generate-tokens.spec.ts +++ b/packages/tokens/scripts/generate-tokens/__tests__/generate-javascript-tokens.spec.ts @@ -1,5 +1,11 @@ import fs from 'fs' -import { OUTPUT_DIR, TOKENS_DIR, extractTokensFromCss } from './generate-tokens' + +import { TOKENS_DIR, variablePrefixes } from '..' +import { + OUTPUT_DIR, + extractTokensFromCss, + generateJsTokensFromCssFile, +} from '../generate-javascript-tokens' jest.mock('fs', () => ({ readFileSync: jest.fn().mockReturnValue(` @@ -10,8 +16,13 @@ jest.mock('fs', () => ({ writeFileSync: jest.fn(), })) -describe('Generate Tokens', () => { +describe('Generate JavaScript tokens', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + it('should extract tokens from CSS content', () => { + generateJsTokensFromCssFile(TOKENS_DIR, variablePrefixes) expect(fs.readFileSync).toHaveBeenCalledWith(TOKENS_DIR, 'utf8') expect(fs.writeFileSync).toHaveBeenCalledWith( `${OUTPUT_DIR}/index.ts`, diff --git a/packages/tokens/scripts/generate-tokens/__tests__/generate-json-tokens.spec.ts b/packages/tokens/scripts/generate-tokens/__tests__/generate-json-tokens.spec.ts new file mode 100644 index 000000000..e78c234a7 --- /dev/null +++ b/packages/tokens/scripts/generate-tokens/__tests__/generate-json-tokens.spec.ts @@ -0,0 +1,48 @@ +import fs from 'fs' +import path from 'path' + +import { TOKENS_DIR, variablePrefixes } from '..' +import { + OUTPUT_DIR, + extractTokensFromCss, + generateJsonTokensFromCssFile, +} from '../generate-json-tokens' + +jest.mock('fs', () => ({ + readFileSync: jest.fn().mockReturnValue(` + --color-neutral-black: #000; + --color-contextual-success-dark-1: #106105; + --color-brand-primary-dark-1: #b85000; + --spacing-small: 4px; + --zindex-1: 1; + `), + writeFileSync: jest.fn(), +})) + +describe('Generate tokens.json', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should generate JSON file with tokens', () => { + generateJsonTokensFromCssFile(TOKENS_DIR, variablePrefixes) + + const expectedTokens = `{\n "color-neutral-black\": \"#000\",\n "color-contextual-success-dark-1\": \"#106105\",\n "color-brand-primary-dark-1\": \"#b85000\",\n "spacing-small\": \"4px\",\n "zindex-1\": \"1\"\n}` + + expect(fs.readFileSync).toHaveBeenCalledWith(TOKENS_DIR, 'utf8') + expect(fs.writeFileSync).toHaveBeenCalledWith( + path.join(`${OUTPUT_DIR}/tokens.json`), + expectedTokens, + 'utf8' + ) + }) + + it('should ignore CSS content without prefix tokens', () => { + const cssContent = ` + --any-variable: any_value; + ` + const tokens: Record = {} + extractTokensFromCss(cssContent, 'color') + expect(tokens).toEqual({}) + }) +}) diff --git a/packages/tokens/scripts/generate-tokens.ts b/packages/tokens/scripts/generate-tokens/generate-javascript-tokens.ts similarity index 77% rename from packages/tokens/scripts/generate-tokens.ts rename to packages/tokens/scripts/generate-tokens/generate-javascript-tokens.ts index 96277d61f..6ed42505d 100644 --- a/packages/tokens/scripts/generate-tokens.ts +++ b/packages/tokens/scripts/generate-tokens/generate-javascript-tokens.ts @@ -2,10 +2,8 @@ import fs from 'fs' import path from 'path' const CURRENT_DIR = __dirname -export const TOKENS_DIR = path.resolve(CURRENT_DIR, '../tokens.css') export const OUTPUT_DIR = path.resolve(CURRENT_DIR, '../../') -const variablePrefixes = ['color', 'spacing', 'screen'] const tokens: Record = {} export function extractTokensFromCss(cssContent: string, prefix: string) { @@ -27,7 +25,7 @@ export function extractTokensFromCss(cssContent: string, prefix: string) { ) } -export function generateJavaScriptFile(outputFilePath: string) { +export function generateJsFile(outputFilePath: string) { const jsCode = Object.entries(tokens) .map(([variable, value]) => `export const ${variable} = '${value}';`) .join('\n') @@ -35,7 +33,10 @@ export function generateJavaScriptFile(outputFilePath: string) { fs.writeFileSync(outputFilePath, jsCode, 'utf8') } -function processCssFileByTokenPrefix(cssFilePath: string) { +export function generateJsTokensFromCssFile( + cssFilePath: string, + variablePrefixes: string[] +) { const cssContent = fs.readFileSync(cssFilePath, 'utf8') variablePrefixes.forEach((prefix) => extractTokensFromCss(cssContent, prefix)) @@ -43,7 +44,5 @@ function processCssFileByTokenPrefix(cssFilePath: string) { const outputFileName = 'index.ts' const outputFilePath = path.join(OUTPUT_DIR, outputFileName) - generateJavaScriptFile(outputFilePath) + generateJsFile(outputFilePath) } - -processCssFileByTokenPrefix(TOKENS_DIR) diff --git a/packages/tokens/scripts/generate-tokens/generate-json-tokens.ts b/packages/tokens/scripts/generate-tokens/generate-json-tokens.ts new file mode 100644 index 000000000..1f8404bf0 --- /dev/null +++ b/packages/tokens/scripts/generate-tokens/generate-json-tokens.ts @@ -0,0 +1,43 @@ +import fs from 'fs' +import path from 'path' + +const CURRENT_DIR = __dirname +export const OUTPUT_DIR = path.resolve(CURRENT_DIR, '../../dist') + +const tokens: Record = {} + +export function extractTokensFromCss(cssContent: string, prefix: string) { + const cssVariablePattern = new RegExp(`--(${prefix}[\\w-]+):\\s*([^;]+)`, 'g') + + cssContent.replace( + cssVariablePattern, + (_, variable: string, value: string) => { + const variableKebabCase = variable + .replace(/([a-z])([A-Z])/g, '$1-$2') + .toLowerCase() + + tokens[variableKebabCase] = value.trim() + return '' + } + ) +} + +export function generateJsonFile(outputFilePath: string) { + const jsonOutput = JSON.stringify(tokens, null, 2) + + fs.writeFileSync(outputFilePath, jsonOutput, 'utf8') +} + +export function generateJsonTokensFromCssFile( + cssFilePath: string, + variablePrefixes: string[] +) { + const cssContent = fs.readFileSync(cssFilePath, 'utf8') + + variablePrefixes.forEach((prefix) => extractTokensFromCss(cssContent, prefix)) + + const outputFileName = 'tokens.json' + const outputFilePath = path.join(OUTPUT_DIR, outputFileName) + + generateJsonFile(outputFilePath) +} diff --git a/packages/tokens/scripts/generate-tokens/index.ts b/packages/tokens/scripts/generate-tokens/index.ts new file mode 100644 index 000000000..d9710c945 --- /dev/null +++ b/packages/tokens/scripts/generate-tokens/index.ts @@ -0,0 +1,20 @@ +import path from 'path' + +import { generateJsTokensFromCssFile } from './generate-javascript-tokens' +import { generateJsonTokensFromCssFile } from './generate-json-tokens' + +const CURRENT_DIR = __dirname +export const TOKENS_DIR = path.resolve(CURRENT_DIR, '../../dist/tokens.css') + +export const variablePrefixes = [ + 'color', + 'spacing', + 'screen', + 'font', + 'border', + 'zindex', + 'transition', +] + +generateJsTokensFromCssFile(TOKENS_DIR, variablePrefixes) +generateJsonTokensFromCssFile(TOKENS_DIR, variablePrefixes) diff --git a/packages/tokens/tsconfig.json b/packages/tokens/tsconfig.json index 8121b4be7..019f5928c 100644 --- a/packages/tokens/tsconfig.json +++ b/packages/tokens/tsconfig.json @@ -10,5 +10,10 @@ "esModuleInterop": true, "skipLibCheck": true }, + "ts-node": { + "compilerOptions": { + "module": "commonjs" + } + }, "exclude": ["node_modules", "dist"] }