poops 1.0.20 → 1.2.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/README.md +674 -138
- package/lib/copy.js +14 -18
- package/lib/markup/collections.js +247 -0
- package/lib/markup/engines/liquid.js +240 -0
- package/lib/markup/engines/nunjucks.js +261 -0
- package/lib/markup/helpers.js +170 -0
- package/lib/markup/highlight.js +77 -0
- package/lib/markup/indexer.js +154 -0
- package/lib/markup/stop-words-en.json +25 -0
- package/lib/markups.js +233 -456
- package/lib/postcss.js +127 -0
- package/lib/reactor.js +169 -0
- package/lib/scripts.js +20 -23
- package/lib/styles.js +28 -117
- package/lib/utils/helpers.js +41 -181
- package/lib/utils/log.js +64 -0
- package/package.json +38 -12
- package/poops.js +153 -158
- package/lib/utils/print-style.js +0 -72
package/lib/utils/helpers.js
CHANGED
|
@@ -1,51 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { hasMagic } from 'glob'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import yaml from 'yaml'
|
|
5
|
+
import { convertGlobToRegex } from 'book-of-spells'
|
|
5
6
|
|
|
6
|
-
function pathExists() {
|
|
7
|
+
export function pathExists() {
|
|
7
8
|
return fs.existsSync(path.join(...arguments))
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
function pathIsDirectory() {
|
|
11
|
+
export function pathIsDirectory() {
|
|
11
12
|
return fs.lstatSync(path.join(...arguments)).isDirectory()
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function mkDir(dirPath) {
|
|
15
|
+
export function mkDir(dirPath) {
|
|
15
16
|
if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true })
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
function mkPath(filePath) {
|
|
19
|
+
export function mkPath(filePath) {
|
|
19
20
|
const dirPath = path.dirname(filePath)
|
|
20
21
|
mkDir(dirPath)
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
function pathForFile(filePath) {
|
|
24
|
-
return
|
|
24
|
+
export function pathForFile(filePath) {
|
|
25
|
+
return path.extname(filePath) !== ''
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
function insertMinSuffix(filePath) {
|
|
28
|
+
export function insertMinSuffix(filePath) {
|
|
28
29
|
const { name, ext } = path.parse(filePath)
|
|
29
30
|
return path.join(path.dirname(filePath), `${name}.min${ext}`)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
33
|
+
export function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
33
34
|
if (pathForFile(outputPath)) return outputPath
|
|
34
35
|
const { name } = path.parse(inputPath)
|
|
35
36
|
return path.join(path.join(outputPath, `${name}.css`))
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function buildScriptOutputFilePath(inputPath, outputPath) {
|
|
39
|
+
export function buildScriptOutputFilePath(inputPath, outputPath) {
|
|
39
40
|
if (pathForFile(outputPath)) return outputPath
|
|
40
41
|
const { name, ext } = path.parse(inputPath)
|
|
41
|
-
|
|
42
|
+
const outExt = /\.(tsx?|jsx)$/i.test(ext) ? '.js' : ext
|
|
43
|
+
return path.join(outputPath, `${name}${outExt}`)
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
function fillBannerTemplate(template, packagesPath) {
|
|
46
|
+
export function fillBannerTemplate(template, packagesPath) {
|
|
45
47
|
packagesPath = packagesPath || process.cwd()
|
|
46
48
|
const packagesFilePath = path.join(packagesPath, 'package.json')
|
|
47
49
|
if (!pathExists(packagesFilePath)) return template
|
|
48
|
-
const pkg =
|
|
50
|
+
const pkg = JSON.parse(fs.readFileSync(packagesFilePath, 'utf-8'))
|
|
49
51
|
const { name, version, homepage, description, license, author } = pkg
|
|
50
52
|
const year = new Date().getFullYear()
|
|
51
53
|
|
|
@@ -59,96 +61,46 @@ function fillBannerTemplate(template, packagesPath) {
|
|
|
59
61
|
.replace(/{{\s?year\s?}}/g, year)
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
function buildTime(start, end) {
|
|
64
|
+
export function buildTime(start, end) {
|
|
63
65
|
const time = Math.round(end - start)
|
|
64
66
|
if (time < 1000) return `${time}ms`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
const seconds = Math.floor(time / 1000)
|
|
68
|
+
const ms = time % 1000
|
|
69
|
+
if (time < 60 * 1000) return `${seconds}s ${ms}ms`
|
|
70
|
+
const minutes = Math.floor(seconds / 60)
|
|
71
|
+
const remainingSeconds = seconds % 60
|
|
72
|
+
return `${minutes}m ${remainingSeconds}s ${ms}ms`
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
function fileSize(filePath) {
|
|
75
|
+
export function fileSize(filePath) {
|
|
70
76
|
const stats = fs.statSync(filePath)
|
|
71
77
|
const fileSizeInBytes = stats.size
|
|
72
78
|
if (fileSizeInBytes < 1000) return `${fileSizeInBytes}B`
|
|
73
79
|
if (fileSizeInBytes < 1000 * 1000) return `${(fileSizeInBytes / 1000).toFixed(0)}KB`
|
|
74
|
-
if (fileSizeInBytes < 1000 * 1000 * 1000) return `${(fileSizeInBytes / 1000 / 1000)
|
|
80
|
+
if (fileSizeInBytes < 1000 * 1000 * 1000) return `${Math.floor(fileSizeInBytes / 1000 / 1000)}MB ${Math.floor((fileSizeInBytes % (1000 * 1000)) / 1000)}KB`
|
|
75
81
|
return fileSizeInBytes
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
function readJsonFile(filePath) {
|
|
84
|
+
export function readJsonFile(filePath) {
|
|
79
85
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
function readYamlFile(filePath) {
|
|
88
|
+
export function readYamlFile(filePath) {
|
|
83
89
|
try {
|
|
84
|
-
return
|
|
90
|
+
return yaml.parse(fs.readFileSync(filePath, 'utf8'))
|
|
85
91
|
} catch (e) {
|
|
86
92
|
console.error(`Error reading YAML file at ${filePath}:`, e)
|
|
87
93
|
return null
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
|
|
91
|
-
function
|
|
92
|
-
let stat
|
|
93
|
-
try {
|
|
94
|
-
stat = fs.statSync(filePath)
|
|
95
|
-
} catch (e) {
|
|
96
|
-
throw new Error(`Error stating file at ${filePath}: ${e.message}`)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const cached = frontMatterCache.get(filePath)
|
|
100
|
-
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
101
|
-
return { frontMatter: { ...cached.value.frontMatter }, content: cached.value.content }
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let content = ''
|
|
105
|
-
try {
|
|
106
|
-
content = fs.readFileSync(filePath, 'utf8')
|
|
107
|
-
} catch (e) {
|
|
108
|
-
throw new Error(`Error reading file at ${filePath}: ${e.message}`)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!content) {
|
|
112
|
-
throw new Error(`File at ${filePath} is empty`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const frontMatterRegex = /^\s*---\s*[\r\n]+([\s\S]*?)\s*---\s*[\r\n]+/
|
|
116
|
-
const match = content.match(frontMatterRegex)
|
|
117
|
-
|
|
118
|
-
if (!match) {
|
|
119
|
-
const value = { frontMatter: {}, content }
|
|
120
|
-
frontMatterCache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, value })
|
|
121
|
-
return { frontMatter: {}, content }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
let frontMatter = {}
|
|
125
|
-
try {
|
|
126
|
-
frontMatter = require('yaml').parse(match[1])
|
|
127
|
-
} catch (e) {
|
|
128
|
-
throw new Error(`Error parsing front matter in file at ${filePath}: ${e.message}`)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const contentWithoutFrontMatter = content.slice(match[0].length)
|
|
132
|
-
const value = { frontMatter, content: contentWithoutFrontMatter }
|
|
133
|
-
frontMatterCache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, value })
|
|
134
|
-
return { frontMatter: { ...frontMatter }, content: contentWithoutFrontMatter }
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function clearFrontMatterCache(filePath) {
|
|
138
|
-
if (!filePath) {
|
|
139
|
-
frontMatterCache.clear()
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
frontMatterCache.delete(filePath)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function readDataFile(filePath) {
|
|
97
|
+
export function readDataFile(filePath) {
|
|
146
98
|
if (/(\.json)$/i.test(filePath)) return readJsonFile(filePath)
|
|
147
99
|
if (/(\.ya?ml)$/i.test(filePath)) return readYamlFile(filePath)
|
|
148
100
|
return fs.readFileSync(filePath, 'utf8')
|
|
149
101
|
}
|
|
150
102
|
|
|
151
|
-
function deleteDirectoryContents(directory) {
|
|
103
|
+
export function deleteDirectoryContents(directory) {
|
|
152
104
|
if (!pathExists(directory)) return
|
|
153
105
|
const files = fs.readdirSync(directory)
|
|
154
106
|
|
|
@@ -157,21 +109,19 @@ function deleteDirectoryContents(directory) {
|
|
|
157
109
|
const stat = fs.statSync(filePath)
|
|
158
110
|
|
|
159
111
|
if (stat.isDirectory()) {
|
|
160
|
-
|
|
161
|
-
fs.rmdirSync(filePath)
|
|
112
|
+
fs.rmSync(filePath, { recursive: true })
|
|
162
113
|
} else {
|
|
163
114
|
fs.unlinkSync(filePath)
|
|
164
115
|
}
|
|
165
116
|
}
|
|
166
117
|
}
|
|
167
118
|
|
|
168
|
-
function deleteDirectory(directory) {
|
|
119
|
+
export function deleteDirectory(directory) {
|
|
169
120
|
if (!pathExists(directory)) return
|
|
170
|
-
|
|
171
|
-
fs.rmdirSync(directory)
|
|
121
|
+
fs.rmSync(directory, { recursive: true })
|
|
172
122
|
}
|
|
173
123
|
|
|
174
|
-
function copyDirectory(src, dest) {
|
|
124
|
+
export function copyDirectory(src, dest) {
|
|
175
125
|
if (!pathExists(src)) return
|
|
176
126
|
|
|
177
127
|
if (!pathIsDirectory(src)) {
|
|
@@ -195,80 +145,13 @@ function copyDirectory(src, dest) {
|
|
|
195
145
|
}
|
|
196
146
|
}
|
|
197
147
|
|
|
198
|
-
function
|
|
199
|
-
let regexString = ''
|
|
200
|
-
const charMap = {
|
|
201
|
-
'*': '[^\\/\\\\]*',
|
|
202
|
-
'?': '[^\\/\\\\]',
|
|
203
|
-
'!': '^',
|
|
204
|
-
'{': '(',
|
|
205
|
-
'}': ')',
|
|
206
|
-
',': '|',
|
|
207
|
-
'.': '\\.',
|
|
208
|
-
'-': '\\-',
|
|
209
|
-
// eslint-disable-next-line quote-props
|
|
210
|
-
'$': '\\$',
|
|
211
|
-
'+': '\\+',
|
|
212
|
-
'/': '\\/',
|
|
213
|
-
'\\': '\\\\'
|
|
214
|
-
}
|
|
215
|
-
let insideSquareBrackets = false
|
|
216
|
-
let insideCurlyBraces = false
|
|
217
|
-
|
|
218
|
-
for (let i = 0; i < glob.length; i++) {
|
|
219
|
-
const char = glob[i]
|
|
220
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
221
|
-
if (charMap.hasOwnProperty(char)) {
|
|
222
|
-
let currentChar = charMap[char]
|
|
223
|
-
switch (char) {
|
|
224
|
-
case '{':
|
|
225
|
-
insideCurlyBraces = true
|
|
226
|
-
break
|
|
227
|
-
case '}':
|
|
228
|
-
insideCurlyBraces = false
|
|
229
|
-
break
|
|
230
|
-
case '!':
|
|
231
|
-
if (!insideSquareBrackets) currentChar = '\\!' // Escape '!' outside square brackets
|
|
232
|
-
break
|
|
233
|
-
case ',':
|
|
234
|
-
if (!insideCurlyBraces) currentChar = '\\,' // Escape ',' outside curly braces
|
|
235
|
-
break
|
|
236
|
-
case '-':
|
|
237
|
-
if (insideSquareBrackets) currentChar = char // Don't escape '-' inside square brackets
|
|
238
|
-
break
|
|
239
|
-
}
|
|
240
|
-
regexString += currentChar
|
|
241
|
-
} else {
|
|
242
|
-
switch (char) {
|
|
243
|
-
case '[':
|
|
244
|
-
insideSquareBrackets = true
|
|
245
|
-
break
|
|
246
|
-
case ']':
|
|
247
|
-
insideSquareBrackets = false
|
|
248
|
-
break
|
|
249
|
-
}
|
|
250
|
-
regexString += char
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
let re = null
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
re = new RegExp(regexString)
|
|
258
|
-
} catch (e) {
|
|
259
|
-
// Invalid regex
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return re
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function removeDirNavWildcards(filePath) {
|
|
148
|
+
export function stripDirNavSegments(filePath) {
|
|
266
149
|
return path.normalize(filePath).replace(/(\.\.\/|\.\/|\/\.\.|\.\.\\|\.\\|\\\.\.)/g, '')
|
|
267
150
|
}
|
|
268
151
|
|
|
269
|
-
function pathContainsPathSegment(filePath, segment) {
|
|
270
|
-
segment =
|
|
271
|
-
if (
|
|
152
|
+
export function pathContainsPathSegment(filePath, segment) {
|
|
153
|
+
segment = stripDirNavSegments(segment)
|
|
154
|
+
if (hasMagic(segment)) {
|
|
272
155
|
segment = convertGlobToRegex(segment)
|
|
273
156
|
if (!segment) return false
|
|
274
157
|
return segment.test(filePath)
|
|
@@ -277,7 +160,7 @@ function pathContainsPathSegment(filePath, segment) {
|
|
|
277
160
|
}
|
|
278
161
|
}
|
|
279
162
|
|
|
280
|
-
function doesFileBelongToPath(filePath, configPaths) {
|
|
163
|
+
export function doesFileBelongToPath(filePath, configPaths) {
|
|
281
164
|
if (!configPaths) return false
|
|
282
165
|
if (!Array.isArray(configPaths)) configPaths = [configPaths]
|
|
283
166
|
for (const configPath of configPaths) {
|
|
@@ -294,26 +177,3 @@ function doesFileBelongToPath(filePath, configPaths) {
|
|
|
294
177
|
}
|
|
295
178
|
return false
|
|
296
179
|
}
|
|
297
|
-
|
|
298
|
-
module.exports = {
|
|
299
|
-
pathExists,
|
|
300
|
-
pathIsDirectory,
|
|
301
|
-
mkDir,
|
|
302
|
-
mkPath,
|
|
303
|
-
pathForFile,
|
|
304
|
-
insertMinSuffix,
|
|
305
|
-
buildStyleOutputFilePath,
|
|
306
|
-
buildScriptOutputFilePath,
|
|
307
|
-
fillBannerTemplate,
|
|
308
|
-
buildTime,
|
|
309
|
-
fileSize,
|
|
310
|
-
readJsonFile,
|
|
311
|
-
readYamlFile,
|
|
312
|
-
readDataFile,
|
|
313
|
-
deleteDirectoryContents,
|
|
314
|
-
deleteDirectory,
|
|
315
|
-
copyDirectory,
|
|
316
|
-
doesFileBelongToPath,
|
|
317
|
-
parseFrontMatter,
|
|
318
|
-
clearFrontMatterCache
|
|
319
|
-
}
|
package/lib/utils/log.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import PrintStyle from 'printstyle'
|
|
2
|
+
|
|
3
|
+
const pstyle = new PrintStyle()
|
|
4
|
+
|
|
5
|
+
const TAG_COLORS = {
|
|
6
|
+
script: 'yellowBright',
|
|
7
|
+
reactor: 'yellowBright',
|
|
8
|
+
style: 'magentaBright',
|
|
9
|
+
markup: 'cyanBright',
|
|
10
|
+
copy: 'green',
|
|
11
|
+
info: 'blue',
|
|
12
|
+
error: 'redBright'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_COLOR = 'white'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Object} options
|
|
19
|
+
* @param {string} options.tag - Tag label (e.g. 'script', 'style', 'markup', 'reactor', 'copy', 'info')
|
|
20
|
+
* @param {boolean} [options.error] - Whether this is an error message
|
|
21
|
+
* @param {string} options.text - Main message text
|
|
22
|
+
* @param {string} [options.link] - File path or URL to display underlined
|
|
23
|
+
* @param {string} [options.size] - File size string
|
|
24
|
+
* @param {string} [options.time] - Build time string
|
|
25
|
+
*/
|
|
26
|
+
export default function log({ tag, error, text, link, size, time }) {
|
|
27
|
+
if (tag === 'error') error = true
|
|
28
|
+
const color = TAG_COLORS[tag] || DEFAULT_COLOR
|
|
29
|
+
let msg = pstyle.paint(`{${color}.bold|[${tag}]}`)
|
|
30
|
+
|
|
31
|
+
if (error) {
|
|
32
|
+
msg += pstyle.paint(' {redBright.bold|[error]}')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
msg += pstyle.paint(` {dim|${text}}`)
|
|
36
|
+
|
|
37
|
+
if (link) {
|
|
38
|
+
msg += pstyle.paint(` {italic.underline|${link}}`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (size) {
|
|
42
|
+
msg += pstyle.paint(` {greenBright|${size}}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (time) {
|
|
46
|
+
msg += pstyle.paint(` {green|(${time})}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (error) {
|
|
50
|
+
msg += pstyle.bell
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (error) {
|
|
54
|
+
console.error(msg)
|
|
55
|
+
} else {
|
|
56
|
+
console.log(msg)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const bell = pstyle.bell
|
|
61
|
+
|
|
62
|
+
export function styledLog(template) {
|
|
63
|
+
console.log(pstyle.paint(template))
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poops",
|
|
3
3
|
"description": "Straightforward, no-bullshit bundler for the web.",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"main": "poops.js",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
@@ -14,7 +15,8 @@
|
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"poops.js",
|
|
17
|
-
"lib/"
|
|
18
|
+
"lib/",
|
|
19
|
+
"!lib/**/__tests__/"
|
|
18
20
|
],
|
|
19
21
|
"author": "Stamat <@stamat> (http://stamat.info)",
|
|
20
22
|
"homepage": "https://github.com/stamat/poops",
|
|
@@ -24,36 +26,60 @@
|
|
|
24
26
|
"javascript",
|
|
25
27
|
"sass",
|
|
26
28
|
"nunjucks",
|
|
29
|
+
"liquid",
|
|
27
30
|
"esbuild",
|
|
28
31
|
"dart-sass",
|
|
32
|
+
"tailwindcss",
|
|
29
33
|
"toolchain-script",
|
|
30
34
|
"static-site-generator"
|
|
31
35
|
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"postcss": "^8.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"postcss": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
32
44
|
"scripts": {
|
|
33
45
|
"build": "node ./poops.js -b",
|
|
34
|
-
"lint": "eslint ./poops.js"
|
|
46
|
+
"lint": "eslint ./poops.js",
|
|
47
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
35
48
|
},
|
|
36
49
|
"dependencies": {
|
|
50
|
+
"argoyle": "^1.0.0",
|
|
51
|
+
"book-of-spells": "^1.0.32",
|
|
37
52
|
"chokidar": "^3.5.3",
|
|
38
53
|
"connect": "^3.7.0",
|
|
39
|
-
"
|
|
54
|
+
"dayjs": "^1.11.19",
|
|
40
55
|
"esbuild": "^0.25.0",
|
|
41
|
-
"glob": "^
|
|
56
|
+
"glob": "^13.0.6",
|
|
57
|
+
"highlight.js": "^11.11.1",
|
|
58
|
+
"liquidjs": "^10.24.0",
|
|
42
59
|
"livereload": "^0.9.3",
|
|
43
60
|
"marked": "^9.0.3",
|
|
44
|
-
"moment": "^2.29.4",
|
|
45
61
|
"nunjucks": "^3.2.4",
|
|
46
62
|
"portscanner": "^2.2.0",
|
|
63
|
+
"printstyle": "^1.0.0",
|
|
47
64
|
"sass": "^1.63.4",
|
|
65
|
+
"sass-path-resolver": "^1.0.2",
|
|
66
|
+
"sass-token-importer": "^1.0.0",
|
|
48
67
|
"serve-static": "^1.15.0",
|
|
49
68
|
"yaml": "^2.3.1"
|
|
50
69
|
},
|
|
51
70
|
"devDependencies": {
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"eslint
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
71
|
+
"@jest/globals": "^30.2.0",
|
|
72
|
+
"@tailwindcss/postcss": "^4.2.1",
|
|
73
|
+
"eslint": "^9.39.3",
|
|
74
|
+
"jest": "^30.2.0",
|
|
75
|
+
"neostandard": "^0.12.2",
|
|
76
|
+
"postcss": "^8.5.8",
|
|
77
|
+
"react": "^19.2.4",
|
|
78
|
+
"react-dom": "^19.2.4",
|
|
79
|
+
"sulphuris": "^2.0.0",
|
|
80
|
+
"tailwindcss": "^4.2.1"
|
|
81
|
+
},
|
|
82
|
+
"overrides": {
|
|
83
|
+
"minimatch": ">=10.2.1"
|
|
58
84
|
}
|
|
59
85
|
}
|