@swissjs/swite 0.4.1 → 0.4.2
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/CHANGELOG.md +7 -0
- package/__tests__/import-rewriter-bug.test.ts +122 -122
- package/__tests__/security-r001-r002.test.ts +190 -190
- package/dist/cli.js +0 -0
- package/dist/dev-engine/hmr/hmr-client-template.js +111 -111
- package/dist/dev-engine/middleware/middleware-setup.js +4 -3
- package/docs/architecture/build-pipeline.md +97 -97
- package/docs/architecture/dev-server.md +87 -87
- package/docs/architecture/hmr.md +78 -78
- package/docs/architecture/import-rewriting.md +101 -101
- package/docs/architecture/index.md +16 -16
- package/docs/architecture/python-integration.md +93 -93
- package/docs/architecture/resolution.md +92 -92
- package/docs/cli/build.md +78 -78
- package/docs/cli/dev.md +90 -90
- package/docs/cli/index.md +15 -15
- package/docs/cli/start.md +45 -45
- package/docs/development/contributing.md +74 -74
- package/docs/development/index.md +12 -12
- package/docs/development/internals.md +101 -101
- package/docs/guide/configuration.md +89 -89
- package/docs/guide/index.md +13 -13
- package/docs/guide/project-structure.md +75 -75
- package/docs/guide/quickstart.md +113 -113
- package/docs/index.md +16 -16
- package/package.json +10 -9
- package/src/config/env.ts +98 -98
- package/src/dev-engine/handlers/ui-handler.ts +30 -30
- package/src/dev-engine/handlers/uix-handler.ts +21 -21
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -122
- package/src/dev-engine/middleware/middleware-setup.ts +354 -354
- package/src/dev-engine/middleware/static-files.ts +813 -813
- package/src/resolution/cdn/cdn-fallback.ts +40 -40
- package/src/resolution/path/path-fixup.ts +27 -27
- package/src/resolution/rewriting/import-rewriter.ts +237 -237
- package/src/resolution/symlink-registry.ts +114 -114
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Symlink Registry - CG-03 root cause fix
|
|
3
|
-
*
|
|
4
|
-
* fs.realpath() throughout Swite's handler chain resolves symlinks to absolute
|
|
5
|
-
* filesystem paths (e.g. /mnt/c/.../swiss-lib/packages/core/src/index.ts).
|
|
6
|
-
* These leak into toUrl() and hit the startsWith("/") early-return before the
|
|
7
|
-
* proper node_modules/swiss-lib handling logic is reached.
|
|
8
|
-
*
|
|
9
|
-
* At server startup we scan node_modules directories for symlinks and build a
|
|
10
|
-
* map: realpath → /node_modules/<pkg-name>
|
|
11
|
-
* toUrl() consults this registry FIRST and short-circuits back to the correct
|
|
12
|
-
* browser URL.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { promises as fs } from "node:fs";
|
|
16
|
-
import path from "node:path";
|
|
17
|
-
|
|
18
|
-
// realpath (normalized, forward slashes) → browser URL prefix
|
|
19
|
-
const registry = new Map<string, string>();
|
|
20
|
-
|
|
21
|
-
export async function buildSymlinkRegistry(
|
|
22
|
-
nodeModulesDirs: string[]
|
|
23
|
-
): Promise<void> {
|
|
24
|
-
registry.clear();
|
|
25
|
-
for (const dir of nodeModulesDirs) {
|
|
26
|
-
await scanNodeModulesDir(dir);
|
|
27
|
-
}
|
|
28
|
-
console.log(
|
|
29
|
-
`[SWITE] Symlink registry built: ${registry.size} entries from ${nodeModulesDirs.length} node_modules dirs`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function scanNodeModulesDir(nodeModulesDir: string): Promise<void> {
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
let dirents: any[];
|
|
36
|
-
try {
|
|
37
|
-
dirents = await fs.readdir(nodeModulesDir, {
|
|
38
|
-
withFileTypes: true,
|
|
39
|
-
encoding: "utf8",
|
|
40
|
-
});
|
|
41
|
-
} catch {
|
|
42
|
-
return; // dir doesn't exist — skip silently
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const dirent of dirents) {
|
|
46
|
-
if (dirent.name.startsWith(".")) continue;
|
|
47
|
-
|
|
48
|
-
if (dirent.name.startsWith("@")) {
|
|
49
|
-
// Scoped scope directory — scan one level deeper
|
|
50
|
-
const scopeDir = path.join(nodeModulesDir, dirent.name);
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
-
let scopedDirents: any[];
|
|
53
|
-
try {
|
|
54
|
-
scopedDirents = await fs.readdir(scopeDir, {
|
|
55
|
-
withFileTypes: true,
|
|
56
|
-
encoding: "utf8",
|
|
57
|
-
});
|
|
58
|
-
} catch {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
for (const scoped of scopedDirents) {
|
|
62
|
-
if (scoped.isSymbolicLink()) {
|
|
63
|
-
await registerSymlink(
|
|
64
|
-
path.join(scopeDir, scoped.name),
|
|
65
|
-
`${dirent.name}/${scoped.name}`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} else if (dirent.isSymbolicLink()) {
|
|
70
|
-
await registerSymlink(
|
|
71
|
-
path.join(nodeModulesDir, dirent.name),
|
|
72
|
-
dirent.name
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function registerSymlink(
|
|
79
|
-
symlinkPath: string,
|
|
80
|
-
pkgName: string
|
|
81
|
-
): Promise<void> {
|
|
82
|
-
try {
|
|
83
|
-
const realPath = await fs.realpath(symlinkPath);
|
|
84
|
-
const key = realPath.replace(/\\/g, "/");
|
|
85
|
-
const value = `/node_modules/${pkgName}`;
|
|
86
|
-
registry.set(key, value);
|
|
87
|
-
console.log(`[SWITE] Registry: ${pkgName}: ${key} → ${value}`);
|
|
88
|
-
} catch {
|
|
89
|
-
// broken symlink — ignore
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Look up an absolute filesystem path in the symlink registry.
|
|
95
|
-
*
|
|
96
|
-
* Returns the browser URL if absolutePath is, or is within, a package whose
|
|
97
|
-
* realpath was registered at startup. Returns null if not found.
|
|
98
|
-
*
|
|
99
|
-
* Example:
|
|
100
|
-
* /mnt/c/.../swiss-lib/packages/core/src/index.ts
|
|
101
|
-
* → /node_modules/@swissjs/core/src/index.ts
|
|
102
|
-
*/
|
|
103
|
-
export function lookupInSymlinkRegistry(absolutePath: string): string | null {
|
|
104
|
-
const normalized = absolutePath.replace(/\\/g, "/");
|
|
105
|
-
for (const [realPkgPath, browserPrefix] of registry) {
|
|
106
|
-
if (normalized === realPkgPath) {
|
|
107
|
-
return browserPrefix;
|
|
108
|
-
}
|
|
109
|
-
if (normalized.startsWith(realPkgPath + "/")) {
|
|
110
|
-
return browserPrefix + normalized.slice(realPkgPath.length);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Symlink Registry - CG-03 root cause fix
|
|
3
|
+
*
|
|
4
|
+
* fs.realpath() throughout Swite's handler chain resolves symlinks to absolute
|
|
5
|
+
* filesystem paths (e.g. /mnt/c/.../swiss-lib/packages/core/src/index.ts).
|
|
6
|
+
* These leak into toUrl() and hit the startsWith("/") early-return before the
|
|
7
|
+
* proper node_modules/swiss-lib handling logic is reached.
|
|
8
|
+
*
|
|
9
|
+
* At server startup we scan node_modules directories for symlinks and build a
|
|
10
|
+
* map: realpath → /node_modules/<pkg-name>
|
|
11
|
+
* toUrl() consults this registry FIRST and short-circuits back to the correct
|
|
12
|
+
* browser URL.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { promises as fs } from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
// realpath (normalized, forward slashes) → browser URL prefix
|
|
19
|
+
const registry = new Map<string, string>();
|
|
20
|
+
|
|
21
|
+
export async function buildSymlinkRegistry(
|
|
22
|
+
nodeModulesDirs: string[]
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
registry.clear();
|
|
25
|
+
for (const dir of nodeModulesDirs) {
|
|
26
|
+
await scanNodeModulesDir(dir);
|
|
27
|
+
}
|
|
28
|
+
console.log(
|
|
29
|
+
`[SWITE] Symlink registry built: ${registry.size} entries from ${nodeModulesDirs.length} node_modules dirs`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function scanNodeModulesDir(nodeModulesDir: string): Promise<void> {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
let dirents: any[];
|
|
36
|
+
try {
|
|
37
|
+
dirents = await fs.readdir(nodeModulesDir, {
|
|
38
|
+
withFileTypes: true,
|
|
39
|
+
encoding: "utf8",
|
|
40
|
+
});
|
|
41
|
+
} catch {
|
|
42
|
+
return; // dir doesn't exist — skip silently
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const dirent of dirents) {
|
|
46
|
+
if (dirent.name.startsWith(".")) continue;
|
|
47
|
+
|
|
48
|
+
if (dirent.name.startsWith("@")) {
|
|
49
|
+
// Scoped scope directory — scan one level deeper
|
|
50
|
+
const scopeDir = path.join(nodeModulesDir, dirent.name);
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
let scopedDirents: any[];
|
|
53
|
+
try {
|
|
54
|
+
scopedDirents = await fs.readdir(scopeDir, {
|
|
55
|
+
withFileTypes: true,
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
});
|
|
58
|
+
} catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
for (const scoped of scopedDirents) {
|
|
62
|
+
if (scoped.isSymbolicLink()) {
|
|
63
|
+
await registerSymlink(
|
|
64
|
+
path.join(scopeDir, scoped.name),
|
|
65
|
+
`${dirent.name}/${scoped.name}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else if (dirent.isSymbolicLink()) {
|
|
70
|
+
await registerSymlink(
|
|
71
|
+
path.join(nodeModulesDir, dirent.name),
|
|
72
|
+
dirent.name
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function registerSymlink(
|
|
79
|
+
symlinkPath: string,
|
|
80
|
+
pkgName: string
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
try {
|
|
83
|
+
const realPath = await fs.realpath(symlinkPath);
|
|
84
|
+
const key = realPath.replace(/\\/g, "/");
|
|
85
|
+
const value = `/node_modules/${pkgName}`;
|
|
86
|
+
registry.set(key, value);
|
|
87
|
+
console.log(`[SWITE] Registry: ${pkgName}: ${key} → ${value}`);
|
|
88
|
+
} catch {
|
|
89
|
+
// broken symlink — ignore
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Look up an absolute filesystem path in the symlink registry.
|
|
95
|
+
*
|
|
96
|
+
* Returns the browser URL if absolutePath is, or is within, a package whose
|
|
97
|
+
* realpath was registered at startup. Returns null if not found.
|
|
98
|
+
*
|
|
99
|
+
* Example:
|
|
100
|
+
* /mnt/c/.../swiss-lib/packages/core/src/index.ts
|
|
101
|
+
* → /node_modules/@swissjs/core/src/index.ts
|
|
102
|
+
*/
|
|
103
|
+
export function lookupInSymlinkRegistry(absolutePath: string): string | null {
|
|
104
|
+
const normalized = absolutePath.replace(/\\/g, "/");
|
|
105
|
+
for (const [realPkgPath, browserPrefix] of registry) {
|
|
106
|
+
if (normalized === realPkgPath) {
|
|
107
|
+
return browserPrefix;
|
|
108
|
+
}
|
|
109
|
+
if (normalized.startsWith(realPkgPath + "/")) {
|
|
110
|
+
return browserPrefix + normalized.slice(realPkgPath.length);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|