@mandujs/core 0.54.25 → 0.54.27
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,51 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Regression guard for
|
|
3
|
-
*
|
|
4
|
-
* React's main entry does NOT export `jsxDEV` (it lives in
|
|
5
|
-
* `react/jsx-dev-runtime`), so `jsxDEV` resolved to `undefined` and every
|
|
6
|
-
* island threw `TypeError: jsxDEV is not a function` in dev mode.
|
|
2
|
+
* Regression guard for issues #322 / #323 — dev island hydration broke with
|
|
3
|
+
* `TypeError: jsxDEV is not a function`.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* #322: the shim imported `jsxDEV` from bare `"react"`, which (via the import
|
|
6
|
+
* map → `_react.js`) only worked if `_react.js` re-exported jsxDEV.
|
|
7
|
+
* #323: the "fix" changed the import to `"react/jsx-dev-runtime"`, but the
|
|
8
|
+
* import map maps `react/jsx-dev-runtime` to THIS shim — a circular
|
|
9
|
+
* self-import, so jsxDEV stayed `undefined`.
|
|
10
|
+
*
|
|
11
|
+
* The correct source: import JSX runtime functions from bare `"react"`, which
|
|
12
|
+
* the import map resolves to the self-contained `_react.js` vendor bundle
|
|
13
|
+
* (built with no `external`, so the real jsx/jsxs/jsxDEV/Fragment are inlined
|
|
14
|
+
* and re-exported). The shim must NEVER import from the import-map-aliased
|
|
15
|
+
* subpath that points back at itself.
|
|
10
16
|
*/
|
|
11
17
|
import { describe, expect, test } from "bun:test";
|
|
18
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import path from "node:path";
|
|
12
21
|
import {
|
|
13
22
|
_testOnly_generateJsxDevRuntimeShimSource,
|
|
14
23
|
_testOnly_generateJsxRuntimeShimSource,
|
|
15
24
|
} from "../build";
|
|
16
25
|
|
|
17
|
-
describe("JSX runtime shim sources (
|
|
18
|
-
test("dev shim imports jsxDEV/Fragment from react
|
|
26
|
+
describe("JSX runtime shim sources (issues #322 / #323)", () => {
|
|
27
|
+
test("dev shim imports jsxDEV/Fragment from bare 'react', never the self-aliased subpath", () => {
|
|
19
28
|
const source = _testOnly_generateJsxDevRuntimeShimSource();
|
|
20
29
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
30
|
+
// #323: must NOT import from 'react/jsx-dev-runtime' — that is this shim's
|
|
31
|
+
// own import-map alias, which would be a circular self-import. (Targets
|
|
32
|
+
// the import statement, not comment mentions of the path.)
|
|
33
|
+
expect(source).not.toMatch(/from\s*['"]react\/jsx-dev-runtime['"]/);
|
|
26
34
|
|
|
27
|
-
//
|
|
28
|
-
expect(source).
|
|
29
|
-
/import\s*\{
|
|
35
|
+
// Correct: jsxDEV + Fragment come from bare 'react' (→ _react.js).
|
|
36
|
+
expect(source).toMatch(
|
|
37
|
+
/import\s*\{\s*jsxDEV\s*,\s*Fragment\s*\}\s*from\s*['"]react['"]/,
|
|
30
38
|
);
|
|
31
39
|
|
|
32
40
|
// Shim still re-exports what the import map expects.
|
|
33
41
|
expect(source).toContain("export { jsxDEV, Fragment }");
|
|
34
42
|
});
|
|
35
43
|
|
|
36
|
-
test("prod shim imports jsx/jsxs/Fragment from react
|
|
44
|
+
test("prod shim imports jsx/jsxs/Fragment from bare 'react', never the self-aliased subpath", () => {
|
|
37
45
|
const source = _testOnly_generateJsxRuntimeShimSource();
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
expect(source).toMatch(
|
|
41
|
-
/import\s*\{\s*jsx\s*,\s*jsxs\s*,\s*Fragment\s*\}\s*from\s*['"]react\/jsx-runtime['"]/,
|
|
42
|
-
);
|
|
47
|
+
// #323: must NOT import from 'react/jsx-runtime' (this shim's own alias).
|
|
48
|
+
expect(source).not.toMatch(/from\s*['"]react\/jsx-runtime['"]/);
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/import\s*\{[^}]*\bjsx\b[^}]*\}\s*from\s*['"]react['"]/,
|
|
50
|
+
expect(source).toMatch(
|
|
51
|
+
/import\s*\{\s*jsx\s*,\s*jsxs\s*,\s*Fragment\s*\}\s*from\s*['"]react['"]/,
|
|
47
52
|
);
|
|
48
53
|
|
|
49
54
|
expect(source).toContain("export { jsx, jsxs, Fragment }");
|
|
50
55
|
});
|
|
56
|
+
|
|
57
|
+
// #323 build-level guard: the SOURCE test alone missed the real bug because
|
|
58
|
+
// the broken 0.54.24 build still emitted `from "react/jsx-dev-runtime"` in
|
|
59
|
+
// the OUTPUT (the import-map alias pointing back at this shim). Build the
|
|
60
|
+
// shim the same way buildVendorShims does (external: ["react"]) and assert
|
|
61
|
+
// the emitted module never self-imports the alias.
|
|
62
|
+
test("built dev shim never self-imports react/jsx-dev-runtime", async () => {
|
|
63
|
+
const dir = await mkdtemp(path.join(tmpdir(), "mandu-jsx-shim-"));
|
|
64
|
+
try {
|
|
65
|
+
const srcPath = path.join(dir, "_jsx-dev-runtime.js");
|
|
66
|
+
await writeFile(srcPath, _testOnly_generateJsxDevRuntimeShimSource(), "utf-8");
|
|
67
|
+
|
|
68
|
+
const result = await Bun.build({
|
|
69
|
+
entrypoints: [srcPath],
|
|
70
|
+
external: ["react"],
|
|
71
|
+
target: "browser",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(result.success).toBe(true);
|
|
75
|
+
const out = await result.outputs[0]!.text();
|
|
76
|
+
|
|
77
|
+
// The emitted shim must source jsxDEV from bare 'react' (→ _react.js),
|
|
78
|
+
// NOT from the import-map alias that resolves back to itself.
|
|
79
|
+
expect(out).not.toMatch(/from\s*["']react\/jsx-dev-runtime["']/);
|
|
80
|
+
expect(out).toMatch(/from\s*["']react["']/);
|
|
81
|
+
expect(out).toContain("jsxDEV");
|
|
82
|
+
} finally {
|
|
83
|
+
await rm(dir, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
51
86
|
});
|
package/src/bundler/build.ts
CHANGED
|
@@ -633,11 +633,13 @@ function generateJsxRuntimeShimSource(): string {
|
|
|
633
633
|
/**
|
|
634
634
|
* Mandu JSX Runtime Shim (Generated)
|
|
635
635
|
* Production JSX 변환용
|
|
636
|
-
*
|
|
637
|
-
* import map의 'react/jsx-runtime'이 다시 이 셰임을
|
|
638
|
-
*
|
|
636
|
+
*
|
|
637
|
+
* #323: import map의 'react/jsx-runtime'이 다시 이 셰임을 가리키므로
|
|
638
|
+
* 'react/jsx-runtime'에서 import하면 순환 self-import가 된다. 대신
|
|
639
|
+
* 'react'(→ _react.js, external 없이 react 전체를 인라인하며 jsx/jsxs/
|
|
640
|
+
* Fragment를 re-export하는 vendor 번들)에서 가져와 순환을 끊는다.
|
|
639
641
|
*/
|
|
640
|
-
import { jsx, jsxs, Fragment } from 'react
|
|
642
|
+
import { jsx, jsxs, Fragment } from 'react';
|
|
641
643
|
|
|
642
644
|
// Named exports
|
|
643
645
|
export { jsx, jsxs, Fragment };
|
|
@@ -656,11 +658,14 @@ function generateJsxDevRuntimeShimSource(): string {
|
|
|
656
658
|
/**
|
|
657
659
|
* Mandu JSX Dev Runtime Shim (Generated)
|
|
658
660
|
* Development JSX 변환용
|
|
659
|
-
*
|
|
660
|
-
* import map의 'react/jsx-dev-runtime'이 다시 이 셰임을
|
|
661
|
-
*
|
|
661
|
+
*
|
|
662
|
+
* #323: import map의 'react/jsx-dev-runtime'이 다시 이 셰임을 가리키므로
|
|
663
|
+
* 'react/jsx-dev-runtime'에서 import하면 순환 self-import가 되어 jsxDEV가
|
|
664
|
+
* undefined가 된다. 대신 'react'(→ _react.js, external 없이 react 전체를
|
|
665
|
+
* 인라인하며 jsxDEV/Fragment를 re-export하는 vendor 번들)에서 가져와
|
|
666
|
+
* 순환을 끊는다.
|
|
662
667
|
*/
|
|
663
|
-
import { jsxDEV, Fragment } from 'react
|
|
668
|
+
import { jsxDEV, Fragment } from 'react';
|
|
664
669
|
|
|
665
670
|
// Named exports
|
|
666
671
|
export { jsxDEV, Fragment };
|
|
@@ -174,11 +174,22 @@ export class SourceContextProvider {
|
|
|
174
174
|
* 안전한 경로 확인 (Path Traversal 방지)
|
|
175
175
|
*/
|
|
176
176
|
private resolveSafePath(file: string): string | null {
|
|
177
|
+
// Reject absolute inputs outright: path.resolve(root, "/etc/passwd")
|
|
178
|
+
// returns "/etc/passwd", escaping the project root.
|
|
179
|
+
if (path.isAbsolute(file)) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
177
183
|
const absolutePath = path.resolve(this.options.projectRoot, file);
|
|
178
184
|
const normalizedRoot = path.normalize(this.options.projectRoot);
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
185
|
+
// Compare with a trailing separator so a sibling sharing the root's
|
|
186
|
+
// prefix (e.g. "/app/project-secrets" vs root "/app/project") is NOT
|
|
187
|
+
// treated as inside the root. Allow the root itself.
|
|
188
|
+
const rootWithSep = normalizedRoot.endsWith(path.sep)
|
|
189
|
+
? normalizedRoot
|
|
190
|
+
: normalizedRoot + path.sep;
|
|
191
|
+
|
|
192
|
+
if (absolutePath !== normalizedRoot && !absolutePath.startsWith(rootWithSep)) {
|
|
182
193
|
return null;
|
|
183
194
|
}
|
|
184
195
|
|
|
@@ -245,6 +245,8 @@ export class WorkerManager {
|
|
|
245
245
|
{ source: '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}', label: 'EMAIL' },
|
|
246
246
|
{ source: '\\\\+?[1-9]\\\\d{1,14}', label: 'PHONE' },
|
|
247
247
|
{ source: '\\\\b(?:\\\\d{1,3}\\\\.){3}\\\\d{1,3}\\\\b', label: 'IP' },
|
|
248
|
+
{ source: '\\\\b(?:\\\\d{4}[- ]?){3}\\\\d{4}\\\\b', label: 'CARD' },
|
|
249
|
+
{ source: '\\\\b\\\\d{3}-\\\\d{2}-\\\\d{4}\\\\b', label: 'SSN' },
|
|
248
250
|
];
|
|
249
251
|
|
|
250
252
|
function applyPattern(text, pattern) {
|