juxscript 1.0.131 → 1.1.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.
Files changed (129) hide show
  1. package/README.md +1 -32
  2. package/bin/cli.js +4 -2
  3. package/index.d.ts +200 -0
  4. package/index.js +96 -22
  5. package/juxconfig.example.js +58 -63
  6. package/lib/components/alert.ts +200 -0
  7. package/lib/components/app.ts +247 -0
  8. package/lib/components/badge.ts +101 -0
  9. package/lib/components/base/BaseComponent.ts +421 -0
  10. package/lib/components/base/FormInput.ts +227 -0
  11. package/lib/components/button.ts +178 -0
  12. package/lib/components/card.ts +173 -0
  13. package/lib/components/chart.ts +231 -0
  14. package/lib/components/checkbox.ts +242 -0
  15. package/lib/components/code.ts +123 -0
  16. package/lib/components/container.ts +140 -0
  17. package/lib/components/data.ts +135 -0
  18. package/lib/components/datepicker.ts +234 -0
  19. package/lib/components/dialog.ts +172 -0
  20. package/lib/components/divider.ts +100 -0
  21. package/lib/components/dropdown.ts +186 -0
  22. package/lib/components/element.ts +267 -0
  23. package/lib/components/fileupload.ts +309 -0
  24. package/lib/components/grid.ts +291 -0
  25. package/lib/components/guard.ts +92 -0
  26. package/lib/components/heading.ts +96 -0
  27. package/lib/components/helpers.ts +41 -0
  28. package/lib/components/hero.ts +224 -0
  29. package/lib/components/icon.ts +178 -0
  30. package/lib/components/icons.ts +464 -0
  31. package/lib/components/include.ts +410 -0
  32. package/lib/components/input.ts +457 -0
  33. package/lib/components/list.ts +419 -0
  34. package/lib/components/loading.ts +100 -0
  35. package/lib/components/menu.ts +275 -0
  36. package/lib/components/modal.ts +284 -0
  37. package/lib/components/nav.ts +257 -0
  38. package/lib/components/paragraph.ts +97 -0
  39. package/lib/components/progress.ts +159 -0
  40. package/lib/components/radio.ts +278 -0
  41. package/lib/components/req.ts +303 -0
  42. package/lib/components/script.ts +41 -0
  43. package/lib/components/select.ts +252 -0
  44. package/lib/components/sidebar.ts +275 -0
  45. package/lib/components/style.ts +41 -0
  46. package/lib/components/switch.ts +246 -0
  47. package/lib/components/table.ts +1249 -0
  48. package/lib/components/tabs.ts +250 -0
  49. package/lib/components/theme-toggle.ts +293 -0
  50. package/lib/components/tooltip.ts +144 -0
  51. package/lib/components/view.ts +190 -0
  52. package/lib/components/write.ts +272 -0
  53. package/lib/globals.d.ts +19 -5
  54. package/lib/layouts/default.css +260 -0
  55. package/lib/layouts/figma.css +334 -0
  56. package/lib/reactivity/state.ts +78 -0
  57. package/lib/utils/{fetch.js → fetch.ts} +206 -81
  58. package/package.json +9 -31
  59. package/create/index.jux +0 -77
  60. package/create/layout.jux +0 -18
  61. package/create/style.css +0 -57
  62. package/create/themes/assets/jux.svg +0 -34
  63. package/create/themes/base.css +0 -197
  64. package/create/themes/base2.css +0 -54
  65. package/create/themes/layouts/base.jux +0 -16
  66. package/create/themes/layouts/base_blog.jux +0 -0
  67. package/create/themes/layouts/base_docs.jux +0 -0
  68. package/create/themes/layouts/base_login.jux +0 -0
  69. package/create/themes/layouts/base_marketing.jux +0 -0
  70. package/create/themes/layouts/base_saas.jux +0 -0
  71. package/lib/componentsv2/base/BaseEngine.d.ts +0 -112
  72. package/lib/componentsv2/base/BaseEngine.js +0 -279
  73. package/lib/componentsv2/base/BaseSkin.d.ts +0 -74
  74. package/lib/componentsv2/base/BaseSkin.js +0 -130
  75. package/lib/componentsv2/base/Neighborhood.d.ts +0 -22
  76. package/lib/componentsv2/base/Neighborhood.js +0 -56
  77. package/lib/componentsv2/base/OptionsContract.d.ts +0 -20
  78. package/lib/componentsv2/base/OptionsContract.js +0 -107
  79. package/lib/componentsv2/base/State.d.ts +0 -18
  80. package/lib/componentsv2/base/State.js +0 -68
  81. package/lib/componentsv2/element/Element.d.ts +0 -29
  82. package/lib/componentsv2/element/Element.js +0 -49
  83. package/lib/componentsv2/element/ElementEngine.d.ts +0 -58
  84. package/lib/componentsv2/element/ElementEngine.js +0 -112
  85. package/lib/componentsv2/element/ElementSkin.d.ts +0 -10
  86. package/lib/componentsv2/element/ElementSkin.js +0 -56
  87. package/lib/componentsv2/element/structure.css +0 -261
  88. package/lib/componentsv2/grid/Grid.d.ts +0 -13
  89. package/lib/componentsv2/grid/Grid.js +0 -27
  90. package/lib/componentsv2/grid/GridEngine.d.ts +0 -77
  91. package/lib/componentsv2/grid/GridEngine.js +0 -153
  92. package/lib/componentsv2/grid/GridSkin.d.ts +0 -11
  93. package/lib/componentsv2/grid/GridSkin.js +0 -84
  94. package/lib/componentsv2/grid/structure.css +0 -27
  95. package/lib/componentsv2/input/Input.d.ts +0 -6
  96. package/lib/componentsv2/input/Input.js +0 -21
  97. package/lib/componentsv2/input/InputEngine.d.ts +0 -70
  98. package/lib/componentsv2/input/InputEngine.js +0 -143
  99. package/lib/componentsv2/input/InputSkin.d.ts +0 -11
  100. package/lib/componentsv2/input/InputSkin.js +0 -89
  101. package/lib/componentsv2/input/structure.css +0 -47
  102. package/lib/componentsv2/list/List.d.ts +0 -49
  103. package/lib/componentsv2/list/List.js +0 -105
  104. package/lib/componentsv2/list/ListEngine.d.ts +0 -121
  105. package/lib/componentsv2/list/ListEngine.js +0 -322
  106. package/lib/componentsv2/list/ListSkin.d.ts +0 -20
  107. package/lib/componentsv2/list/ListSkin.js +0 -345
  108. package/lib/componentsv2/list/structure.css +0 -359
  109. package/lib/componentsv2/plugins/ClientSQLitePlugin.d.ts +0 -21
  110. package/lib/componentsv2/plugins/ClientSQLitePlugin.js +0 -130
  111. package/lib/componentsv2/plugins/IndexedDBPlugin.d.ts +0 -18
  112. package/lib/componentsv2/plugins/IndexedDBPlugin.js +0 -75
  113. package/lib/componentsv2/plugins/LocalStoragePlugin.d.ts +0 -20
  114. package/lib/componentsv2/plugins/LocalStoragePlugin.js +0 -65
  115. package/lib/componentsv2/plugins/ServerSQLitePlugin.d.ts +0 -25
  116. package/lib/componentsv2/plugins/ServerSQLitePlugin.js +0 -70
  117. package/lib/componentsv2/stubs/ComponentComposition.ts.stub +0 -32
  118. package/lib/componentsv2/stubs/ComponentEngine.ts.stub +0 -36
  119. package/lib/componentsv2/stubs/ComponentSkin.ts.stub +0 -35
  120. package/lib/componentsv2/stubs/ComponentStructure.css.d.ts.stub +0 -2
  121. package/lib/componentsv2/stubs/ComponentStructure.css.stub +0 -13
  122. package/lib/utils/fetch.d.ts +0 -176
  123. package/machinery/build3.js +0 -159
  124. package/machinery/compiler3.js +0 -688
  125. package/machinery/config.js +0 -155
  126. package/machinery/serve.js +0 -255
  127. package/machinery/validators/file-validator.js +0 -123
  128. package/machinery/watcher.js +0 -59
  129. package/types/css.d.ts +0 -10
@@ -1,155 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs';
3
- import { fileURLToPath } from 'url';
4
-
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = path.dirname(__filename);
7
-
8
- // Default configuration
9
- export const defaultConfig = {
10
- sourceDir: 'jux',
11
- distDir: '.jux-dist',
12
- ports: {
13
- http: 3000,
14
- ws: 3001
15
- },
16
- build: {
17
- minify: false,
18
- sourcemap: true
19
- },
20
- bootstrap: []
21
- };
22
-
23
- // Load user configuration
24
- export async function loadConfig(projectRoot) {
25
- const configPath = path.join(projectRoot, 'juxconfig.js');
26
-
27
- if (fs.existsSync(configPath)) {
28
- try {
29
- const configModule = await import(`file://${configPath}`);
30
- const userConfig = configModule.default || configModule;
31
-
32
- return {
33
- ...defaultConfig,
34
- ...userConfig,
35
- ports: { ...defaultConfig.ports, ...(userConfig.ports || {}) },
36
- build: { ...defaultConfig.build, ...(userConfig.build || {}) }
37
- };
38
- } catch (err) {
39
- console.warn(`⚠️ Failed to load juxconfig.js: ${err.message}`);
40
- console.warn(` Using default configuration\n`);
41
- return defaultConfig;
42
- }
43
- }
44
-
45
- console.log('ℹ️ No juxconfig.js found, using defaults');
46
- return defaultConfig;
47
- }
48
-
49
- // Run bootstrap functions
50
- export async function runBootstrap(bootstrapFunctions = []) {
51
- if (!Array.isArray(bootstrapFunctions) || bootstrapFunctions.length === 0) {
52
- return;
53
- }
54
-
55
- console.log('🚀 Running bootstrap functions...\n');
56
-
57
- for (const fn of bootstrapFunctions) {
58
- if (typeof fn === 'function') {
59
- try {
60
- await fn();
61
- } catch (err) {
62
- console.error(`❌ Bootstrap function failed:`, err.message);
63
- }
64
- }
65
- }
66
-
67
- console.log('✅ Bootstrap complete\n');
68
- }
69
-
70
- /**
71
- * Type helper for JUX Configuration (Identity function for Autocomplete)
72
- * Used in juxconfig.js to provide Intellisense.
73
- * @param {Object} config
74
- * @returns {Object}
75
- */
76
- export function defineConfig(config) {
77
- return config;
78
- }
79
-
80
- /**
81
- * Normalizes user configuration into the strict format used by the compiler/server.
82
- * Handles "DX Sugar" like:
83
- * - Inferring file extensions (.jux, .css)
84
- * - Mapping 'dist' -> 'distribution'
85
- * - Resolving shortcuts (e.g. layouts)
86
- * - Defaulting ports
87
- */
88
- export function resolveConfig(userConfig, projectRoot = process.cwd()) {
89
- const root = userConfig.root || projectRoot;
90
-
91
- // 1. Directories (Map 'dist' alias to 'distribution')
92
- const dirs = userConfig.directories || {};
93
- const directories = {
94
- source: dirs.source || 'jux',
95
- distribution: dirs.dist || dirs.distribution || '.jux-dist',
96
- themes: dirs.themes || 'themes',
97
- layouts: dirs.layouts || 'themes/layouts',
98
- assets: dirs.assets || 'themes/assets'
99
- };
100
-
101
- // 2. Defaults (Infer extensions)
102
- const defs = userConfig.defaults || {};
103
- const httpPort = defs.port || defs.httpPort || 3000;
104
-
105
- const defaults = {
106
- httpPort,
107
- wsPort: defs.wsPort || (httpPort + 1),
108
- autoRoute: defs.autoRoute !== false, // default true
109
- layout: ensureExt(defs.layout, '.jux') || 'base.jux',
110
- theme: ensureExt(defs.theme, '.css') || 'base.css'
111
- };
112
-
113
- // 3. Pages Normalization (Recursively fix route paths)
114
- const pages = normalizePages(userConfig.pages || {});
115
-
116
- // 4. Return Normalized Config
117
- return {
118
- root,
119
- directories,
120
- defaults,
121
- pages,
122
- hooks: userConfig.hooks || {}
123
- };
124
- }
125
-
126
- /**
127
- * Helper: Appends extension if missing
128
- */
129
- function ensureExt(file, ext) {
130
- if (!file) return file;
131
- return file.endsWith(ext) ? file : `${file}${ext}`;
132
- }
133
-
134
- /**
135
- * Helper: Recursively normalizes page routes
136
- */
137
- function normalizePages(pages) {
138
- const normalized = {};
139
- for (const [key, value] of Object.entries(pages)) {
140
- if (typeof value === 'string') {
141
- // Direct route: '/' -> 'experiments/index'
142
- // Becomes: '/' -> 'experiments/index.jux'
143
- normalized[key] = ensureExt(value, '.jux');
144
- } else if (typeof value === 'object') {
145
- // Route Group
146
- normalized[key] = {
147
- ...value,
148
- layout: value.layout ? ensureExt(value.layout, '.jux') : undefined,
149
- theme: value.theme ? ensureExt(value.theme, '.css') : undefined,
150
- routes: normalizePages(value.routes || {})
151
- };
152
- }
153
- }
154
- return normalized;
155
- }
@@ -1,255 +0,0 @@
1
- import express from 'express';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import http from 'http';
5
- import { fileURLToPath } from 'url';
6
- import { WebSocketServer } from 'ws';
7
- import { createWatcher } from './watcher.js';
8
- import { JuxCompiler } from './compiler3.js';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
-
13
- // ═══════════════════════════════════════════════════════════════
14
- // CONFIGURATION
15
- // ═══════════════════════════════════════════════════════════════
16
- const args = process.argv.slice(2);
17
- const HOT_RELOAD = args.includes('--hot') || args.includes('-h') || args.includes('--watch');
18
-
19
- // Extract port from args (e.g., --port=3000 or -p 3000)
20
- function getArgValue(flag, shortFlag, defaultValue) {
21
- for (let i = 0; i < args.length; i++) {
22
- if (args[i].startsWith(`${flag}=`)) return args[i].split('=')[1];
23
- if (args[i].startsWith(`${shortFlag}=`)) return args[i].split('=')[1];
24
- if ((args[i] === flag || args[i] === shortFlag) && args[i + 1]) return args[i + 1];
25
- }
26
- return defaultValue;
27
- }
28
-
29
- const PORT = parseInt(getArgValue('--port', '-p', process.env.PORT || '3000'));
30
- const WS_PORT = parseInt(getArgValue('--ws-port', '-w', process.env.WS_PORT || String(PORT + 1)));
31
-
32
- // Resolve paths relative to CWD (user's project)
33
- const PROJECT_ROOT = process.cwd();
34
- const DIST_DIR = path.resolve(PROJECT_ROOT, '.jux-dist');
35
- const SRC_DIR = path.resolve(PROJECT_ROOT, 'jux');
36
-
37
- const app = express();
38
- let lastBuildResult = { success: true, errors: [] };
39
-
40
- // Check if dist directory exists - if not, try to build first
41
- if (!fs.existsSync(DIST_DIR) || !fs.existsSync(path.join(DIST_DIR, 'index.html'))) {
42
- console.log('⚠️ Dist directory not found. Running initial build...');
43
-
44
- const compiler = new JuxCompiler({
45
- srcDir: SRC_DIR,
46
- distDir: DIST_DIR
47
- });
48
-
49
- try {
50
- const result = await compiler.build();
51
- lastBuildResult = result;
52
-
53
- if (!result.success) {
54
- console.log('\n❌ Initial build failed. Starting server to show error overlay.\n');
55
- }
56
- } catch (err) {
57
- console.error('❌ Initial build failed:', err.message);
58
- lastBuildResult = { success: false, errors: [{ message: err.message }] };
59
- }
60
- }
61
-
62
- if (!fs.existsSync(DIST_DIR)) {
63
- fs.mkdirSync(DIST_DIR, { recursive: true });
64
- }
65
-
66
- app.use((req, res, next) => {
67
- res.setHeader('Cache-Control', 'no-store');
68
- next();
69
- });
70
-
71
- app.use((req, res, next) => {
72
- if (req.path.endsWith('.js')) {
73
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
74
- }
75
- next();
76
- });
77
-
78
- app.get('/favicon.ico', (req, res) => res.status(204).end());
79
- app.get('/favicon.png', (req, res) => res.status(204).end());
80
-
81
- app.get('/__jux_sources.json', (req, res) => {
82
- const snapshotPath = path.join(DIST_DIR, '__jux_sources.json');
83
- if (fs.existsSync(snapshotPath)) {
84
- res.setHeader('Content-Type', 'application/json');
85
- res.sendFile(snapshotPath);
86
- } else {
87
- res.json({});
88
- }
89
- });
90
-
91
- const hotReloadScript = `
92
- <script>
93
- (function() {
94
- var ws = new WebSocket('ws://localhost:${WS_PORT}');
95
- ws.onopen = function() { console.log('🔥 Hot reload connected'); };
96
- ws.onmessage = function(e) {
97
- var data = JSON.parse(e.data);
98
- if (data.type === 'reload') location.reload();
99
- else if (data.type === 'build-error') console.error('❌ Build error:', data.errors);
100
- };
101
- ws.onclose = function() { setTimeout(function() { location.reload(); }, 2000); };
102
- })();
103
- </script>
104
- `;
105
-
106
- function getErrorPageHtml(errors) {
107
- return `<!DOCTYPE html>
108
- <html lang="en">
109
- <head>
110
- <meta charset="UTF-8">
111
- <title>JUX Build Error</title>
112
- <style>
113
- * { margin: 0; padding: 0; box-sizing: border-box; }
114
- body { font-family: monospace; background: #1a1a2e; color: #eee; min-height: 100vh; padding: 40px; }
115
- .container { max-width: 800px; margin: 0 auto; }
116
- h1 { color: #ff6b6b; margin-bottom: 8px; }
117
- .subtitle { color: #888; margin-bottom: 30px; }
118
- .error-card { background: #16213e; padding: 16px 20px; border-radius: 8px; margin-bottom: 12px; border-left: 4px solid #ff6b6b; }
119
- .error-header { color: #ff6b6b; font-weight: bold; margin-bottom: 8px; }
120
- .error-code { background: #0f0f23; padding: 12px; border-radius: 4px; font-size: 13px; color: #888; }
121
- </style>
122
- </head>
123
- <body>
124
- <div class="container">
125
- <h1>🛑 Build Failed</h1>
126
- <p class="subtitle">Fix the errors below and save to rebuild.</p>
127
- ${errors.map(err => `
128
- <div class="error-card">
129
- <div class="error-header">${err.view ? `[${err.view}] ` : ''}${err.message}</div>
130
- ${err.code ? `<pre class="error-code">${err.code}</pre>` : ''}
131
- </div>
132
- `).join('')}
133
- </div>
134
- ${HOT_RELOAD ? `<script>
135
- var ws = new WebSocket('ws://localhost:${WS_PORT}');
136
- ws.onmessage = function(e) { if (JSON.parse(e.data).type === 'reload') location.reload(); };
137
- </script>` : ''}
138
- </body>
139
- </html>`;
140
- }
141
-
142
- if (HOT_RELOAD) {
143
- app.get(['/', '/index.html'], (req, res) => {
144
- const indexPath = path.join(DIST_DIR, 'index.html');
145
-
146
- if (!fs.existsSync(indexPath) || !lastBuildResult.success) {
147
- return res.send(getErrorPageHtml(lastBuildResult.errors));
148
- }
149
-
150
- let html = fs.readFileSync(indexPath, 'utf8');
151
- html = html.replace('</body>', hotReloadScript + '</body>');
152
- res.setHeader('Content-Type', 'text/html');
153
- res.send(html);
154
- });
155
- }
156
-
157
- app.use(express.static(DIST_DIR));
158
-
159
- app.get('*', (req, res) => {
160
- if (path.extname(req.path) && req.path !== '/') {
161
- return res.status(404).send('Not found');
162
- }
163
-
164
- const indexPath = path.join(DIST_DIR, 'index.html');
165
-
166
- if (!fs.existsSync(indexPath) || !lastBuildResult.success) {
167
- return res.send(getErrorPageHtml(lastBuildResult.errors));
168
- }
169
-
170
- if (HOT_RELOAD) {
171
- let html = fs.readFileSync(indexPath, 'utf8');
172
- html = html.replace('</body>', hotReloadScript + '</body>');
173
- res.setHeader('Content-Type', 'text/html');
174
- res.send(html);
175
- } else {
176
- res.sendFile(indexPath);
177
- }
178
- });
179
-
180
- const server = http.createServer(app);
181
- let wss = null;
182
- let watcher = null;
183
-
184
- if (HOT_RELOAD) {
185
- wss = new WebSocketServer({ port: WS_PORT });
186
- const clients = new Set();
187
-
188
- wss.on('connection', (ws) => {
189
- clients.add(ws);
190
- ws.on('close', () => clients.delete(ws));
191
- });
192
-
193
- const broadcast = (message) => {
194
- const data = JSON.stringify(message);
195
- clients.forEach(client => {
196
- if (client.readyState === 1) client.send(data);
197
- });
198
- };
199
-
200
- const compiler = new JuxCompiler({
201
- srcDir: SRC_DIR,
202
- distDir: DIST_DIR
203
- });
204
-
205
- watcher = createWatcher(SRC_DIR, {
206
- onChange: async (changedFiles) => {
207
- console.log('🔄 Rebuilding...');
208
-
209
- try {
210
- const result = await compiler.build();
211
- lastBuildResult = result;
212
-
213
- if (!result.success) {
214
- console.error('❌ Rebuild failed');
215
- broadcast({ type: 'build-error', errors: result.errors });
216
- return;
217
- }
218
-
219
- console.log('✅ Rebuild complete');
220
- broadcast({ type: 'reload', files: changedFiles });
221
- } catch (err) {
222
- console.error('❌ Rebuild failed:', err.message);
223
- lastBuildResult = { success: false, errors: [{ message: err.message }] };
224
- broadcast({ type: 'build-error', errors: [{ message: err.message }] });
225
- }
226
- },
227
- onReady: () => {
228
- console.log(`👀 Watching: ${SRC_DIR}`);
229
- }
230
- });
231
- }
232
-
233
- server.listen(PORT, () => {
234
- console.log(`\n🚀 JUX Server running at http://localhost:${PORT}`);
235
- if (HOT_RELOAD) {
236
- console.log(`🔥 Hot reload: ws://localhost:${WS_PORT}`);
237
- }
238
- if (!lastBuildResult.success) {
239
- console.log(`⚠️ Build has errors - fix them and save\n`);
240
- }
241
- });
242
-
243
- const shutdown = () => {
244
- console.log('\n👋 Shutting down...');
245
- if (watcher) watcher.close();
246
- if (wss) {
247
- wss.clients.forEach(client => client.terminate());
248
- wss.close();
249
- }
250
- server.close(() => process.exit(0));
251
- setTimeout(() => process.exit(0), 2000);
252
- };
253
-
254
- process.on('SIGINT', shutdown);
255
- process.on('SIGTERM', shutdown);
@@ -1,123 +0,0 @@
1
- /**
2
- * File validation utilities for Jux compiler
3
- * Validates file types and content security
4
- */
5
- export class FileValidator {
6
- constructor() {
7
- this.cssExtensions = /\.(css|scss|sass|less)$/i;
8
- this.jsExtensions = /\.(js|mjs|ts|tsx|jsx)$/i;
9
- this.imageExtensions = /\.(png|jpg|jpeg|gif|svg|webp|ico|bmp)$/i;
10
- }
11
-
12
- /**
13
- * Check if file is a CSS file
14
- */
15
- isCSSFile(filepath) {
16
- return this.cssExtensions.test(filepath);
17
- }
18
-
19
- /**
20
- * Check if file is a JavaScript file
21
- */
22
- isJavaScriptFile(filepath) {
23
- return this.jsExtensions.test(filepath);
24
- }
25
-
26
- /**
27
- * Check if file is an image file
28
- */
29
- isImageFile(filepath) {
30
- return this.imageExtensions.test(filepath);
31
- }
32
-
33
- /**
34
- * Validate CSS content for security issues
35
- * Detects <script> tags that could be injected
36
- */
37
- validateStyleContent(content, source = 'unknown') {
38
- if (/<script/i.test(content)) {
39
- throw new Error(
40
- `🚨 Security Error: <script> tag detected in ${source}\n` +
41
- `CSS content must not contain script tags.`
42
- );
43
- }
44
-
45
- if (/<\/script/i.test(content)) {
46
- throw new Error(
47
- `🚨 Security Error: </script> tag detected in ${source}\n` +
48
- `CSS content must not contain script tags.`
49
- );
50
- }
51
-
52
- return content;
53
- }
54
-
55
- /**
56
- * Determine import type based on file extension
57
- * Returns: 'css' | 'js' | 'image' | 'unknown'
58
- */
59
- validateImportPath(importPath) {
60
- // Handle URLs
61
- if (importPath.startsWith('http://') || importPath.startsWith('https://')) {
62
- // Try to infer from URL
63
- if (this.isCSSFile(importPath)) return { type: 'css', path: importPath };
64
- if (this.isJavaScriptFile(importPath)) return { type: 'js', path: importPath };
65
- if (this.isImageFile(importPath)) return { type: 'image', path: importPath };
66
-
67
- // Default to unknown for CDN URLs without clear extensions
68
- return { type: 'unknown', path: importPath };
69
- }
70
-
71
- // Local files
72
- if (this.isCSSFile(importPath)) {
73
- return { type: 'css', path: importPath };
74
- }
75
-
76
- if (this.isJavaScriptFile(importPath)) {
77
- return { type: 'js', path: importPath };
78
- }
79
-
80
- if (this.isImageFile(importPath)) {
81
- return { type: 'image', path: importPath };
82
- }
83
-
84
- return { type: 'unknown', path: importPath };
85
- }
86
-
87
- /**
88
- * Validate array of imports and categorize them
89
- * Returns categorized imports with warnings
90
- */
91
- categorizeImports(imports) {
92
- const categorized = {
93
- css: [],
94
- js: [],
95
- images: [],
96
- unknown: []
97
- };
98
-
99
- const warnings = [];
100
-
101
- for (const importPath of imports) {
102
- const result = this.validateImportPath(importPath);
103
-
104
- if (result.type === 'unknown') {
105
- warnings.push(
106
- `⚠️ Unknown import type: ${importPath}\n` +
107
- ` Supported: .css/.scss/.sass, .js/.ts, .png/.jpg/.svg`
108
- );
109
- }
110
-
111
- categorized[result.type === 'unknown' ? 'unknown' : result.type].push(result.path);
112
- }
113
-
114
- return { categorized, warnings };
115
- }
116
-
117
- /**
118
- * Check if inline style content is empty or whitespace-only
119
- */
120
- isEmptyStyle(styleContent) {
121
- return !styleContent || styleContent.trim().length === 0;
122
- }
123
- }
@@ -1,59 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- /**
5
- * Watch a directory for changes and trigger callbacks
6
- */
7
- export function createWatcher(srcDir, options = {}) {
8
- const {
9
- onChange = () => {},
10
- onReady = () => {},
11
- debounceMs = 100,
12
- extensions = ['.jux', '.js', '.css']
13
- } = options;
14
-
15
- const absoluteSrcDir = path.resolve(srcDir);
16
- let debounceTimer = null;
17
- let pendingChanges = new Set();
18
-
19
- console.log(`👀 Watching: ${absoluteSrcDir}`);
20
-
21
- // Debounced change handler
22
- const handleChange = (eventType, filename) => {
23
- if (!filename) return;
24
-
25
- // Filter by extension
26
- const ext = path.extname(filename);
27
- if (!extensions.includes(ext)) return;
28
-
29
- pendingChanges.add(filename);
30
-
31
- // Debounce rapid changes
32
- if (debounceTimer) clearTimeout(debounceTimer);
33
- debounceTimer = setTimeout(() => {
34
- const changes = [...pendingChanges];
35
- pendingChanges.clear();
36
-
37
- console.log(`📝 Changed: ${changes.join(', ')}`);
38
- onChange(changes);
39
- }, debounceMs);
40
- };
41
-
42
- // Start watching
43
- const watcher = fs.watch(absoluteSrcDir, { recursive: true }, handleChange);
44
-
45
- watcher.on('error', (err) => {
46
- console.error('❌ Watcher error:', err);
47
- });
48
-
49
- onReady();
50
-
51
- // Return control object
52
- return {
53
- close: () => {
54
- if (debounceTimer) clearTimeout(debounceTimer);
55
- watcher.close();
56
- console.log('👋 Watcher closed');
57
- }
58
- };
59
- }
package/types/css.d.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * CSS Module Type Declaration
3
- *
4
- * This single declaration handles ALL .css imports in the project.
5
- * No need for individual .d.ts files next to each CSS file.
6
- */
7
- declare module '*.css' {
8
- const content: string;
9
- export default content;
10
- }