@spark-ui/cli-utils 2.7.3 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.8.0](https://github.com/adevinta/spark/compare/@spark-ui/cli-utils@2.7.3...@spark-ui/cli-utils@2.8.0) (2023-03-23)
7
+
8
+ ### Features
9
+
10
+ - **cli-utils:** remove deprecated spark-setup-themes cli package ([a2efa9c](https://github.com/adevinta/spark/commit/a2efa9c15b3ece4285043fd25858435f455f257d))
11
+
6
12
  ## [2.7.3](https://github.com/adevinta/spark/compare/@spark-ui/cli-utils@2.7.2...@spark-ui/cli-utils@2.7.3) (2023-03-22)
7
13
 
8
14
  **Note:** Version bump only for package @spark-ui/cli-utils
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@spark-ui/cli-utils",
3
- "version": "2.7.3",
3
+ "version": "2.8.0",
4
4
  "description": "Spark CLI utils",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "bin": {
9
9
  "spark": "./bin/spark.mjs",
10
- "spark-generate": "./bin/spark-generate.mjs",
11
- "spark-setup-themes": "./bin/spark-setup-themes.mjs"
10
+ "spark-generate": "./bin/spark-generate.mjs"
12
11
  },
13
12
  "type": "module",
14
13
  "repository": {
@@ -18,19 +17,16 @@
18
17
  },
19
18
  "dependencies": {
20
19
  "@clack/prompts": "0.6.2",
21
- "@spark-ui/theme-utils": "^2.10.1",
22
20
  "camel-case": "4.1.2",
23
21
  "chalk": "5.2.0",
24
22
  "commander": "10.0.0",
25
- "deepmerge": "4.3.0",
26
23
  "esbuild": "0.17.11",
27
24
  "fs-extra": "11.1.0",
28
25
  "glob": "8.1.0",
29
- "hex-rgb": "5.0.0",
30
26
  "pascal-case": "3.1.2"
31
27
  },
32
28
  "devDependencies": {
33
29
  "@types/fs-extra": "11.0.1"
34
30
  },
35
- "gitHead": "8d5929fd944837fe356de0a553b2db7577fa83d2"
31
+ "gitHead": "9b2398b8c58cf0359169bdde07e8e33ede075e16"
36
32
  }
package/src/index.doc.mdx CHANGED
@@ -9,7 +9,6 @@ import { Alert } from '@docs/helpers/Alert'
9
9
  This package provides some CLI commands to improve the development experience when building the Spark design system. Right now there are some different commands available:
10
10
 
11
11
  - A command for generating new components: `$ spark generate`.
12
- - A command for doing the initial setup for one or more themes: `$ spark setup-themes`.
13
12
 
14
13
  ## Contents
15
14
 
@@ -44,14 +43,3 @@ Then, a command prompt will guide you through the process by asking you for:
44
43
  - the package name (required),
45
44
  - the template used (required, only `Component` template available right now)
46
45
  - and the package description (optional).
47
-
48
- <StoryHeading label="Setting up your themes" as="h2" />
49
-
50
- <Alert kind="info">
51
- [Step-by-step guide to integrating Spark into your current
52
- project](?path=/docs/getting-started--docs#getting-started)
53
- </Alert>
54
-
55
- ```bash
56
- $ spark setup-themes
57
- ```
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from 'child_process'
4
- import { join, extname } from 'node:path'
5
- import { readFileSync, readdirSync, writeFileSync, unlinkSync } from 'node:fs'
6
- import { transformSync } from 'esbuild'
7
- import { createRequire } from 'node:module'
8
- import { Logger, System } from '../src/core/index.mjs'
9
- import {
10
- createCSSTokensFile,
11
- createTailwindThemeConfigFile,
12
- } from '../src/setup-themes/utils/index.js'
13
-
14
- const logger = new Logger()
15
- const system = new System({ logger })
16
-
17
- const require = createRequire(import.meta.url)
18
- const configFile = readdirSync(process.cwd()).find(fileName =>
19
- /^(spark\.theme\.config)\.(js|ts|mjs|mts|cjs|cts)$/.test(fileName)
20
- )
21
-
22
- if (!configFile) {
23
- system.exit(
24
- "We couldn't find a `spark.theme.config` file in this folder. Please make sure that the file is located in the root folder of your project"
25
- )
26
- }
27
-
28
- const configFilePath = join(process.cwd(), configFile)
29
- const configFileIsMJS = /spark\.theme\.config\.(mjs)$/.test(configFile)
30
-
31
- const allowedExtensions = ['.ts', '.mts', '.cts', '.js', '.cjs', '.mjs']
32
- const jsFileExtension = '.mjs'
33
- const configFileExtension = extname(configFilePath)
34
- if (!allowedExtensions.includes(configFileExtension)) {
35
- system.exit(`Your spark.theme.config file extension (${configFileExtension}) is not supported.`)
36
- }
37
-
38
- const configFileContent = readFileSync(configFilePath, 'utf-8')
39
- const jsCode = transformSync(configFileContent, { loader: 'ts' }).code
40
-
41
- const jsFilePath = configFilePath.replace(/\.ts$|\.mts$|\.cts$|\.js|\.cjs$/, jsFileExtension)
42
- const jsFileContents = jsCode
43
-
44
- if (!configFileIsMJS) writeFileSync(jsFilePath, jsFileContents)
45
-
46
- import(jsFilePath)
47
- .then(module => {
48
- const { tailwindThemeConfigPath, tailwindCSSPath, themes, htmlFontSize = 16 } = module.default
49
-
50
- createTailwindThemeConfigFile(tailwindThemeConfigPath)
51
- createCSSTokensFile(tailwindCSSPath, themes, htmlFontSize)
52
-
53
- const child = spawn(process.execPath, [jsFilePath], {
54
- stdio: 'inherit',
55
- })
56
-
57
- child.on('exit', code => {
58
- if (!configFileIsMJS) unlinkSync(jsFilePath)
59
- logger.success(
60
- `✨ Your Spark Tailwind theme config file has been successfully created: ${join(
61
- process.cwd(),
62
- tailwindThemeConfigPath
63
- )}`
64
- )
65
-
66
- logger.success(
67
- `✨ Your Spark Tailwind CSS Tokens file file has been successfully created: ${join(
68
- process.cwd(),
69
- tailwindCSSPath
70
- )}`
71
- )
72
-
73
- process.exit(code)
74
- })
75
- })
76
- .catch(err => {
77
- unlinkSync(jsFilePath)
78
- system.exit(`
79
- Something went wrong while running ${configFilePath}: ${err}`)
80
- })
@@ -1,13 +0,0 @@
1
- export const tailwindKeys = {
2
- colors: 'colors',
3
- fontSize: 'fontSize',
4
- screens: 'screens',
5
- }
6
-
7
- export const DEFAULT_KEY = 'DEFAULT'
8
-
9
- export const defaultColors = {
10
- inherit: 'inherit',
11
- current: 'currentColor',
12
- transparent: 'transparent',
13
- }
@@ -1,109 +0,0 @@
1
- import { appendFileSync } from 'node:fs'
2
- import { join } from 'node:path'
3
-
4
- import hexRgb from 'hex-rgb'
5
-
6
- import { DEFAULT_KEY } from './constants.js'
7
- import {
8
- doubleHyphensRegex,
9
- getRemEquivalentValue,
10
- isHex,
11
- isObject,
12
- isStringOrNumber,
13
- toKebabCase,
14
- } from './utils.js'
15
-
16
- function toCSSVars(_theme, className, htmlFontSize) {
17
- const flattenedTheme = {}
18
-
19
- function flatten(theme, paths = []) {
20
- Object.entries(theme).forEach(([key, value]) => {
21
- if (isObject(value)) {
22
- return flatten(value, paths.concat(key))
23
- }
24
-
25
- if (isStringOrNumber(value)) {
26
- const getFormattedValue = () => {
27
- if (isHex(value)) {
28
- const { red, green, blue } = hexRgb(value)
29
-
30
- return `${red} ${green} ${blue}`
31
- }
32
-
33
- if (/rem$/gi.test(value)) {
34
- return getRemEquivalentValue(value, htmlFontSize)
35
- }
36
-
37
- return value
38
- }
39
-
40
- flattenedTheme[
41
- `--${[...paths, key === DEFAULT_KEY ? key.toLowerCase() : key]
42
- .map(toKebabCase)
43
- .join('-')
44
- .replace(doubleHyphensRegex, '-')}`
45
- ] = getFormattedValue()
46
- }
47
- })
48
- }
49
-
50
- flatten(_theme)
51
-
52
- return {
53
- ...flattenedTheme,
54
- className,
55
- }
56
- }
57
-
58
- const toStringifiedTheme = theme =>
59
- Object.entries(theme)
60
- .map(([k, v]) => `${k}:${v}`)
61
- .join(';')
62
-
63
- const getStringifiedThemes = (themeRecord, htmlFontSize) =>
64
- Object.keys(themeRecord).map(key => {
65
- const { className, ...rest } = toCSSVars(themeRecord[key], key, htmlFontSize)
66
-
67
- return key === 'default'
68
- ? `:root{${toStringifiedTheme(rest)}}`
69
- : `.${className}{${toStringifiedTheme(rest)}}`
70
- })
71
-
72
- /**
73
- * Creates a CSS file containing theme tokens represented as CSS custom properties
74
- *
75
- * @param {string} path - The file path where the CSS file will be created.
76
- * @param {Record<string, Theme>} themeRecord - A record (with a required key of "default") of themes that will be included in the CSS Tokens file.
77
- *
78
- * @example
79
- *
80
- * const defaultTheme: Theme = { ... }
81
- * const darkTheme: Theme = { ... }
82
- * const otherTheme: Theme = { ... }
83
- *
84
- * const themes = {
85
- * default: defaultTheme,
86
- * dark: darkTheme
87
- * other: otherTheme
88
- * }
89
- *
90
- * createCSSTokensFile('somePath.css', themes) // will generate a "somePath.css" file in the relative location from which it was called
91
- */
92
- export function createCSSTokensFile(path, themeRecord, htmlFontSize) {
93
- try {
94
- appendFileSync(
95
- join(process.cwd(), path),
96
- `
97
- @tailwind base;
98
- @tailwind components;
99
- @tailwind utilities;
100
- @layer base {${getStringifiedThemes(themeRecord, htmlFontSize).join('')}}
101
- `,
102
- {
103
- flag: 'w',
104
- }
105
- )
106
- } catch (error) {
107
- console.error('Failed to create the CSS token file', error)
108
- }
109
- }
@@ -1,136 +0,0 @@
1
- import { writeFileSync } from 'node:fs'
2
- import { join } from 'node:path'
3
-
4
- import { defaultTheme } from '@spark-ui/theme-utils'
5
-
6
- import { DEFAULT_KEY, defaultColors, tailwindKeys } from './constants.js'
7
- import {
8
- doubleHyphensRegex,
9
- hasNumber,
10
- isAlphanumericWithLeadingLetter,
11
- isCamelCase,
12
- isHex,
13
- isObject,
14
- isStringOrNumber,
15
- toKebabCase,
16
- } from './utils.js'
17
-
18
- function toTailwindConfig(_theme) {
19
- const themeCpy = JSON.parse(JSON.stringify(_theme))
20
-
21
- const { fontSize, colors, screens } = tailwindKeys
22
-
23
- /* eslint-disable complexity */
24
- function traverse(theme, paths = []) {
25
- Object.entries(theme).forEach(([key, value]) => {
26
- // 👀 see: https://tailwindcss.com/docs/font-size#providing-a-default-line-height
27
- if (isObject(value) && !paths.length && key === fontSize) {
28
- Object.keys(value).forEach(k => {
29
- const prefix = toKebabCase(fontSize)
30
- if (isStringOrNumber(value[k])) {
31
- theme[key][k] = `var(--${prefix}-${k})`
32
-
33
- return
34
- }
35
-
36
- const kebabedKey = isCamelCase(k) || hasNumber(k) ? toKebabCase(k) : k
37
-
38
- if (kebabedKey !== k) {
39
- const tmp = theme[key][k]
40
- delete theme[key][k]
41
- theme[key][kebabedKey] = tmp
42
- }
43
-
44
- theme[key][kebabedKey] = [
45
- `var(--${prefix}-${kebabedKey}-font-size)`,
46
- {
47
- ...(value[kebabedKey].lineHeight && {
48
- lineHeight: `var(--${prefix}-${kebabedKey}-line-height)`,
49
- }),
50
- ...(value[kebabedKey].letterSpacing && {
51
- letterSpacing: `var(--${prefix}-${kebabedKey}-letter-spacing)`,
52
- }),
53
- ...(value[kebabedKey].fontWeight && {
54
- fontWeight: `var(--${prefix}-${kebabedKey}-font-weight)`,
55
- }),
56
- },
57
- ]
58
- })
59
-
60
- return
61
- }
62
-
63
- if (isObject(value)) {
64
- Object.keys(value).forEach(k => {
65
- if (k === DEFAULT_KEY) {
66
- return
67
- }
68
-
69
- if (!isObject(value[k]) && !isCamelCase(k)) {
70
- return
71
- }
72
-
73
- const tmp = value[k]
74
- delete value[k]
75
- value[toKebabCase(k)] = tmp
76
- })
77
-
78
- return traverse(value, paths.concat(key))
79
- }
80
-
81
- if (isStringOrNumber(value)) {
82
- const rootPath = paths.at(0) ?? ''
83
- const isScreenValue = rootPath.includes(screens)
84
- const isColorValue = rootPath.includes(colors)
85
-
86
- const formattedValue = (() => {
87
- if (isColorValue && isHex(value)) {
88
- return `rgb(var(--${paths.join('-')}-${key}) / <alpha-value>)`
89
- }
90
- if (isScreenValue) {
91
- return String(value).toLowerCase()
92
- }
93
-
94
- return `var(--${paths.join('-')}-${key.toLowerCase()})`
95
- })()
96
-
97
- const formattedKey = isAlphanumericWithLeadingLetter(key) ? toKebabCase(key) : key
98
-
99
- if (formattedKey !== key) {
100
- delete theme[key]
101
- }
102
-
103
- theme[formattedKey] = isScreenValue
104
- ? formattedValue
105
- : toKebabCase(formattedValue).replace(doubleHyphensRegex, '-')
106
- }
107
- })
108
- }
109
-
110
- traverse(themeCpy)
111
-
112
- return { ...themeCpy, colors: { ...themeCpy.colors, ...defaultColors } }
113
- }
114
-
115
- /**
116
- * Creates a Tailwind config file that links the [theme options](https://tailwindcss.com/docs/theme#configuration-reference) provided by Tailwind with the CSS custom property values generated using the "createCSSTokensFile" function
117
- *
118
- * @param {string} path - The file path where the Tailwind config file will be created.
119
- *
120
- * @example
121
- *
122
- * createTailwindThemeConfigFile('./tailwind.theme.js') // will generate a "tailwind.theme.js" in the relative location from which it was called
123
- */
124
- export function createTailwindThemeConfigFile(path) {
125
- try {
126
- writeFileSync(
127
- join(process.cwd(), path),
128
- `module.exports = ${JSON.stringify(toTailwindConfig(defaultTheme))}`,
129
- {
130
- flag: 'w',
131
- }
132
- )
133
- } catch (error) {
134
- console.error('Failed to create the Tailwind theme config file', error)
135
- }
136
- }
@@ -1,2 +0,0 @@
1
- export { createCSSTokensFile } from './createCSSTokenfile.js'
2
- export { createTailwindThemeConfigFile } from './createTailwindThemeConfigFile.js'
@@ -1,54 +0,0 @@
1
- function toKebabCase(v) {
2
- return v.replace(/[A-Z]+(?=[a-z0-9])|\d+/g, match => '-' + match.toLowerCase())
3
- }
4
-
5
- function isAlphanumericWithLeadingLetter(v) {
6
- return /^[a-zA-Z](?=.*\d)[a-zA-Z\d]*$/.test(v)
7
- }
8
-
9
- function isHex(value) {
10
- if (typeof value === 'number') {
11
- return false
12
- }
13
-
14
- const regexp = /^#[0-9a-fA-F]+$/
15
-
16
- return regexp.test(value)
17
- }
18
-
19
- function isStringOrNumber(value) {
20
- return typeof value === 'string' || typeof value === 'number'
21
- }
22
-
23
- function isObject(x) {
24
- return !!x && x.constructor === Object
25
- }
26
-
27
- function isCamelCase(value) {
28
- return /[A-Z]/.test(value.slice(1))
29
- }
30
-
31
- function hasNumber(value) {
32
- return /\d/.test(value)
33
- }
34
-
35
- function getRemEquivalentValue(remValue, htmlFontSize) {
36
- const defaultBrowserBase = 16
37
- const pxValue = parseFloat(remValue) * defaultBrowserBase
38
-
39
- return `${pxValue / htmlFontSize}rem`
40
- }
41
-
42
- const doubleHyphensRegex = /(?<!var\()--+/g
43
-
44
- export {
45
- toKebabCase,
46
- isHex,
47
- isStringOrNumber,
48
- isObject,
49
- isCamelCase,
50
- isAlphanumericWithLeadingLetter,
51
- hasNumber,
52
- getRemEquivalentValue,
53
- doubleHyphensRegex,
54
- }