@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 +2 -1
- package/readme.md +1 -1
- package/src/jsx/jsx-runtime.js +13 -1
- package/src/jsx/jsx-runtime.test.jsx +77 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.28.
|
|
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 [](https://github.com/franciscop/server-next/actions)
|
package/src/jsx/jsx-runtime.js
CHANGED
|
@@ -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) =>
|
|
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(
|
|
146
|
-
|
|
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's"></div>',
|
|
176
|
-
);
|
|
178
|
+
expect(<div title={"it's"}></div>).toRender('<div title="it'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
|
|
321
|
-
)
|
|
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>
|
|
333
|
-
)
|
|
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>
|
|
339
|
-
)
|
|
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>
|
|
345
|
-
)
|
|
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
|
|
373
|
-
)
|
|
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
|
-
"<script>alert(1)</script>",
|
|
416
|
-
);
|
|
416
|
+
expect(<Unsafe />).toRender("<script>alert(1)</script>");
|
|
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
|
+
});
|