@swissjs/swite 0.3.0
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/.changeset/config.json +11 -0
- package/.github/workflows/ci.yml +59 -0
- package/.github/workflows/publish.yml +50 -0
- package/.github/workflows/release.yml +53 -0
- package/BUILD_ANALYSIS.md +89 -0
- package/BUILD_STRATEGY.md +75 -0
- package/CHANGELOG.md +53 -0
- package/DIRECTIVE.md +488 -0
- package/__tests__/css-extraction.test.ts +261 -0
- package/__tests__/css-injection-integration.test.ts +247 -0
- package/__tests__/css-middleware.test.ts +191 -0
- package/__tests__/import-rewriter-bug.test.ts +135 -0
- package/dist/builder.d.ts +36 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +772 -0
- package/dist/cache/compilation-cache.d.ts +33 -0
- package/dist/cache/compilation-cache.d.ts.map +1 -0
- package/dist/cache/compilation-cache.js +130 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/config-loader.d.ts +8 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +40 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +7 -0
- package/dist/dev/pythonDevManager.d.ts +12 -0
- package/dist/dev/pythonDevManager.d.ts.map +1 -0
- package/dist/dev/pythonDevManager.js +85 -0
- package/dist/env.d.ts +19 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +112 -0
- package/dist/handlers/base-handler.d.ts +21 -0
- package/dist/handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/base-handler.js +38 -0
- package/dist/handlers/js-handler.d.ts +10 -0
- package/dist/handlers/js-handler.d.ts.map +1 -0
- package/dist/handlers/js-handler.js +87 -0
- package/dist/handlers/mjs-handler.d.ts +8 -0
- package/dist/handlers/mjs-handler.d.ts.map +1 -0
- package/dist/handlers/mjs-handler.js +44 -0
- package/dist/handlers/node-module-handler.d.ts +16 -0
- package/dist/handlers/node-module-handler.d.ts.map +1 -0
- package/dist/handlers/node-module-handler.js +267 -0
- package/dist/handlers/ts-handler.d.ts +11 -0
- package/dist/handlers/ts-handler.d.ts.map +1 -0
- package/dist/handlers/ts-handler.js +120 -0
- package/dist/handlers/ui-handler.d.ts +12 -0
- package/dist/handlers/ui-handler.d.ts.map +1 -0
- package/dist/handlers/ui-handler.js +182 -0
- package/dist/handlers/uix-handler.d.ts +12 -0
- package/dist/handlers/uix-handler.d.ts.map +1 -0
- package/dist/handlers/uix-handler.js +135 -0
- package/dist/hmr.d.ts +20 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +265 -0
- package/dist/import-rewriter.d.ts +3 -0
- package/dist/import-rewriter.d.ts.map +1 -0
- package/dist/import-rewriter.js +351 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/middleware/hmr-routes.d.ts +12 -0
- package/dist/middleware/hmr-routes.d.ts.map +1 -0
- package/dist/middleware/hmr-routes.js +97 -0
- package/dist/middleware/middleware-setup.d.ts +23 -0
- package/dist/middleware/middleware-setup.d.ts.map +1 -0
- package/dist/middleware/middleware-setup.js +596 -0
- package/dist/middleware/static-files.d.ts +15 -0
- package/dist/middleware/static-files.d.ts.map +1 -0
- package/dist/middleware/static-files.js +585 -0
- package/dist/proxy/SwiteProxyError.d.ts +6 -0
- package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
- package/dist/proxy/SwiteProxyError.js +9 -0
- package/dist/proxy/proxyToPython.d.ts +28 -0
- package/dist/proxy/proxyToPython.d.ts.map +1 -0
- package/dist/proxy/proxyToPython.js +66 -0
- package/dist/resolver/bare-import-resolver.d.ts +9 -0
- package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
- package/dist/resolver/bare-import-resolver.js +363 -0
- package/dist/resolver/symlink-registry.d.ts +13 -0
- package/dist/resolver/symlink-registry.d.ts.map +1 -0
- package/dist/resolver/symlink-registry.js +98 -0
- package/dist/resolver/url-resolver.d.ts +11 -0
- package/dist/resolver/url-resolver.d.ts.map +1 -0
- package/dist/resolver/url-resolver.js +268 -0
- package/dist/resolver/workspace-package-resolver.d.ts +10 -0
- package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
- package/dist/resolver/workspace-package-resolver.js +185 -0
- package/dist/resolver.d.ts +17 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +191 -0
- package/dist/router/file-router.d.ts +19 -0
- package/dist/router/file-router.d.ts.map +1 -0
- package/dist/router/file-router.js +114 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/utils/cdn-fallback.d.ts +14 -0
- package/dist/utils/cdn-fallback.d.ts.map +1 -0
- package/dist/utils/cdn-fallback.js +36 -0
- package/dist/utils/file-path-resolver.d.ts +9 -0
- package/dist/utils/file-path-resolver.d.ts.map +1 -0
- package/dist/utils/file-path-resolver.js +187 -0
- package/dist/utils/generate-import-map-cli.d.ts +3 -0
- package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
- package/dist/utils/generate-import-map-cli.js +32 -0
- package/dist/utils/generate-import-map.d.ts +21 -0
- package/dist/utils/generate-import-map.d.ts.map +1 -0
- package/dist/utils/generate-import-map.js +119 -0
- package/dist/utils/package-finder.d.ts +24 -0
- package/dist/utils/package-finder.d.ts.map +1 -0
- package/dist/utils/package-finder.js +161 -0
- package/dist/utils/package-registry.d.ts +36 -0
- package/dist/utils/package-registry.d.ts.map +1 -0
- package/dist/utils/package-registry.js +159 -0
- package/dist/utils/workspace.d.ts +6 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +65 -0
- package/docs/IMPORT_REWRITING.md +164 -0
- package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
- package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
- package/package.json +49 -0
- package/src/adapters/proxy/SwiteProxyError.ts +12 -0
- package/src/adapters/proxy/proxyToPython.ts +88 -0
- package/src/build-engine/builder.ts +960 -0
- package/src/cli.ts +109 -0
- package/src/config/config-loader.ts +46 -0
- package/src/config/config.ts +34 -0
- package/src/config/env.ts +98 -0
- package/src/dev-engine/handlers/base-handler.ts +68 -0
- package/src/dev-engine/handlers/js-handler.ts +134 -0
- package/src/dev-engine/handlers/mjs-handler.ts +65 -0
- package/src/dev-engine/handlers/node-module-handler.ts +339 -0
- package/src/dev-engine/handlers/ts-handler.ts +143 -0
- package/src/dev-engine/handlers/ui-handler.ts +105 -0
- package/src/dev-engine/handlers/uix-handler.ts +90 -0
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
- package/src/dev-engine/hmr/hmr.ts +173 -0
- package/src/dev-engine/middleware/hmr-routes.ts +120 -0
- package/src/dev-engine/middleware/middleware-setup.ts +351 -0
- package/src/dev-engine/middleware/static-files.ts +728 -0
- package/src/dev-engine/pythonDevManager.ts +116 -0
- package/src/dev-engine/router/file-router.ts +164 -0
- package/src/dev-engine/server.ts +152 -0
- package/src/index.ts +26 -0
- package/src/internal/cache/compilation-cache.ts +182 -0
- package/src/internal/generate-import-map-cli.ts +40 -0
- package/src/internal/generate-import-map.ts +154 -0
- package/src/kernel/package-finder.ts +164 -0
- package/src/kernel/package-registry.ts +198 -0
- package/src/kernel/workspace.ts +62 -0
- package/src/resolution/bare-import-resolver.ts +400 -0
- package/src/resolution/cdn/cdn-fallback.ts +37 -0
- package/src/resolution/path/file-path-resolver.ts +190 -0
- package/src/resolution/path/path-fixup.ts +19 -0
- package/src/resolution/resolver.ts +198 -0
- package/src/resolution/rewriting/import-rewriter.ts +237 -0
- package/src/resolution/symlink-registry.ts +114 -0
- package/src/resolution/url-resolver.ts +231 -0
- package/src/resolution/workspace-package-resolver.ts +94 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { findWorkspaceRoot } from "./workspace.js";
|
|
9
|
+
import { findSwissLibMonorepo } from "./package-finder.js";
|
|
10
|
+
/**
|
|
11
|
+
* Resolve file path from URL, handling SWISS packages, workspace packages, and app files
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveFilePath(url, root, workspaceRoot = null) {
|
|
14
|
+
// /node_modules/ URLs: walk up from app root until we find the package.
|
|
15
|
+
// pnpm may place deps at the app root, one level up (workspace pkg), or at
|
|
16
|
+
// the monorepo root depending on hoisting config and pnpm version.
|
|
17
|
+
if (url.startsWith("/node_modules/")) {
|
|
18
|
+
const urlPath = url.startsWith("/") ? url.slice(1) : url;
|
|
19
|
+
// Walk up the directory tree from root, trying node_modules at each level
|
|
20
|
+
let current = path.resolve(root);
|
|
21
|
+
const visited = new Set();
|
|
22
|
+
for (let i = 0; i < 8; i++) {
|
|
23
|
+
const candidate = path.join(current, urlPath);
|
|
24
|
+
if (!visited.has(candidate)) {
|
|
25
|
+
visited.add(candidate);
|
|
26
|
+
try {
|
|
27
|
+
const resolved = await fs.realpath(candidate);
|
|
28
|
+
await fs.access(resolved);
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// try parent level
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const parent = path.dirname(current);
|
|
36
|
+
if (parent === current)
|
|
37
|
+
break; // filesystem root
|
|
38
|
+
current = parent;
|
|
39
|
+
}
|
|
40
|
+
// Explicit workspace root (covers hoisted-to-root installs)
|
|
41
|
+
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
42
|
+
if (wsRoot) {
|
|
43
|
+
const wsPath = path.join(wsRoot, urlPath);
|
|
44
|
+
if (!visited.has(wsPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const resolved = await fs.realpath(wsPath);
|
|
47
|
+
await fs.access(resolved);
|
|
48
|
+
return resolved;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// not found there either
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return path.join(path.resolve(root), urlPath); // fallback; handler will 404
|
|
56
|
+
}
|
|
57
|
+
// Check if this is a swiss-lib package file
|
|
58
|
+
if (url.startsWith("/swiss-packages/")) {
|
|
59
|
+
// Dynamically find swiss-lib monorepo instead of hardcoded paths
|
|
60
|
+
const swissLib = await findSwissLibMonorepo(root);
|
|
61
|
+
if (swissLib) {
|
|
62
|
+
// Remove /swiss-packages prefix and use the rest as relative path
|
|
63
|
+
const relativePath = url.replace(/^\/swiss-packages\//, "");
|
|
64
|
+
const swissPackagesPath = path.join(swissLib, "packages");
|
|
65
|
+
const fullPath = path.join(swissPackagesPath, relativePath);
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(fullPath);
|
|
68
|
+
console.log(`[file-path-resolver] Found swiss-lib package at: ${fullPath}`);
|
|
69
|
+
return fullPath;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
console.warn(`[file-path-resolver] swiss-lib package file not found: ${fullPath}`);
|
|
73
|
+
return fullPath; // Return path anyway, will error later if needed
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Fallback: construct path from root (may not work, but better than nothing)
|
|
78
|
+
const relativePath = url.replace(/^\/swiss-packages\//, "");
|
|
79
|
+
const fallbackPath = path.join(root, "..", "..", "..", "swiss-lib", "packages", relativePath);
|
|
80
|
+
console.warn(`[file-path-resolver] swiss-lib not found, using fallback: ${fallbackPath}`);
|
|
81
|
+
return fallbackPath;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Workspace-level directories: always resolve from workspace root
|
|
85
|
+
// Updated: lib/ now contains all packages (moved from packages/)
|
|
86
|
+
if (url.startsWith("/lib/") ||
|
|
87
|
+
url.startsWith("/libraries/") ||
|
|
88
|
+
url.startsWith("/packages/") ||
|
|
89
|
+
url.startsWith("/modules/")) {
|
|
90
|
+
let wsRoot = workspaceRoot;
|
|
91
|
+
if (!wsRoot) {
|
|
92
|
+
wsRoot = await findWorkspaceRoot(root);
|
|
93
|
+
console.log(`[file-path-resolver] Detected workspace root: ${wsRoot} (from app root: ${root})`);
|
|
94
|
+
}
|
|
95
|
+
// Normalize URL: path.join with leading slash is wrong on Windows (treats as drive root)
|
|
96
|
+
const urlPath = url.startsWith("/") ? url.slice(1) : url;
|
|
97
|
+
// CRITICAL: For /lib/ paths, we MUST find the SWS root (which has lib/ directory)
|
|
98
|
+
// Start from app root and walk up until we find a directory with both pnpm-workspace.yaml AND lib/
|
|
99
|
+
if (url.startsWith("/lib/")) {
|
|
100
|
+
let current = root;
|
|
101
|
+
for (let i = 0; i < 10; i++) {
|
|
102
|
+
const workspaceFile = path.join(current, "pnpm-workspace.yaml");
|
|
103
|
+
const libDir = path.join(current, "lib");
|
|
104
|
+
try {
|
|
105
|
+
await fs.access(workspaceFile);
|
|
106
|
+
await fs.access(libDir);
|
|
107
|
+
// Found SWS root!
|
|
108
|
+
const resolved = path.join(current, urlPath);
|
|
109
|
+
console.log(`[file-path-resolver] Found SWS root with lib/: ${current}`);
|
|
110
|
+
console.log(`[file-path-resolver] Resolving ${url} from SWS root: ${current} -> ${resolved}`);
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Continue searching up
|
|
115
|
+
}
|
|
116
|
+
const parent = path.dirname(current);
|
|
117
|
+
if (parent === current)
|
|
118
|
+
break;
|
|
119
|
+
current = parent;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// For other paths, use detected workspace root
|
|
123
|
+
if (wsRoot) {
|
|
124
|
+
let resolved = path.join(wsRoot, urlPath);
|
|
125
|
+
// Dev fallback: if URL is /packages/.../dist/... and file doesn't exist, try src/ (unbuilt workspace packages)
|
|
126
|
+
if ((url.startsWith("/packages/") || url.startsWith("/lib/")) &&
|
|
127
|
+
url.includes("/dist/")) {
|
|
128
|
+
try {
|
|
129
|
+
await fs.access(resolved);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
const srcUrl = urlPath.replace("/dist/", "/src/").replace(/\.js$/, ".ts");
|
|
133
|
+
const srcResolved = path.join(wsRoot, srcUrl);
|
|
134
|
+
try {
|
|
135
|
+
await fs.access(srcResolved);
|
|
136
|
+
console.log(`[file-path-resolver] dist not found, serving src: ${resolved} -> ${srcResolved}`);
|
|
137
|
+
return srcResolved;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Keep original resolved; handler will 404
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
console.log(`[file-path-resolver] Resolving ${url} from workspace root: ${wsRoot} -> ${resolved}`);
|
|
145
|
+
return resolved;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.warn(`[file-path-resolver] No workspace root found, using app root: ${root}`);
|
|
149
|
+
return path.join(root, urlPath);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// For app files, check if URL already includes the app path
|
|
153
|
+
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
154
|
+
if (wsRoot) {
|
|
155
|
+
const appRelativeToWorkspace = path
|
|
156
|
+
.relative(wsRoot, root)
|
|
157
|
+
.replace(/\\/g, "/");
|
|
158
|
+
if (url.startsWith(`/${appRelativeToWorkspace}/`)) {
|
|
159
|
+
// URL already includes app path, use workspace root
|
|
160
|
+
return path.join(wsRoot, url);
|
|
161
|
+
}
|
|
162
|
+
else if (url.startsWith("/src/") ||
|
|
163
|
+
url.startsWith("/public/") ||
|
|
164
|
+
url.startsWith("/assets/")) {
|
|
165
|
+
// App-specific paths (src/, public/, assets/) - resolve from app root
|
|
166
|
+
return path.join(root, url);
|
|
167
|
+
}
|
|
168
|
+
else if (url.startsWith("/")) {
|
|
169
|
+
// Other absolute URLs, try workspace root first, then app root
|
|
170
|
+
const workspacePath = path.join(wsRoot, url);
|
|
171
|
+
try {
|
|
172
|
+
await fs.access(workspacePath);
|
|
173
|
+
return workspacePath;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return path.join(root, url);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Relative to app root
|
|
181
|
+
return path.join(root, url);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
return path.join(root, url);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-import-map-cli.d.ts","sourceRoot":"","sources":["../../src/utils/generate-import-map-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
4
|
+
* SWITE - SWISS Development Server
|
|
5
|
+
* CLI tool to generate import maps
|
|
6
|
+
* Licensed under the MIT License.
|
|
7
|
+
*/
|
|
8
|
+
import { generateImportMap, saveImportMap } from "./generate-import-map.js";
|
|
9
|
+
import { findWorkspaceRoot } from "./workspace.js";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
async function main() {
|
|
15
|
+
// Find app root (where swite is running from)
|
|
16
|
+
// This script is typically run from the app directory
|
|
17
|
+
const appRoot = process.cwd();
|
|
18
|
+
const workspaceRoot = await findWorkspaceRoot(appRoot);
|
|
19
|
+
console.log(`[ImportMap] App root: ${appRoot}`);
|
|
20
|
+
console.log(`[ImportMap] Workspace root: ${workspaceRoot || "none"}`);
|
|
21
|
+
// Generate import map
|
|
22
|
+
const importMap = await generateImportMap(appRoot, workspaceRoot);
|
|
23
|
+
// Save to .swite/import-map.json in app root
|
|
24
|
+
const outputPath = path.join(appRoot, ".swite", "import-map.json");
|
|
25
|
+
await saveImportMap(importMap, outputPath);
|
|
26
|
+
console.log(`[ImportMap] ✅ Import map generated successfully`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
main().catch((error) => {
|
|
30
|
+
console.error("[ImportMap] Error:", error);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ImportMap {
|
|
2
|
+
version: string;
|
|
3
|
+
generated: number;
|
|
4
|
+
imports: {
|
|
5
|
+
[specifier: string]: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Scan workspace and generate import map
|
|
10
|
+
* This pre-resolves all known packages to eliminate runtime scanning
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateImportMap(root: string, workspaceRoot: string | null): Promise<ImportMap>;
|
|
13
|
+
/**
|
|
14
|
+
* Save import map to file
|
|
15
|
+
*/
|
|
16
|
+
export declare function saveImportMap(importMap: ImportMap, outputPath: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Load import map from file
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadImportMap(filePath: string): Promise<ImportMap | null>;
|
|
21
|
+
//# sourceMappingURL=generate-import-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-import-map.d.ts","sourceRoot":"","sources":["../../src/utils/generate-import-map.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;KAC7B,CAAC;CACH;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,OAAO,CAAC,SAAS,CAAC,CAkGpB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAO3B"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Generate pre-resolved import maps at build time
|
|
5
|
+
* Licensed under the MIT License.
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { ModuleResolver } from "../resolver.js";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import { getPackageRegistry } from "./package-registry.js";
|
|
12
|
+
import { findSwissLibMonorepo } from "./package-finder.js";
|
|
13
|
+
/**
|
|
14
|
+
* Scan workspace and generate import map
|
|
15
|
+
* This pre-resolves all known packages to eliminate runtime scanning
|
|
16
|
+
*/
|
|
17
|
+
export async function generateImportMap(root, workspaceRoot) {
|
|
18
|
+
const resolver = new ModuleResolver(root);
|
|
19
|
+
const importMap = {
|
|
20
|
+
version: "1.0",
|
|
21
|
+
generated: Date.now(),
|
|
22
|
+
imports: {},
|
|
23
|
+
};
|
|
24
|
+
console.log(chalk.blue("[ImportMap] Using dynamic package registry..."));
|
|
25
|
+
// Use dynamic package registry instead of manual scanning
|
|
26
|
+
const registry = getPackageRegistry();
|
|
27
|
+
const scanRoot = workspaceRoot || root;
|
|
28
|
+
if (!scanRoot) {
|
|
29
|
+
console.warn(chalk.yellow("[ImportMap] No workspace root or app root provided, cannot scan packages"));
|
|
30
|
+
return importMap;
|
|
31
|
+
}
|
|
32
|
+
// Add swiss-lib monorepo if it exists
|
|
33
|
+
const additionalRoots = [];
|
|
34
|
+
const swissLib = await findSwissLibMonorepo(root);
|
|
35
|
+
if (swissLib) {
|
|
36
|
+
try {
|
|
37
|
+
const swissPackageJson = path.join(swissLib, "package.json");
|
|
38
|
+
await fs.access(swissPackageJson);
|
|
39
|
+
additionalRoots.push(swissLib);
|
|
40
|
+
console.log(chalk.blue("[ImportMap] Including swiss-lib monorepo..."));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// swiss-lib monorepo not accessible, skip
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Scan workspace using registry
|
|
47
|
+
try {
|
|
48
|
+
await registry.scanWorkspace(scanRoot, additionalRoots);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(chalk.red(`[ImportMap] Error scanning workspace: ${error.message}`));
|
|
52
|
+
return importMap;
|
|
53
|
+
}
|
|
54
|
+
// Get all discovered packages
|
|
55
|
+
const packages = registry.getAllPackages().map(pkg => ({
|
|
56
|
+
name: pkg.name,
|
|
57
|
+
path: pkg.path,
|
|
58
|
+
}));
|
|
59
|
+
console.log(chalk.blue(`[ImportMap] Resolving ${packages.length} packages...`));
|
|
60
|
+
// Resolve each package
|
|
61
|
+
let resolved = 0;
|
|
62
|
+
for (const pkg of packages) {
|
|
63
|
+
try {
|
|
64
|
+
// Resolve main export
|
|
65
|
+
const resolvedPath = await resolver.resolve(pkg.name, "");
|
|
66
|
+
if (resolvedPath && !resolvedPath.startsWith("http")) {
|
|
67
|
+
importMap.imports[pkg.name] = resolvedPath;
|
|
68
|
+
resolved++;
|
|
69
|
+
}
|
|
70
|
+
// Also resolve common subpaths (components, tokens, etc.)
|
|
71
|
+
const commonSubpaths = [
|
|
72
|
+
"/components",
|
|
73
|
+
"/tokens",
|
|
74
|
+
"/context",
|
|
75
|
+
"/shell",
|
|
76
|
+
"/jsx-runtime",
|
|
77
|
+
"/jsx-dev-runtime",
|
|
78
|
+
];
|
|
79
|
+
for (const subpath of commonSubpaths) {
|
|
80
|
+
try {
|
|
81
|
+
const subpathSpecifier = `${pkg.name}${subpath}`;
|
|
82
|
+
const subpathResolved = await resolver.resolve(subpathSpecifier, "");
|
|
83
|
+
if (subpathResolved && !subpathResolved.startsWith("http")) {
|
|
84
|
+
importMap.imports[subpathSpecifier] = subpathResolved;
|
|
85
|
+
resolved++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Subpath doesn't exist, skip
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.warn(chalk.yellow(`[ImportMap] Failed to resolve ${pkg.name}:`, error));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
console.log(chalk.green(`[ImportMap] ✅ Generated import map with ${resolved} entries`));
|
|
98
|
+
return importMap;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Save import map to file
|
|
102
|
+
*/
|
|
103
|
+
export async function saveImportMap(importMap, outputPath) {
|
|
104
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
105
|
+
await fs.writeFile(outputPath, JSON.stringify(importMap, null, 2), "utf-8");
|
|
106
|
+
console.log(chalk.green(`[ImportMap] ✅ Saved to ${outputPath}`));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Load import map from file
|
|
110
|
+
*/
|
|
111
|
+
export async function loadImportMap(filePath) {
|
|
112
|
+
try {
|
|
113
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
114
|
+
return JSON.parse(content);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically find package directories by searching up the file tree
|
|
3
|
+
* No hardcoded paths - works from any directory structure
|
|
4
|
+
*/
|
|
5
|
+
export interface PackageLocation {
|
|
6
|
+
path: string;
|
|
7
|
+
type: 'swiss-lib' | 'workspace' | 'node_modules';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Find swiss-lib monorepo by searching for swiss-lib/package.json or swiss-lib/packages/core
|
|
11
|
+
*/
|
|
12
|
+
export declare function findSwissLibMonorepo(startPath: string): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Find a specific package by name, searching in:
|
|
15
|
+
* 1. node_modules (local and workspace)
|
|
16
|
+
* 2. swiss-lib/packages (if found)
|
|
17
|
+
* 3. workspace packages (lib/, packages/, modules/)
|
|
18
|
+
*/
|
|
19
|
+
export declare function findPackage(packageName: string, startPath: string, workspaceRoot?: string | null): Promise<PackageLocation | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Find all possible workspace roots by searching up the tree
|
|
22
|
+
*/
|
|
23
|
+
export declare function findWorkspaceRoots(startPath: string): Promise<string[]>;
|
|
24
|
+
//# sourceMappingURL=package-finder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-finder.d.ts","sourceRoot":"","sources":["../../src/utils/package-finder.ts"],"names":[],"mappings":"AASA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;CAClD;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0DpF;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAoDjC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B7E"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
/**
|
|
9
|
+
* Find swiss-lib monorepo by searching for swiss-lib/package.json or swiss-lib/packages/core
|
|
10
|
+
*/
|
|
11
|
+
export async function findSwissLibMonorepo(startPath) {
|
|
12
|
+
let current = startPath;
|
|
13
|
+
for (let i = 0; i < 20; i++) { // Search up to 20 levels
|
|
14
|
+
// Check for swiss-lib directory with packages/core
|
|
15
|
+
const swissLibPath = path.join(current, "swiss-lib");
|
|
16
|
+
const swissLibPackageJson = path.join(swissLibPath, "package.json");
|
|
17
|
+
const corePackage = path.join(swissLibPath, "packages", "core", "package.json");
|
|
18
|
+
try {
|
|
19
|
+
// Check if swiss-lib exists and has core package
|
|
20
|
+
if (await fileExists(swissLibPackageJson) || await fileExists(corePackage)) {
|
|
21
|
+
console.log(`[package-finder] Found swiss-lib at: ${swissLibPath}`);
|
|
22
|
+
return swissLibPath;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Continue searching
|
|
27
|
+
}
|
|
28
|
+
// Also check for legacy SWISS directory
|
|
29
|
+
const swissPath = path.join(current, "SWISS");
|
|
30
|
+
const swissPackageJson = path.join(swissPath, "package.json");
|
|
31
|
+
const swissCorePackage = path.join(swissPath, "packages", "core", "package.json");
|
|
32
|
+
try {
|
|
33
|
+
if (await fileExists(swissPackageJson) || await fileExists(swissCorePackage)) {
|
|
34
|
+
console.log(`[package-finder] Found legacy SWISS at: ${swissPath}`);
|
|
35
|
+
return swissPath;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Continue searching
|
|
40
|
+
}
|
|
41
|
+
// Scan immediate subdirectories of `current` for a swiss-lib/ child
|
|
42
|
+
try {
|
|
43
|
+
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
44
|
+
const subdirs = entries.filter((e) => e.name !== "node_modules" && (e.isDirectory() || e.isSymbolicLink()));
|
|
45
|
+
for (const entry of subdirs) {
|
|
46
|
+
const sub = path.join(current, entry.name);
|
|
47
|
+
const subSwissLib = path.join(sub, "swiss-lib");
|
|
48
|
+
const subPkgJson = path.join(subSwissLib, "package.json");
|
|
49
|
+
const subCorePkgJson = path.join(subSwissLib, "packages", "core", "package.json");
|
|
50
|
+
if (await fileExists(subPkgJson) || await fileExists(subCorePkgJson)) {
|
|
51
|
+
console.log(`[package-finder] Found swiss-lib via subdir scan at: ${subSwissLib}`);
|
|
52
|
+
return subSwissLib;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Skip on permission errors
|
|
58
|
+
}
|
|
59
|
+
const parent = path.dirname(current);
|
|
60
|
+
if (parent === current)
|
|
61
|
+
break;
|
|
62
|
+
current = parent;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Find a specific package by name, searching in:
|
|
68
|
+
* 1. node_modules (local and workspace)
|
|
69
|
+
* 2. swiss-lib/packages (if found)
|
|
70
|
+
* 3. workspace packages (lib/, packages/, modules/)
|
|
71
|
+
*/
|
|
72
|
+
export async function findPackage(packageName, startPath, workspaceRoot) {
|
|
73
|
+
// 1. Check local node_modules
|
|
74
|
+
const localNodeModules = path.join(startPath, "node_modules", packageName);
|
|
75
|
+
if (await fileExists(path.join(localNodeModules, "package.json"))) {
|
|
76
|
+
return { path: localNodeModules, type: 'node_modules' };
|
|
77
|
+
}
|
|
78
|
+
// 2. Check workspace root node_modules
|
|
79
|
+
if (workspaceRoot) {
|
|
80
|
+
const workspaceNodeModules = path.join(workspaceRoot, "node_modules", packageName);
|
|
81
|
+
if (await fileExists(path.join(workspaceNodeModules, "package.json"))) {
|
|
82
|
+
return { path: workspaceNodeModules, type: 'node_modules' };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 3. Check swiss-lib monorepo (for @kibologic/* packages)
|
|
86
|
+
if (packageName.startsWith("@kibologic/")) {
|
|
87
|
+
const swissLib = await findSwissLibMonorepo(startPath);
|
|
88
|
+
if (swissLib) {
|
|
89
|
+
const packageDir = packageName.replace("@kibologic/", "");
|
|
90
|
+
const swissPackage = path.join(swissLib, "packages", packageDir);
|
|
91
|
+
if (await fileExists(path.join(swissPackage, "package.json"))) {
|
|
92
|
+
return { path: swissPackage, type: 'swiss-lib' };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// 4. Check workspace packages (lib/, packages/, modules/)
|
|
97
|
+
if (workspaceRoot) {
|
|
98
|
+
const packageDirs = ["lib", "packages", "modules", "libraries", "apps"];
|
|
99
|
+
for (const dir of packageDirs) {
|
|
100
|
+
const searchDir = path.join(workspaceRoot, dir);
|
|
101
|
+
if (!(await fileExists(searchDir)))
|
|
102
|
+
continue;
|
|
103
|
+
// Try scoped package name
|
|
104
|
+
if (packageName.startsWith("@")) {
|
|
105
|
+
const unscoped = packageName.split("/")[1];
|
|
106
|
+
const packagePath = path.join(searchDir, unscoped);
|
|
107
|
+
if (await fileExists(path.join(packagePath, "package.json"))) {
|
|
108
|
+
return { path: packagePath, type: 'workspace' };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Try full package name
|
|
112
|
+
const packagePath = path.join(searchDir, packageName);
|
|
113
|
+
if (await fileExists(path.join(packagePath, "package.json"))) {
|
|
114
|
+
return { path: packagePath, type: 'workspace' };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Find all possible workspace roots by searching up the tree
|
|
122
|
+
*/
|
|
123
|
+
export async function findWorkspaceRoots(startPath) {
|
|
124
|
+
const roots = [];
|
|
125
|
+
let current = startPath;
|
|
126
|
+
for (let i = 0; i < 20; i++) {
|
|
127
|
+
const workspaceFile = path.join(current, "pnpm-workspace.yaml");
|
|
128
|
+
const packageJson = path.join(current, "package.json");
|
|
129
|
+
try {
|
|
130
|
+
if (await fileExists(workspaceFile)) {
|
|
131
|
+
roots.push(current);
|
|
132
|
+
}
|
|
133
|
+
else if (await fileExists(packageJson)) {
|
|
134
|
+
const pkg = JSON.parse(await fs.readFile(packageJson, "utf-8"));
|
|
135
|
+
if (pkg?.workspaces) {
|
|
136
|
+
roots.push(current);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Continue
|
|
142
|
+
}
|
|
143
|
+
const parent = path.dirname(current);
|
|
144
|
+
if (parent === current)
|
|
145
|
+
break;
|
|
146
|
+
current = parent;
|
|
147
|
+
}
|
|
148
|
+
return roots;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if a file exists
|
|
152
|
+
*/
|
|
153
|
+
async function fileExists(filePath) {
|
|
154
|
+
try {
|
|
155
|
+
await fs.access(filePath);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface PackageInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
packageJson: any;
|
|
5
|
+
}
|
|
6
|
+
export declare class PackageRegistry {
|
|
7
|
+
private packages;
|
|
8
|
+
private scanned;
|
|
9
|
+
private scanRoots;
|
|
10
|
+
/**
|
|
11
|
+
* Scan workspace for all packages
|
|
12
|
+
*/
|
|
13
|
+
scanWorkspace(workspaceRoot: string, additionalRoots?: string[]): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Recursively scan directory for package.json files
|
|
16
|
+
*/
|
|
17
|
+
private scanDirectory;
|
|
18
|
+
/**
|
|
19
|
+
* Find package by name
|
|
20
|
+
*/
|
|
21
|
+
findPackage(packageName: string): PackageInfo | null;
|
|
22
|
+
/**
|
|
23
|
+
* Get all packages
|
|
24
|
+
*/
|
|
25
|
+
getAllPackages(): PackageInfo[];
|
|
26
|
+
/**
|
|
27
|
+
* Clear cache and rescan
|
|
28
|
+
*/
|
|
29
|
+
rescan(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Get package count
|
|
32
|
+
*/
|
|
33
|
+
getPackageCount(): number;
|
|
34
|
+
}
|
|
35
|
+
export declare function getPackageRegistry(): PackageRegistry;
|
|
36
|
+
//# sourceMappingURL=package-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-registry.d.ts","sourceRoot":"","sources":["../../src/utils/package-registry.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAgB;IAEjC;;OAEG;IACG,aAAa,CACjB,aAAa,EAAE,MAAM,EACrB,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,IAAI,CAAC;IAwChB;;OAEG;YACW,aAAa;IAmF3B;;OAEG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAIpD;;OAEG;IACH,cAAc,IAAI,WAAW,EAAE;IAI/B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAW7B;;OAEG;IACH,eAAe,IAAI,MAAM;CAG1B;AAKD,wBAAgB,kBAAkB,IAAI,eAAe,CAKpD"}
|