Skip to content

Commit 0b624a1

Browse files
committed
Add theme json script
- Move colors in theme json - Change SCSS variables to the new name based on the new generated theme-json SCSS variable file
1 parent 4f8e741 commit 0b624a1

File tree

6 files changed

+339
-74
lines changed

6 files changed

+339
-74
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
const chalk = require('chalk')
2+
const path = require('path')
3+
const fs = require('fs')
4+
5+
const logId = '[' + chalk.blue('WebpackThemeJsonPlugin') + ']'
6+
7+
class WebpackThemeJsonPlugin {
8+
/**
9+
* constructor
10+
* @param {Object} options = {
11+
* context: string - default: '../src/theme-json'
12+
* output: string - default: '../theme.json'
13+
* scssOutput: string - default: '../src/scss/00-variables/_theme-json.scss'
14+
* watch: boolean - default: false
15+
* }
16+
*/
17+
constructor(options) {
18+
// folders
19+
this._context = options.context || path.resolve(__dirname, '../src/theme-json') + '/'
20+
this._output = options.output || path.resolve(__dirname, '../theme.json')
21+
this._scssOutput = options.scssOutput || path.resolve(__dirname, '../src/scss/01-abstract/_theme-json.scss')
22+
23+
if (options.watch) {
24+
fs.watch(this._context, () => {
25+
this.refresh()
26+
})
27+
}
28+
29+
this.refresh()
30+
}
31+
32+
/**
33+
* apply
34+
*/
35+
apply() {}
36+
37+
/**
38+
* Generate theme json file
39+
*/
40+
generateThemeJson() {
41+
const jsonFiles = fs.readdirSync(this._context, {
42+
withFileTypes: true,
43+
})
44+
const themeJson = {}
45+
46+
jsonFiles.forEach((file) => {
47+
if (file.isFile() && file.name.endsWith('.json')) {
48+
let json = fs.readFileSync(this._context + file.name, 'utf8')
49+
50+
try {
51+
json = JSON.parse(json)
52+
} catch (e) {
53+
// eslint-disable-next-line no-console
54+
console.error(logId, 'Error parsing JSON file:', file.name)
55+
}
56+
57+
if (isPlainObject(json)) {
58+
extend(true, themeJson, json)
59+
} else {
60+
// eslint-disable-next-line no-console
61+
console.error(logId, 'JSON file is not a plain object:', file.name)
62+
}
63+
}
64+
})
65+
66+
fs.writeFileSync(this._output, JSON.stringify(themeJson, null, 2))
67+
// eslint-disable-next-line no-console
68+
console.log(logId, 'JSON files successfully generated !')
69+
70+
return this
71+
}
72+
73+
/**
74+
* Generate scss variables file
75+
*/
76+
generateScssVariables() {
77+
const comment = [
78+
'/**',
79+
' * Theme JSON',
80+
' * scss variables are extracted from theme.json',
81+
' *',
82+
" * !!! DON'T EDIT THIS FILE !!!",
83+
' *',
84+
' */',
85+
]
86+
const tasks = {
87+
'settings-color-palette'(key, value) {
88+
let result = ''
89+
90+
for (const color of value) {
91+
result += `${getVariableName('settings-color-' + color.slug)}: ${color.color};\n`
92+
}
93+
94+
return result
95+
},
96+
'settings-custom': 'default',
97+
'settings-spacing-spacingSizes'(key, value) {
98+
let result = ''
99+
100+
for (const spacing of value) {
101+
result += `${getVariableName('settings-spacing-' + spacing.slug)}: ${spacing.size};\n`
102+
}
103+
104+
return result
105+
},
106+
}
107+
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
108+
const taskNames = Object.keys(tasks)
109+
let jsonFile = fs.readFileSync(this._output, 'utf8')
110+
111+
// check if the theme.json file is valid
112+
try {
113+
jsonFile = JSON.parse(jsonFile)
114+
} catch (e) {
115+
// eslint-disable-next-line no-console
116+
console.error(logId, 'Error parsing JSON file:', this._output)
117+
return this
118+
}
119+
120+
// format the scss variable name
121+
function getVariableName(id) {
122+
return `$${id.replace(/([A-Z])/g, '-$1').toLowerCase()}`
123+
}
124+
125+
// traverse the theme.json file and generate the scss variables
126+
function traverse(obj, parents = [], result = '') {
127+
for (const key in obj) {
128+
const id = (parents.length > 0 ? parents.join('-') + '-' : '') + key
129+
const taskName = taskNames.filter((t) => (id.startsWith(t) ? t : null))[0]
130+
const task = taskName ? tasks[taskName] : null
131+
132+
if (isPlainObject(obj[key])) {
133+
result += traverse(obj[key], [...parents, key])
134+
} else if (task) {
135+
if (task === 'default' && typeof obj[key] === 'string') {
136+
result += `${getVariableName(id)}: ${obj[key]};\n`
137+
} else if (typeof task === 'function') {
138+
result += task(key, obj[key])
139+
}
140+
}
141+
}
142+
143+
return result
144+
}
145+
146+
fs.writeFileSync(this._scssOutput, comment.join('\n') + '\n' + traverse(jsonFile))
147+
148+
return this
149+
}
150+
151+
/**
152+
* Refresh the theme json and scss variables files
153+
*/
154+
refresh() {
155+
this.generateThemeJson()
156+
this.generateScssVariables()
157+
return this
158+
}
159+
}
160+
161+
// ----
162+
// utils
163+
// ----
164+
function isPlainObject(o) {
165+
return o?.constructor === Object || Object.getPrototypeOf(o ?? 0) === null
166+
}
167+
168+
function extend() {
169+
const args = arguments
170+
const firstArgIsBool = typeof args[0] === 'boolean'
171+
const deep = firstArgIsBool ? args[0] : false
172+
const start = firstArgIsBool ? 1 : 0
173+
const rt = isPlainObject(args[start]) ? args[start] : {}
174+
175+
for (let i = start + 1; i < args.length; i++) {
176+
for (const prop in args[i]) {
177+
if (deep && isPlainObject(args[i][prop])) {
178+
rt[prop] = extend(true, {}, rt[prop], args[i][prop])
179+
} else if (typeof args[i][prop] !== 'undefined') {
180+
rt[prop] = args[i][prop]
181+
}
182+
}
183+
}
184+
185+
return rt
186+
}
187+
188+
module.exports = WebpackThemeJsonPlugin

src/theme-json/colors.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{
2+
"version": 3,
3+
"settings": {
4+
"color": {
5+
"defaultDuotone": false,
6+
"defaultGradients": false,
7+
"defaultPalette": false,
8+
"palette": [
9+
{
10+
"name": "Noir",
11+
"slug": "black",
12+
"color": "#000"
13+
},
14+
{
15+
"name": "Blanc",
16+
"slug": "white",
17+
"color": "#fff"
18+
},
19+
{
20+
"name": "Jaune 500",
21+
"slug": "yellow-500",
22+
"color": "#ffe600"
23+
},
24+
{
25+
"name": "Gris 100",
26+
"slug": "grey-100",
27+
"color": "#eee"
28+
},
29+
{
30+
"name": "Gris 200",
31+
"slug": "grey-200",
32+
"color": "#ccc"
33+
},
34+
{
35+
"name": "Gris 300",
36+
"slug": "grey-300",
37+
"color": "#aaa"
38+
},
39+
{
40+
"name": "Gris 400",
41+
"slug": "grey-400",
42+
"color": "#999"
43+
},
44+
{
45+
"name": "Gris 500",
46+
"slug": "grey-500",
47+
"color": "#888"
48+
},
49+
{
50+
"name": "Gris 600",
51+
"slug": "grey-600",
52+
"color": "#777"
53+
},
54+
{
55+
"name": "Gris 700",
56+
"slug": "grey-700",
57+
"color": "#555"
58+
},
59+
{
60+
"name": "Gris 800",
61+
"slug": "grey-800",
62+
"color": "#333"
63+
},
64+
{
65+
"name": "Gris 900",
66+
"slug": "grey-900",
67+
"color": "#111"
68+
}
69+
]
70+
}
71+
}
72+
}

src/theme-json/settings.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"version": 3,
3+
"settings": {
4+
"useRootPaddingAwareAlignments": true,
5+
"layout": {
6+
"contentSize": "760px",
7+
"wideSize": "1160px"
8+
},
9+
"border": {
10+
"color": true,
11+
"radius": true,
12+
"style": true,
13+
"width": true
14+
},
15+
"shadow": {
16+
"defaultPresets": false
17+
}
18+
},
19+
"styles": {
20+
"spacing": {
21+
"blockGap": "var(--wp--preset--spacing--sm)",
22+
"padding": {
23+
"right": "clamp(20px, calc(20px + 20 * ((100vw - 400px) / 880)), 40px)",
24+
"left": "clamp(20px, calc(20px + 20 * ((100vw - 400px) / 880)), 40px)"
25+
}
26+
}
27+
}
28+
}

src/theme-json/spacings.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"version": 3,
3+
"settings": {
4+
"spacing": {
5+
"blockGap": true,
6+
"margin": true,
7+
"padding": true,
8+
"units": ["px", "em", "rem", "vh", "vw", "%"],
9+
"defaultSpacingSizes": false,
10+
"spacingSizes": [
11+
{
12+
"slug": "xs",
13+
"size": "16px",
14+
"name": "xs (16px)"
15+
},
16+
{
17+
"slug": "sm",
18+
"size": "32px",
19+
"name": "sm (32px)"
20+
},
21+
{
22+
"slug": "md",
23+
"size": "48px",
24+
"name": "md (48px)"
25+
},
26+
{
27+
"slug": "lg",
28+
"size": "64px",
29+
"name": "lg (64px)"
30+
}
31+
]
32+
}
33+
}
34+
}

src/theme-json/typography.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": 3,
3+
"settings": {
4+
"typography": {
5+
"fontSizes": [],
6+
"defaultFontSizes": false,
7+
"customFontSize": false,
8+
"dropCap": false,
9+
"fontStyle": false,
10+
"fontWeight": false,
11+
"letterSpacing": false,
12+
"textDecoration": false,
13+
"textTransform": false,
14+
"fluid": true
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)