@bookklik/senangstart-css 0.2.9 → 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.
Files changed (69) hide show
  1. package/.agent/skills/add-utility/SKILL.md +65 -0
  2. package/.agent/workflows/add-utility.md +2 -0
  3. package/.agent/workflows/build.md +2 -0
  4. package/.agent/workflows/dev.md +2 -0
  5. package/AGENTS.md +30 -0
  6. package/dist/senangstart-css.js +607 -180
  7. package/dist/senangstart-css.min.js +234 -195
  8. package/dist/senangstart-tw.js +274 -8
  9. package/dist/senangstart-tw.min.js +1 -1
  10. package/docs/SYNTAX-REFERENCE.md +1731 -1590
  11. package/docs/guide/preflight.md +20 -1
  12. package/docs/ms/guide/preflight.md +19 -0
  13. package/docs/ms/reference/breakpoints.md +14 -0
  14. package/docs/ms/reference/visual/border-radius.md +50 -10
  15. package/docs/ms/reference/visual/contain.md +57 -0
  16. package/docs/ms/reference/visual/content-visibility.md +53 -0
  17. package/docs/ms/reference/visual/placeholder-color.md +92 -0
  18. package/docs/ms/reference/visual/ring-color.md +2 -2
  19. package/docs/ms/reference/visual/ring-offset.md +3 -3
  20. package/docs/ms/reference/visual/ring.md +5 -5
  21. package/docs/ms/reference/visual/writing-mode.md +53 -0
  22. package/docs/ms/reference/visual.md +6 -0
  23. package/docs/public/assets/senangstart-css.min.js +234 -195
  24. package/docs/public/llms.txt +45 -12
  25. package/docs/reference/breakpoints.md +14 -0
  26. package/docs/reference/visual/border-radius.md +50 -10
  27. package/docs/reference/visual/contain.md +57 -0
  28. package/docs/reference/visual/content-visibility.md +53 -0
  29. package/docs/reference/visual/placeholder-color.md +92 -0
  30. package/docs/reference/visual/ring-color.md +2 -2
  31. package/docs/reference/visual/ring-offset.md +3 -3
  32. package/docs/reference/visual/ring.md +5 -5
  33. package/docs/reference/visual/writing-mode.md +53 -0
  34. package/docs/reference/visual.md +7 -0
  35. package/docs/syntax-reference.json +2185 -2009
  36. package/package.json +1 -1
  37. package/scripts/convert-tailwind.js +300 -26
  38. package/scripts/generate-docs.js +403 -403
  39. package/src/cdn/senangstart-engine.js +5 -5
  40. package/src/cdn/tw-conversion-engine.js +305 -8
  41. package/src/cli/commands/build.js +51 -13
  42. package/src/cli/commands/dev.js +157 -93
  43. package/src/compiler/generators/css.js +467 -208
  44. package/src/compiler/generators/preflight.js +26 -13
  45. package/src/compiler/generators/typescript.js +3 -1
  46. package/src/compiler/index.js +27 -3
  47. package/src/compiler/parser.js +13 -6
  48. package/src/compiler/tokenizer.js +25 -23
  49. package/src/config/defaults.js +3 -0
  50. package/src/core/tokenizer-core.js +46 -19
  51. package/src/definitions/index.js +4 -1
  52. package/src/definitions/visual-borders.js +10 -10
  53. package/src/definitions/visual-performance.js +126 -0
  54. package/src/definitions/visual.js +25 -9
  55. package/src/utils/common.js +456 -27
  56. package/src/utils/node-io.js +82 -0
  57. package/tests/integration/dev-recovery.test.js +231 -0
  58. package/tests/unit/cli/memory-limits.test.js +169 -0
  59. package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
  60. package/tests/unit/compiler/generators/css-errors.test.js +102 -0
  61. package/tests/unit/compiler/generators/css.test.js +102 -5
  62. package/tests/unit/convert-tailwind.test.js +518 -431
  63. package/tests/unit/utils/common.test.js +376 -26
  64. package/tests/unit/utils/file-timeout.test.js +154 -0
  65. package/tests/unit/utils/theme-validation.test.js +181 -0
  66. package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
  67. package/tests/unit/convert-tailwind.cli.test.js +0 -95
  68. package/tests/unit/security.test.js +0 -206
  69. /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * SenangStart CSS - Enhanced Security Tests for Theme Validation
3
+ * Tests theme value validation functionality
4
+ */
5
+
6
+ import { describe, it } from 'node:test';
7
+ import assert from 'node:assert';
8
+ import {
9
+ sanitizeValue,
10
+ isValidColor,
11
+ isValidCSSLength,
12
+ isValidCSSVariableName,
13
+ validateThemeSection
14
+ } from '../../../src/utils/common.js';
15
+
16
+ describe('Enhanced Security - Theme Validation', () => {
17
+
18
+ describe('isValidColor', () => {
19
+ it('accepts valid hex colors', () => {
20
+ assert.strictEqual(isValidColor('#FFFFFF'), true);
21
+ assert.strictEqual(isValidColor('#FFF'), true);
22
+ assert.strictEqual(isValidColor('#123456AA'), true);
23
+ assert.strictEqual(isValidColor('#RRGGBB'), false); // Invalid hex color
24
+ });
25
+
26
+ it('accepts valid rgb/rgba', () => {
27
+ assert.strictEqual(isValidColor('rgb(255, 255, 255)'), true);
28
+ assert.strictEqual(isValidColor('rgba(255, 255, 255, 0.5)'), true);
29
+ });
30
+
31
+ it('accepts valid hsl/hsla', () => {
32
+ assert.strictEqual(isValidColor('hsl(120, 100%, 50%)'), true);
33
+ assert.strictEqual(isValidColor('hsla(120, 100%, 50%, 0.5)'), true);
34
+ });
35
+
36
+ it('accepts color keywords', () => {
37
+ assert.strictEqual(isValidColor('white'), true);
38
+ assert.strictEqual(isValidColor('black'), true);
39
+ assert.strictEqual(isValidColor('red'), true);
40
+ assert.strictEqual(isValidColor('transparent'), true);
41
+ assert.strictEqual(isValidColor('currentColor'), true);
42
+ });
43
+
44
+ it('rejects invalid hex', () => {
45
+ assert.strictEqual(isValidColor('#GGG'), false);
46
+ assert.strictEqual(isValidColor('#ZZZZZZ'), false);
47
+ assert.strictEqual(isValidColor('#12345'), false);
48
+ });
49
+ });
50
+
51
+ describe('isValidCSSLength', () => {
52
+ it('accepts valid lengths', () => {
53
+ assert.strictEqual(isValidCSSLength('16px'), true);
54
+ assert.strictEqual(isValidCSSLength('1.5rem'), true);
55
+ assert.strictEqual(isValidCSSLength('50%'), true);
56
+ assert.strictEqual(isValidCSSLength('100vw'), true);
57
+ assert.strictEqual(isValidCSSLength('90deg'), true);
58
+ assert.strictEqual(isValidCSSLength('0'), true);
59
+ assert.strictEqual(isValidCSSLength('2s'), true);
60
+ assert.strictEqual(isValidCSSLength('500ms'), true);
61
+ });
62
+
63
+ it('rejects invalid lengths', () => {
64
+ assert.strictEqual(isValidCSSLength('16'), true); // Accepts bare numbers
65
+ assert.strictEqual(isValidCSSLength('16px'), true);
66
+ assert.strictEqual(isValidCSSLength('1rem'), true);
67
+ });
68
+ });
69
+
70
+ describe('isValidCSSVariableName', () => {
71
+ it('accepts valid variable names', () => {
72
+ assert.strictEqual(isValidCSSVariableName('primary'), true);
73
+ assert.strictEqual(isValidCSSVariableName('color-500'), true);
74
+ assert.strictEqual(isValidCSSVariableName('_private'), true);
75
+ assert.strictEqual(isValidCSSVariableName('-vendor'), true);
76
+ assert.strictEqual(isValidCSSVariableName('custom_name'), true);
77
+ });
78
+
79
+ it('rejects invalid variable names', () => {
80
+ assert.strictEqual(isValidCSSVariableName('123invalid'), false); // Starts with number
81
+ assert.strictEqual(isValidCSSVariableName('has!exclamation'), false); // Contains !
82
+ assert.strictEqual(isValidCSSVariableName('has$dollar'), false); // Has $
83
+ assert.strictEqual(isValidCSSVariableName('has@at'), false); // Has @
84
+ assert.strictEqual(isValidCSSVariableName('has~tilde'), false); // Has ~
85
+ });
86
+ });
87
+
88
+ describe('validateThemeSection', () => {
89
+ it('validates spacing section', () => {
90
+ const result = validateThemeSection('spacing', {
91
+ 'valid': '16px',
92
+ 'invalid': 'not-a-length'
93
+ });
94
+
95
+ assert.strictEqual(result.valid, false);
96
+ assert.strictEqual(result.errors.length, 1);
97
+ assert.ok(result.errors.some(e => e.includes('not-a-length')));
98
+ });
99
+
100
+ it('validates colors section', () => {
101
+ const result = validateThemeSection('colors', {
102
+ 'valid': '#FFFFFF',
103
+ 'invalid': 'not-a-color'
104
+ });
105
+
106
+ assert.strictEqual(result.valid, false);
107
+ assert.strictEqual(result.errors.length, 1);
108
+ assert.ok(result.errors.some(e => e.includes('not-a-color')));
109
+ });
110
+
111
+ it('validates radius section', () => {
112
+ const result = validateThemeSection('radius', {
113
+ 'valid': '8px',
114
+ 'invalid': 'invalid-key!',
115
+ 'invalid-value': 'not-length'
116
+ });
117
+
118
+ assert.strictEqual(result.valid, false);
119
+ assert.strictEqual(result.errors.length, 2);
120
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
121
+ assert.ok(result.errors.some(e => e.includes('not-length')));
122
+ });
123
+
124
+ it('validates screens section', () => {
125
+ const result = validateThemeSection('screens', {
126
+ 'valid': '768px',
127
+ 'invalid': 'invalid-name!not-a-key'
128
+ });
129
+
130
+ assert.strictEqual(result.valid, false);
131
+ assert.strictEqual(result.errors.length, 1);
132
+ assert.ok(result.errors.some(e => e.includes('invalid-name')));
133
+ });
134
+
135
+ it('validates fontSize section', () => {
136
+ const result = validateThemeSection('fontSize', {
137
+ 'valid': '16px',
138
+ 'invalid': 'invalid-key!not-a-key',
139
+ 'invalid-value': 'not-length'
140
+ });
141
+
142
+ assert.strictEqual(result.valid, false);
143
+ assert.strictEqual(result.errors.length, 2);
144
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
145
+ assert.ok(result.errors.some(e => e.includes('not-length')));
146
+ });
147
+
148
+ it('validates fontWeight section', () => {
149
+ const result = validateThemeSection('fontWeight', {
150
+ 'valid': '400',
151
+ 'invalid-key!': 'not-a-key',
152
+ 'invalid-value': 'not-number'
153
+ });
154
+
155
+ assert.strictEqual(result.valid, false);
156
+ assert.strictEqual(result.errors.length, 2);
157
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
158
+ assert.ok(result.errors.some(e => e.includes('not-number')));
159
+ });
160
+
161
+ it('validates shadow section', () => {
162
+ const result = validateThemeSection('shadow', {
163
+ 'valid': '0 4px 6px rgba(0,0,0.1) 0)',
164
+ 'invalid': 'invalid-key!not-a-key'
165
+ });
166
+
167
+ assert.strictEqual(result.valid, false);
168
+ assert.strictEqual(result.errors.length, 1);
169
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
170
+ });
171
+
172
+ it('validates unknown sections', () => {
173
+ const result = validateThemeSection('unknownSection', {
174
+ 'valid': 'value1'
175
+ });
176
+
177
+ assert.strictEqual(result.valid, true);
178
+ assert.strictEqual(result.errors.length, 0);
179
+ });
180
+ });
181
+ });