@bookklik/senangstart-css 0.2.10 → 0.2.12
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/.agent/skills/add-utility/SKILL.md +65 -0
- package/.agent/workflows/add-utility.md +2 -0
- package/.agent/workflows/build.md +2 -0
- package/.agent/workflows/dev.md +2 -0
- package/AGENTS.md +30 -0
- package/dist/senangstart-css.js +362 -151
- package/dist/senangstart-css.min.js +175 -174
- package/dist/senangstart-tw.js +4 -4
- package/dist/senangstart-tw.min.js +1 -1
- package/docs/ms/reference/visual/ring-color.md +2 -2
- package/docs/ms/reference/visual/ring-offset.md +3 -3
- package/docs/ms/reference/visual/ring.md +5 -5
- package/docs/public/assets/senangstart-css.min.js +175 -174
- package/docs/public/llms.txt +10 -10
- package/docs/reference/visual/ring-color.md +2 -2
- package/docs/reference/visual/ring-offset.md +3 -3
- package/docs/reference/visual/ring.md +5 -5
- package/package.json +1 -1
- package/src/cdn/tw-conversion-engine.js +4 -4
- package/src/cli/commands/build.js +42 -14
- package/src/cli/commands/dev.js +157 -93
- package/src/compiler/generators/css.js +371 -199
- package/src/compiler/tokenizer.js +25 -23
- package/src/core/tokenizer-core.js +46 -19
- package/src/definitions/visual-borders.js +10 -10
- package/src/utils/common.js +456 -39
- package/src/utils/node-io.js +82 -0
- package/tests/integration/dev-recovery.test.js +231 -0
- package/tests/unit/cli/memory-limits.test.js +169 -0
- package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
- package/tests/unit/compiler/generators/css-errors.test.js +102 -0
- package/tests/unit/convert-tailwind.test.js +518 -442
- package/tests/unit/utils/common.test.js +376 -26
- package/tests/unit/utils/file-timeout.test.js +154 -0
- package/tests/unit/utils/theme-validation.test.js +181 -0
- package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
- package/tests/unit/convert-tailwind.cli.test.js +0 -95
- package/tests/unit/security.test.js +0 -206
- /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
package/docs/public/llms.txt
CHANGED
|
@@ -1247,28 +1247,28 @@ export default {
|
|
|
1247
1247
|
**Syntax:** `visual="ring:[size]"`
|
|
1248
1248
|
|
|
1249
1249
|
* `none`: No ring (`box-shadow: 0 0 0 0 transparent;`)
|
|
1250
|
-
* `thin`: Thin ring (1px)
|
|
1251
|
-
* `regular`: Regular ring (2px)
|
|
1252
|
-
* `small`: Small ring (4px)
|
|
1253
|
-
* `medium`: Medium ring (6px)
|
|
1254
|
-
* `big`: Big ring (8px)
|
|
1250
|
+
* `thin`: Thin ring (1px)
|
|
1251
|
+
* `regular`: Regular ring (2px)
|
|
1252
|
+
* `small`: Small ring (4px)
|
|
1253
|
+
* `medium`: Medium ring (6px)
|
|
1254
|
+
* `big`: Big ring (8px)
|
|
1255
1255
|
|
|
1256
1256
|
## ring-color
|
|
1257
1257
|
> Set ring color
|
|
1258
1258
|
|
|
1259
1259
|
**Syntax:** `visual="ring-color:[color]"`
|
|
1260
1260
|
|
|
1261
|
-
* `primary`: Primary ring color (`--ring-color: var(--c-primary);`)
|
|
1262
|
-
* `blue-500`: Blue ring color (`--ring-color: var(--c-blue-500);`)
|
|
1261
|
+
* `primary`: Primary ring color (`--ss-ring-color: var(--c-primary);`)
|
|
1262
|
+
* `blue-500`: Blue ring color (`--ss-ring-color: var(--c-blue-500);`)
|
|
1263
1263
|
|
|
1264
1264
|
## ring-offset
|
|
1265
1265
|
> Add gap between ring and element
|
|
1266
1266
|
|
|
1267
1267
|
**Syntax:** `visual="ring-offset:[size]"`
|
|
1268
1268
|
|
|
1269
|
-
* `0`: No offset (`--ring-offset: 0px;`)
|
|
1270
|
-
* `2`: 2px offset (`--ring-offset: 2px;`)
|
|
1271
|
-
* `4`: 4px offset (`--ring-offset: 4px;`)
|
|
1269
|
+
* `0`: No offset (`--ss-ring-offset-width: 0px;`)
|
|
1270
|
+
* `2`: 2px offset (`--ss-ring-offset-width: 2px;`)
|
|
1271
|
+
* `4`: 4px offset (`--ss-ring-offset-width: 4px;`)
|
|
1272
1272
|
|
|
1273
1273
|
## scroll-behavior
|
|
1274
1274
|
> Set scroll behavior
|
|
@@ -11,8 +11,8 @@ visual="ring-color:[color]"
|
|
|
11
11
|
|
|
12
12
|
| Value | CSS Output | Description |
|
|
13
13
|
|-------|------------|-------------|
|
|
14
|
-
| `primary` | `--ring-color: var(--c-primary)` | Primary ring color |
|
|
15
|
-
| `blue-500` | `--ring-color: var(--c-blue-500)` | Blue ring color |
|
|
14
|
+
| `primary` | `--ss-ring-color: var(--c-primary)` | Primary ring color |
|
|
15
|
+
| `blue-500` | `--ss-ring-color: var(--c-blue-500)` | Blue ring color |
|
|
16
16
|
|
|
17
17
|
## Examples
|
|
18
18
|
|
|
@@ -11,9 +11,9 @@ visual="ring-offset:[size]"
|
|
|
11
11
|
|
|
12
12
|
| Value | CSS Output | Description |
|
|
13
13
|
|-------|------------|-------------|
|
|
14
|
-
| `0` | `--ring-offset: 0px` | No offset |
|
|
15
|
-
| `2` | `--ring-offset: 2px` | 2px offset |
|
|
16
|
-
| `4` | `--ring-offset: 4px` | 4px offset |
|
|
14
|
+
| `0` | `--ss-ring-offset-width: 0px` | No offset |
|
|
15
|
+
| `2` | `--ss-ring-offset-width: 2px` | 2px offset |
|
|
16
|
+
| `4` | `--ss-ring-offset-width: 4px` | 4px offset |
|
|
17
17
|
|
|
18
18
|
## Examples
|
|
19
19
|
|
|
@@ -12,11 +12,11 @@ visual="ring:[size]"
|
|
|
12
12
|
| Value | CSS Output | Description |
|
|
13
13
|
|-------|------------|-------------|
|
|
14
14
|
| `none` | `box-shadow: 0 0 0 0 transparent` | No ring |
|
|
15
|
-
| `thin` | `box-shadow: 0 0 0 1px var(--ring-color)` | Thin ring (1px) |
|
|
16
|
-
| `regular` | `box-shadow: 0 0 0 2px var(--ring-color)` | Regular ring (2px) |
|
|
17
|
-
| `small` | `box-shadow: 0 0 0 4px var(--ring-color)` | Small ring (4px) |
|
|
18
|
-
| `medium` | `box-shadow: 0 0 0 6px var(--ring-color)` | Medium ring (6px) |
|
|
19
|
-
| `big` | `box-shadow: 0 0 0 8px var(--ring-color)` | Big ring (8px) |
|
|
15
|
+
| `thin` | `box-shadow: var(--ring-inset) 0 0 0 1px var(--ss-ring-color)` | Thin ring (1px) |
|
|
16
|
+
| `regular` | `box-shadow: var(--ring-inset) 0 0 0 2px var(--ss-ring-color)` | Regular ring (2px) |
|
|
17
|
+
| `small` | `box-shadow: var(--ring-inset) 0 0 0 4px var(--ss-ring-color)` | Small ring (4px) |
|
|
18
|
+
| `medium` | `box-shadow: var(--ring-inset) 0 0 0 6px var(--ss-ring-color)` | Medium ring (6px) |
|
|
19
|
+
| `big` | `box-shadow: var(--ring-inset) 0 0 0 8px var(--ss-ring-color)` | Big ring (8px) |
|
|
20
20
|
|
|
21
21
|
## Examples
|
|
22
22
|
|
package/package.json
CHANGED
|
@@ -330,7 +330,7 @@ const visualKeywords = {
|
|
|
330
330
|
|
|
331
331
|
function getSpacing(value, exact) {
|
|
332
332
|
// Check if it's already an arbitrary value with brackets
|
|
333
|
-
if (value.startsWith('[') && value.endsWith(']')) {
|
|
333
|
+
if (value && value.startsWith('[') && value.endsWith(']')) {
|
|
334
334
|
return value; // Return as-is, don't double-wrap
|
|
335
335
|
}
|
|
336
336
|
if (exact) {
|
|
@@ -548,7 +548,7 @@ function convertClass(twClass, exact) {
|
|
|
548
548
|
const side = marginMatch[1] ? "-" + marginMatch[1] : "";
|
|
549
549
|
let val = getSpacing(marginMatch[2], exact);
|
|
550
550
|
|
|
551
|
-
if (isNeg) {
|
|
551
|
+
if (isNeg && val) {
|
|
552
552
|
if (val.startsWith('[') && val.endsWith(']')) {
|
|
553
553
|
// Handle arbitrary value: [10px] -> [-10px]
|
|
554
554
|
const inner = val.slice(1, -1);
|
|
@@ -646,7 +646,7 @@ function convertClass(twClass, exact) {
|
|
|
646
646
|
if (positionMatch) {
|
|
647
647
|
const prop = positionMatch[1];
|
|
648
648
|
let val = positionMatch[2];
|
|
649
|
-
if (val.startsWith('[') && val.endsWith(']')) {
|
|
649
|
+
if (val && val.startsWith('[') && val.endsWith(']')) {
|
|
650
650
|
// Keep arbitrary values as-is
|
|
651
651
|
} else if (fractionScale[val]) {
|
|
652
652
|
// Map fractions to semantic names (1/2 → half, etc.)
|
|
@@ -668,7 +668,7 @@ function convertClass(twClass, exact) {
|
|
|
668
668
|
let val = translateMatch[3];
|
|
669
669
|
|
|
670
670
|
// Map fractions and values
|
|
671
|
-
if (val.startsWith('[') && val.endsWith(']')) {
|
|
671
|
+
if (val && val.startsWith('[') && val.endsWith(']')) {
|
|
672
672
|
// Keep arbitrary values as-is, but handle negative
|
|
673
673
|
if (isNeg) {
|
|
674
674
|
const inner = val.slice(1, -1);
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
* One-time compilation of CSS from source files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'fs';
|
|
7
7
|
import { join, dirname, resolve } from 'path';
|
|
8
8
|
import { defaultConfig, mergeConfig } from '../../config/defaults.js';
|
|
9
9
|
import { parseSource } from '../../compiler/parser.js';
|
|
10
|
-
import { tokenizeAll } from '../../compiler/tokenizer.js';
|
|
10
|
+
import { tokenizeAll, tokenizeAllWithBatching } from '../../compiler/tokenizer.js';
|
|
11
11
|
import { generateCSS, minifyCSS } from '../../compiler/generators/css.js';
|
|
12
12
|
import { generateAIContext } from '../../compiler/generators/ai-context.js';
|
|
13
13
|
import { generateTypeScript } from '../../compiler/generators/typescript.js';
|
|
14
14
|
import logger from '../../utils/logger.js';
|
|
15
|
-
import { validateThemeValue } from '../../utils/common.js';
|
|
15
|
+
import { validateThemeValue, getMemoryUsage } from '../../utils/common.js';
|
|
16
|
+
import { readMultipleFilesWithTimeout } from '../../utils/node-io.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Find files matching content patterns
|
|
@@ -119,31 +120,58 @@ export async function build(options = {}) {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
logger.info(`Found ${files.length} source files`);
|
|
122
|
-
|
|
123
|
-
// Parse all files
|
|
123
|
+
|
|
124
|
+
// Parse all files with timeout protection
|
|
124
125
|
const allTokens = {
|
|
125
126
|
layout: new Set(),
|
|
126
127
|
space: new Set(),
|
|
127
128
|
visual: new Set()
|
|
128
129
|
};
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
|
|
131
|
+
let failedFiles = 0;
|
|
132
|
+
const fileReadResults = await readMultipleFilesWithTimeout(files, 5000);
|
|
133
|
+
|
|
134
|
+
for (const { path: filePath, content, error } of fileReadResults) {
|
|
135
|
+
if (error) {
|
|
136
|
+
logger.warn(`Skipping ${filePath}: ${error.message}`);
|
|
137
|
+
failedFiles++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
131
141
|
try {
|
|
132
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
133
142
|
const parsed = parseSource(content);
|
|
134
|
-
|
|
143
|
+
|
|
135
144
|
parsed.layout.forEach(t => allTokens.layout.add(t));
|
|
136
145
|
parsed.space.forEach(t => allTokens.space.add(t));
|
|
137
146
|
parsed.visual.forEach(t => allTokens.visual.add(t));
|
|
138
147
|
} catch (e) {
|
|
139
|
-
logger.warn(`Could not parse ${filePath}`);
|
|
148
|
+
logger.warn(`Could not parse ${filePath}: ${e.message}`);
|
|
149
|
+
failedFiles++;
|
|
140
150
|
}
|
|
141
151
|
}
|
|
142
|
-
|
|
152
|
+
|
|
153
|
+
if (failedFiles > 0) {
|
|
154
|
+
logger.warn(`${failedFiles} file(s) failed to process`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Calculate total token count
|
|
158
|
+
const totalTokens = allTokens.layout.size + allTokens.space.size + allTokens.visual.size;
|
|
159
|
+
logger.info(`Found ${totalTokens} unique token values`);
|
|
160
|
+
|
|
161
|
+
// Check memory usage and decide whether to use batch processing
|
|
162
|
+
const currentMemory = getMemoryUsage();
|
|
163
|
+
const useBatching = totalTokens > 10000 || currentMemory > 200;
|
|
164
|
+
|
|
143
165
|
// Tokenize
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
166
|
+
let tokens;
|
|
167
|
+
if (useBatching) {
|
|
168
|
+
logger.info('Using batch processing for memory protection');
|
|
169
|
+
tokens = await tokenizeAllWithBatching(allTokens, 1000);
|
|
170
|
+
} else {
|
|
171
|
+
tokens = tokenizeAll(allTokens);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
logger.info(`Generated ${tokens.length} tokens`);
|
|
147
175
|
|
|
148
176
|
// Check for invalid tokens
|
|
149
177
|
const invalidTokens = tokens.filter(token => token.error);
|
package/src/cli/commands/dev.js
CHANGED
|
@@ -1,93 +1,157 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SenangStart CSS - Dev Command
|
|
3
|
-
* Watch mode with live compilation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import chokidar from 'chokidar';
|
|
7
|
-
import { build } from './build.js';
|
|
8
|
-
import logger from '../../utils/logger.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Dev command handler - watches files and rebuilds on changes
|
|
12
|
-
*/
|
|
13
|
-
export async function dev(options = {}) {
|
|
14
|
-
logger.watch('Starting development mode...');
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.on('
|
|
86
|
-
logger.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SenangStart CSS - Dev Command
|
|
3
|
+
* Watch mode with live compilation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chokidar from 'chokidar';
|
|
7
|
+
import { build } from './build.js';
|
|
8
|
+
import logger from '../../utils/logger.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Dev command handler - watches files and rebuilds on changes
|
|
12
|
+
*/
|
|
13
|
+
export async function dev(options = {}) {
|
|
14
|
+
logger.watch('Starting development mode...');
|
|
15
|
+
|
|
16
|
+
// Error tracking for cooldown mechanism
|
|
17
|
+
let consecutiveErrors = 0;
|
|
18
|
+
let lastErrorTime = 0;
|
|
19
|
+
const MAX_CONSECUTIVE_ERRORS = 5;
|
|
20
|
+
const COOLDOWN_DURATION = 30000; // 30 seconds
|
|
21
|
+
|
|
22
|
+
// Build lock to prevent overlapping builds
|
|
23
|
+
let buildInProgress = false;
|
|
24
|
+
let pendingBuild = false;
|
|
25
|
+
|
|
26
|
+
async function runBuild() {
|
|
27
|
+
// Check if we're in cooldown
|
|
28
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
29
|
+
const timeSinceLastError = Date.now() - lastErrorTime;
|
|
30
|
+
if (timeSinceLastError < COOLDOWN_DURATION) {
|
|
31
|
+
const cooldownRemaining = Math.ceil((COOLDOWN_DURATION - timeSinceLastError) / 1000);
|
|
32
|
+
logger.warn(`Cooldown active: ${cooldownRemaining}s remaining. Skipping build.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Cooldown expired, reset error count
|
|
36
|
+
consecutiveErrors = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await build(options);
|
|
41
|
+
// Reset error count on successful build
|
|
42
|
+
consecutiveErrors = 0;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
consecutiveErrors++;
|
|
45
|
+
lastErrorTime = Date.now();
|
|
46
|
+
logger.error(`Build failed (${consecutiveErrors}/${MAX_CONSECUTIVE_ERRORS}): ${error.message}`);
|
|
47
|
+
|
|
48
|
+
// Enter cooldown if max errors reached
|
|
49
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
50
|
+
logger.warn(`Maximum consecutive errors (${MAX_CONSECUTIVE_ERRORS}) reached.`);
|
|
51
|
+
logger.warn(`Entering ${COOLDOWN_DURATION / 1000}s cooldown to prevent resource exhaustion.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Initial build
|
|
57
|
+
await runBuild();
|
|
58
|
+
|
|
59
|
+
function createWatcher() {
|
|
60
|
+
// Watch patterns
|
|
61
|
+
const watchPatterns = [
|
|
62
|
+
'./**/*.html',
|
|
63
|
+
'./**/*.htm',
|
|
64
|
+
'./src/**/*.{html,jsx,tsx,vue,svelte}',
|
|
65
|
+
'./pages/**/*.{html,jsx,tsx}',
|
|
66
|
+
'./components/**/*.{html,jsx,tsx,vue,svelte}'
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Ignore patterns
|
|
70
|
+
const ignorePatterns = [
|
|
71
|
+
'**/node_modules/**',
|
|
72
|
+
'**/dist/**',
|
|
73
|
+
'**/.git/**',
|
|
74
|
+
'**/public/**'
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// Create watcher
|
|
78
|
+
const watcher = chokidar.watch(watchPatterns, {
|
|
79
|
+
ignored: ignorePatterns,
|
|
80
|
+
persistent: true,
|
|
81
|
+
ignoreInitial: true
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Handle watcher errors
|
|
85
|
+
watcher.on('error', (error) => {
|
|
86
|
+
logger.error(`Watcher error: ${error.message}`);
|
|
87
|
+
consecutiveErrors++;
|
|
88
|
+
lastErrorTime = Date.now();
|
|
89
|
+
|
|
90
|
+
// Try to restart watcher after error
|
|
91
|
+
logger.info('Attempting to restart watcher...');
|
|
92
|
+
try {
|
|
93
|
+
watcher.close();
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
const newWatcher = createWatcher();
|
|
96
|
+
Object.assign(watcher, newWatcher);
|
|
97
|
+
logger.success('Watcher restarted successfully');
|
|
98
|
+
}, 1000);
|
|
99
|
+
} catch (restartError) {
|
|
100
|
+
logger.error(`Failed to restart watcher: ${restartError.message}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return watcher;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Initialize watcher
|
|
108
|
+
const watcher = createWatcher();
|
|
109
|
+
|
|
110
|
+
// Debounce timer
|
|
111
|
+
let debounceTimer = null;
|
|
112
|
+
|
|
113
|
+
async function debouncedBuild() {
|
|
114
|
+
if (buildInProgress) {
|
|
115
|
+
pendingBuild = true;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
buildInProgress = true;
|
|
120
|
+
|
|
121
|
+
if (debounceTimer) {
|
|
122
|
+
clearTimeout(debounceTimer);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
debounceTimer = setTimeout(async () => {
|
|
126
|
+
logger.watch('Change detected, rebuilding...');
|
|
127
|
+
await runBuild();
|
|
128
|
+
buildInProgress = false;
|
|
129
|
+
|
|
130
|
+
// Handle pending build
|
|
131
|
+
if (pendingBuild) {
|
|
132
|
+
pendingBuild = false;
|
|
133
|
+
debouncedBuild();
|
|
134
|
+
}
|
|
135
|
+
}, 100);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Watch events
|
|
139
|
+
watcher
|
|
140
|
+
.on('change', (path) => {
|
|
141
|
+
logger.info(`Changed: ${path}`);
|
|
142
|
+
debouncedBuild();
|
|
143
|
+
})
|
|
144
|
+
.on('add', (path) => {
|
|
145
|
+
logger.info(`Added: ${path}`);
|
|
146
|
+
debouncedBuild();
|
|
147
|
+
})
|
|
148
|
+
.on('unlink', (path) => {
|
|
149
|
+
logger.info(`Removed: ${path}`);
|
|
150
|
+
debouncedBuild();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
logger.watch('Watching for changes... (Ctrl+C to stop)');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default dev;
|
|
157
|
+
|