round-core 0.0.3 → 0.0.4

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/package.json CHANGED
@@ -1,20 +1,28 @@
1
1
  {
2
2
  "name": "round-core",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "A lightweight frontend framework for SPA with signals and fine grained reactivity",
5
- "main": "src/index.js",
5
+ "main": "./dist/index.js",
6
+ "exports": {
7
+ ".": "./dist/index.js",
8
+ "./vite-plugin": "./dist/vite-plugin.js"
9
+ },
6
10
  "type": "module",
7
11
  "icon": "round.png",
8
12
  "repository": {
9
13
  "url": "https://github.com/ZtaMDev/RoundJS.git"
10
14
  },
11
15
  "bin": {
12
- "round": "./src/cli.js"
16
+ "round": "./dist/cli.js"
13
17
  },
14
18
  "scripts": {
15
- "dev": "node ./cli.js dev --config ./test/main/round.config.json",
16
- "build": "node ./cli.js build --config ./test/main/round.config.json",
17
- "test": "vitest run"
19
+ "dev": "node ./src/cli.js dev --config ./test/main/round.config.json",
20
+ "build": "node ./src/cli.js build --config ./test/main/round.config.json",
21
+ "build:core": "vite build -c vite.config.build.js",
22
+ "test": "vitest run",
23
+ "bench": "bun run --cwd benchmarks bench",
24
+ "bench:build": "bun run --cwd benchmarks bench:build",
25
+ "bench:runtime": "bun run --cwd benchmarks bench:runtime"
18
26
  },
19
27
  "keywords": [
20
28
  "framework",
package/src/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+
2
2
  import path from 'node:path';
3
3
  import fs from 'node:fs';
4
4
  import process from 'node:process';
@@ -6,6 +6,13 @@ import { fileURLToPath } from 'node:url';
6
6
  import { createServer, build as viteBuild, preview as vitePreview } from 'vite';
7
7
  import RoundPlugin from './compiler/vite-plugin.js';
8
8
 
9
+ // Handle graceful shutdown
10
+ function onSignal() {
11
+ process.exit(0);
12
+ }
13
+ process.on('SIGINT', onSignal);
14
+ process.on('SIGTERM', onSignal);
15
+
9
16
  function normalizePath(p) {
10
17
  return p.replaceAll('\\', '/');
11
18
  }
@@ -239,7 +246,7 @@ async function runInit({ name }) {
239
246
  preview: 'round preview'
240
247
  },
241
248
  dependencies: {
242
- 'round-core': '^0.0.1'
249
+ 'round-core': '^0.0.4'
243
250
  },
244
251
  devDependencies: {
245
252
  vite: '^5.0.0'
@@ -272,7 +279,7 @@ async function runInit({ name }) {
272
279
 
273
280
  writeFileIfMissing(viteConfigPath, [
274
281
  "import { defineConfig } from 'vite';",
275
- "import RoundPlugin from 'round-core/src/compiler/vite-plugin.js';",
282
+ "import RoundPlugin from 'round-core/vite-plugin';",
276
283
  '',
277
284
  'export default defineConfig({',
278
285
  " plugins: [RoundPlugin({ configPath: './round.config.json' })],",
@@ -131,6 +131,9 @@ export default function RoundPlugin(pluginOptions = {}) {
131
131
  const trailingSlash = config?.routing?.trailingSlash;
132
132
  state.routingTrailingSlash = trailingSlash !== undefined ? Boolean(trailingSlash) : true;
133
133
 
134
+ const customTags = config?.htmlTags;
135
+ state.customTags = Array.isArray(customTags) ? customTags : [];
136
+
134
137
  const entryRel = config?.entry;
135
138
  state.entryAbs = entryRel ? resolveMaybeRelative(configDir, entryRel) : null;
136
139
 
@@ -303,7 +306,8 @@ export default function RoundPlugin(pluginOptions = {}) {
303
306
 
304
307
  return {
305
308
  define: {
306
- __ROUND_ROUTING_TRAILING_SLASH__: JSON.stringify(state.routingTrailingSlash)
309
+ __ROUND_ROUTING_TRAILING_SLASH__: JSON.stringify(state.routingTrailingSlash),
310
+ __ROUND_CUSTOM_TAGS__: JSON.stringify(state.customTags ?? [])
307
311
  },
308
312
  esbuild: {
309
313
  include: /\.(round|js|jsx|ts|tsx)$/,
@@ -1,5 +1,5 @@
1
1
  import { effect, untrack } from './signals.js';
2
- import { runInContext as runInLifecycle, createComponentInstance, mountComponent, initLifecycleRoot } from './lifecycle.js';
2
+ import { runInLifecycle, createComponentInstance, mountComponent, initLifecycleRoot } from './lifecycle.js';
3
3
  import { reportErrorSafe } from './error-reporter.js';
4
4
  import { captureContext, runInContext, readContext } from './context.js';
5
5
  import { SuspenseContext } from './suspense.js';
@@ -76,14 +76,13 @@ export function createElement(tag, props = {}, ...children) {
76
76
 
77
77
  if (typeof tag === 'string') {
78
78
  const isCustomElement = tag.includes('-');
79
- // Simple check: if it looks like a component (no hyphen, lowercase start)
80
- // and it's not a standard tag, it's likely an error.
81
- // We use a small heuristic list or regex.
82
- // Actually, user requested: "custom components when they start with lowercase... should give error".
83
- // Using a whitelist of standard tags is robust.
84
- const isStandard = /^(a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|slot|small|source|span|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|path|circle|rect|line|g|defs|linearGradient|stop|radialGradient|text|tspan)$/.test(tag);
85
-
86
- if (!isCustomElement && !isStandard && /^[a-z]/.test(tag)) {
79
+
80
+ const isStandard = /^(a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|slot|small|source|span|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|menu|animate|animateMotion|animateTransform|circle|clipPath|defs|desc|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|foreignObject|g|image|line|linearGradient|marker|mask|metadata|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|stop|switch|symbol|text|textPath|tspan|use|view)$/.test(tag);
81
+
82
+ // __ROUND_CUSTOM_TAGS__ is injected by the vite plugin from round.config.json
83
+ const isCustomConfigured = typeof __ROUND_CUSTOM_TAGS__ !== 'undefined' && __ROUND_CUSTOM_TAGS__.includes(tag);
84
+
85
+ if (!isCustomElement && !isStandard && !isCustomConfigured && /^[a-z]/.test(tag)) {
87
86
  throw new Error(`Component names must start with an uppercase letter: <${tag} />`);
88
87
  }
89
88
  }
@@ -273,6 +272,21 @@ export function createElement(tag, props = {}, ...children) {
273
272
  return;
274
273
  }
275
274
 
275
+ if (key === 'classList') {
276
+ if (value && typeof value === 'object') {
277
+ Object.entries(value).forEach(([className, condition]) => {
278
+ if (typeof condition === 'function') {
279
+ effect(() => {
280
+ element.classList.toggle(className, !!condition());
281
+ }, { onLoad: false });
282
+ } else {
283
+ element.classList.toggle(className, !!condition);
284
+ }
285
+ });
286
+ }
287
+ return;
288
+ }
289
+
276
290
  if (key === 'className') element.className = value;
277
291
  else if (key === 'value') element.value = value;
278
292
  else if (key === 'checked') element.checked = Boolean(value);
@@ -6,7 +6,7 @@ export function getCurrentComponent() {
6
6
  return componentStack[componentStack.length - 1];
7
7
  }
8
8
 
9
- export function runInContext(componentInstance, fn) {
9
+ export function runInLifecycle(componentInstance, fn) {
10
10
  componentStack.push(componentInstance);
11
11
  try {
12
12
  return fn();
@@ -0,0 +1,47 @@
1
+ import { defineConfig } from 'vite';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+
5
+ // Custom plugin to move .d.ts files or raw assets if needed,
6
+ // for now we just handle JS bundling.
7
+
8
+ // Custom plugin to move .d.ts files or raw assets if needed,
9
+ // for now we just handle JS bundling.
10
+
11
+ export default defineConfig({
12
+ build: {
13
+ // Target modern environments
14
+ target: 'es2022',
15
+ outDir: 'dist',
16
+ emptyOutDir: true,
17
+ minify: false, // User can enable if they want extreme minification, but for a lib readable code is nice.
18
+ // Wait, user asked for "extremo rapido y liviano" (extremely fast and light).
19
+ // So I SHOULD minify.
20
+ lib: {
21
+ entry: {
22
+ index: path.resolve(__dirname, 'src/index.js'),
23
+ cli: path.resolve(__dirname, 'src/cli.js'),
24
+ // We expose the plugin separately so users can import it in their vite.config.js
25
+ 'vite-plugin': path.resolve(__dirname, 'src/compiler/vite-plugin.js')
26
+ },
27
+ formats: ['es'] // ESM only is fine for modern "type": "module" package
28
+ },
29
+ rollupOptions: {
30
+ // Externalize dependencies so they aren't bundled into the library
31
+ external: [
32
+ 'vite',
33
+ 'marked',
34
+ 'node:fs', 'node:path', 'node:process', 'node:url', 'node:vm', 'node:util',
35
+ 'fs', 'path', 'process', 'url', 'vm', 'util'
36
+ ],
37
+ output: {
38
+ banner: (chunk) => {
39
+ if (chunk.name === 'cli' || chunk.fileName === 'cli.js') {
40
+ return '#!/usr/bin/env node';
41
+ }
42
+ return '';
43
+ }
44
+ }
45
+ },
46
+ },
47
+ });