@server/next 0.28.8 → 0.28.9

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.28.8",
3
+ "version": "0.28.9",
4
4
  "description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
5
5
  "homepage": "https://server-js.com/",
6
6
  "repository": "https://github.com/franciscop/server-next.git",
@@ -47,6 +47,7 @@
47
47
  "@types/bun": "^1.3.0",
48
48
  "@types/jest": "^30.0.0",
49
49
  "@types/node": "^24.10.0",
50
+ "bun": "^1.3.13",
50
51
  "check-dts": "^0.8.2",
51
52
  "jest": "^29.7.0",
52
53
  "polystore": "^0.18.0",
package/readme.md CHANGED
@@ -1 +1 @@
1
- # Server JS
1
+ # Server JS [![test badge](https://github.com/franciscop/server-next/workflows/tests/badge.svg)](https://github.com/franciscop/server-next/actions)
@@ -99,7 +99,9 @@ const jsx = (tag, { children, ...props } = {}) => {
99
99
  const parts = Array.isArray(children) ? children : [children];
100
100
  const content = parts
101
101
  .filter(isValidChild)
102
- .map((c) => (typeof c === "string" ? c : typeof c === "number" ? String(c) : ""))
102
+ .map((c) =>
103
+ typeof c === "string" ? c : typeof c === "number" ? String(c) : "",
104
+ )
103
105
  .join("");
104
106
  children = raw(content);
105
107
  }
@@ -135,6 +137,16 @@ const jsx = (tag, { children, ...props } = {}) => {
135
137
 
136
138
  if (v === true) return key;
137
139
 
140
+ if (k === "style" && v && typeof v === "object") {
141
+ v = Object.entries(v)
142
+ .filter(([, val]) => val != null)
143
+ .map(([prop, val]) => {
144
+ const cssKey = prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
145
+ return `${cssKey}:${val}`;
146
+ })
147
+ .join(";");
148
+ }
149
+
138
150
  const value =
139
151
  typeof v === "string" || typeof v === "number" ? encode(String(v)) : "";
140
152
 
@@ -142,9 +142,13 @@ describe("text encoding", () => {
142
142
 
143
143
  it("renders array children", () => {
144
144
  const items = ["x", "y", "z"];
145
- expect(<span>{items.map((i) => <b key={i}>{i}</b>)}</span>).toRender(
146
- "<span><b>x</b><b>y</b><b>z</b></span>",
147
- );
145
+ expect(
146
+ <span>
147
+ {items.map((i) => (
148
+ <b key={i}>{i}</b>
149
+ ))}
150
+ </span>,
151
+ ).toRender("<span><b>x</b><b>y</b><b>z</b></span>");
148
152
  });
149
153
 
150
154
  it("treats string returned from a function child as raw HTML", () => {
@@ -171,9 +175,7 @@ describe("attribute encoding", () => {
171
175
  });
172
176
 
173
177
  it("encodes ' in attribute values", () => {
174
- expect(<div title={"it's"}></div>).toRender(
175
- '<div title="it&#39;s"></div>',
176
- );
178
+ expect(<div title={"it's"}></div>).toRender('<div title="it&#39;s"></div>');
177
179
  });
178
180
 
179
181
  it("maps className to class", () => {
@@ -316,9 +318,9 @@ describe("style tag", () => {
316
318
  });
317
319
 
318
320
  it("preserves child combinator in selectors", () => {
319
- expect(
320
- <style>{":not(pre) > code { background: none; }"}</style>,
321
- ).toRender("<style>:not(pre)>code{background:none}</style>");
321
+ expect(<style>{":not(pre) > code { background: none; }"}</style>).toRender(
322
+ "<style>:not(pre)>code{background:none}</style>",
323
+ );
322
324
  });
323
325
 
324
326
  it("preserves sibling combinators in selectors", () => {
@@ -328,21 +330,21 @@ describe("style tag", () => {
328
330
  });
329
331
 
330
332
  it("removes spaces around braces, colons, and semicolons", () => {
331
- expect(
332
- <style>{"a { color : red ; font-size : 1em ; }"}</style>,
333
- ).toRender("<style>a{color:red;font-size:1em}</style>");
333
+ expect(<style>{"a { color : red ; font-size : 1em ; }"}</style>).toRender(
334
+ "<style>a{color:red;font-size:1em}</style>",
335
+ );
334
336
  });
335
337
 
336
338
  it("removes trailing semicolon before closing brace", () => {
337
- expect(
338
- <style>{"p { margin: 0; padding: 0; }"}</style>,
339
- ).toRender("<style>p{margin:0;padding:0}</style>");
339
+ expect(<style>{"p { margin: 0; padding: 0; }"}</style>).toRender(
340
+ "<style>p{margin:0;padding:0}</style>",
341
+ );
340
342
  });
341
343
 
342
344
  it("does not break </style> injection", () => {
343
- expect(
344
- <style>{"a { content: '</style>'; }"}</style>,
345
- ).toRender("<style>a{content:'<\\/style>'}</style>");
345
+ expect(<style>{"a { content: '</style>'; }"}</style>).toRender(
346
+ "<style>a{content:'<\\/style>'}</style>",
347
+ );
346
348
  });
347
349
  });
348
350
 
@@ -368,9 +370,9 @@ describe("dangerouslySetInnerHTML", () => {
368
370
  });
369
371
 
370
372
  it("does not render dangerouslySetInnerHTML as an attribute", () => {
371
- expect(
372
- <div dangerouslySetInnerHTML={{ __html: "hi" }}></div>,
373
- ).toRender("<div>hi</div>");
373
+ expect(<div dangerouslySetInnerHTML={{ __html: "hi" }}></div>).toRender(
374
+ "<div>hi</div>",
375
+ );
374
376
  });
375
377
  });
376
378
 
@@ -411,18 +413,14 @@ describe("function components", () => {
411
413
 
412
414
  it("encodes text returned by a component", () => {
413
415
  const Unsafe = () => "<script>alert(1)</script>";
414
- expect(<Unsafe />).toRender(
415
- "&lt;script&gt;alert(1)&lt;/script&gt;",
416
- );
416
+ expect(<Unsafe />).toRender("&lt;script&gt;alert(1)&lt;/script&gt;");
417
417
  });
418
418
  });
419
419
 
420
420
  describe("forwardRef-like components", () => {
421
421
  it("renders an object with a render function", () => {
422
422
  const Button = { render: ({ children }) => <button>{children}</button> };
423
- expect(<Button>click me</Button>).toRender(
424
- "<button>click me</button>",
425
- );
423
+ expect(<Button>click me</Button>).toRender("<button>click me</button>");
426
424
  });
427
425
 
428
426
  it("passes props to render function", () => {
@@ -487,3 +485,55 @@ describe("React element interop", () => {
487
485
  expect(<Deep />).toRender("<div><ul><li>one</li><li>two</li></ul></div>");
488
486
  });
489
487
  });
488
+ describe("styles", () => {
489
+ it("stringifies style object into CSS", () => {
490
+ expect(
491
+ <div
492
+ style={{
493
+ display: "inline-block",
494
+ padding: "2px 8px",
495
+ borderRadius: "4px",
496
+ }}
497
+ />,
498
+ ).toRender(
499
+ '<div style="display:inline-block;padding:2px 8px;border-radius:4px"></div>',
500
+ );
501
+ });
502
+
503
+ it("handles numbers and mixed style values", () => {
504
+ expect(
505
+ <div
506
+ style={{
507
+ fontSize: 12,
508
+ lineHeight: 1.5,
509
+ fontWeight: "600",
510
+ }}
511
+ />,
512
+ ).toRender(
513
+ '<div style="font-size:12;line-height:1.5;font-weight:600"></div>',
514
+ );
515
+ });
516
+
517
+ it("converts camelCase CSS properties", () => {
518
+ expect(
519
+ <div
520
+ style={{
521
+ backgroundColor: "#fff",
522
+ marginTop: "10px",
523
+ }}
524
+ />,
525
+ ).toRender('<div style="background-color:#fff;margin-top:10px"></div>');
526
+ });
527
+
528
+ it("ignores null and undefined style values", () => {
529
+ expect(
530
+ <div
531
+ style={{
532
+ color: "red",
533
+ padding: null,
534
+ margin: undefined,
535
+ }}
536
+ />,
537
+ ).toRender('<div style="color:red"></div>');
538
+ });
539
+ });