@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.
Files changed (163) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.github/workflows/ci.yml +59 -0
  3. package/.github/workflows/publish.yml +50 -0
  4. package/.github/workflows/release.yml +53 -0
  5. package/BUILD_ANALYSIS.md +89 -0
  6. package/BUILD_STRATEGY.md +75 -0
  7. package/CHANGELOG.md +53 -0
  8. package/DIRECTIVE.md +488 -0
  9. package/__tests__/css-extraction.test.ts +261 -0
  10. package/__tests__/css-injection-integration.test.ts +247 -0
  11. package/__tests__/css-middleware.test.ts +191 -0
  12. package/__tests__/import-rewriter-bug.test.ts +135 -0
  13. package/dist/builder.d.ts +36 -0
  14. package/dist/builder.d.ts.map +1 -0
  15. package/dist/builder.js +772 -0
  16. package/dist/cache/compilation-cache.d.ts +33 -0
  17. package/dist/cache/compilation-cache.d.ts.map +1 -0
  18. package/dist/cache/compilation-cache.js +130 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +85 -0
  22. package/dist/config-loader.d.ts +8 -0
  23. package/dist/config-loader.d.ts.map +1 -0
  24. package/dist/config-loader.js +40 -0
  25. package/dist/config.d.ts +29 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +7 -0
  28. package/dist/dev/pythonDevManager.d.ts +12 -0
  29. package/dist/dev/pythonDevManager.d.ts.map +1 -0
  30. package/dist/dev/pythonDevManager.js +85 -0
  31. package/dist/env.d.ts +19 -0
  32. package/dist/env.d.ts.map +1 -0
  33. package/dist/env.js +112 -0
  34. package/dist/handlers/base-handler.d.ts +21 -0
  35. package/dist/handlers/base-handler.d.ts.map +1 -0
  36. package/dist/handlers/base-handler.js +38 -0
  37. package/dist/handlers/js-handler.d.ts +10 -0
  38. package/dist/handlers/js-handler.d.ts.map +1 -0
  39. package/dist/handlers/js-handler.js +87 -0
  40. package/dist/handlers/mjs-handler.d.ts +8 -0
  41. package/dist/handlers/mjs-handler.d.ts.map +1 -0
  42. package/dist/handlers/mjs-handler.js +44 -0
  43. package/dist/handlers/node-module-handler.d.ts +16 -0
  44. package/dist/handlers/node-module-handler.d.ts.map +1 -0
  45. package/dist/handlers/node-module-handler.js +267 -0
  46. package/dist/handlers/ts-handler.d.ts +11 -0
  47. package/dist/handlers/ts-handler.d.ts.map +1 -0
  48. package/dist/handlers/ts-handler.js +120 -0
  49. package/dist/handlers/ui-handler.d.ts +12 -0
  50. package/dist/handlers/ui-handler.d.ts.map +1 -0
  51. package/dist/handlers/ui-handler.js +182 -0
  52. package/dist/handlers/uix-handler.d.ts +12 -0
  53. package/dist/handlers/uix-handler.d.ts.map +1 -0
  54. package/dist/handlers/uix-handler.js +135 -0
  55. package/dist/hmr.d.ts +20 -0
  56. package/dist/hmr.d.ts.map +1 -0
  57. package/dist/hmr.js +265 -0
  58. package/dist/import-rewriter.d.ts +3 -0
  59. package/dist/import-rewriter.d.ts.map +1 -0
  60. package/dist/import-rewriter.js +351 -0
  61. package/dist/index.d.ts +14 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +13 -0
  64. package/dist/middleware/hmr-routes.d.ts +12 -0
  65. package/dist/middleware/hmr-routes.d.ts.map +1 -0
  66. package/dist/middleware/hmr-routes.js +97 -0
  67. package/dist/middleware/middleware-setup.d.ts +23 -0
  68. package/dist/middleware/middleware-setup.d.ts.map +1 -0
  69. package/dist/middleware/middleware-setup.js +596 -0
  70. package/dist/middleware/static-files.d.ts +15 -0
  71. package/dist/middleware/static-files.d.ts.map +1 -0
  72. package/dist/middleware/static-files.js +585 -0
  73. package/dist/proxy/SwiteProxyError.d.ts +6 -0
  74. package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
  75. package/dist/proxy/SwiteProxyError.js +9 -0
  76. package/dist/proxy/proxyToPython.d.ts +28 -0
  77. package/dist/proxy/proxyToPython.d.ts.map +1 -0
  78. package/dist/proxy/proxyToPython.js +66 -0
  79. package/dist/resolver/bare-import-resolver.d.ts +9 -0
  80. package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
  81. package/dist/resolver/bare-import-resolver.js +363 -0
  82. package/dist/resolver/symlink-registry.d.ts +13 -0
  83. package/dist/resolver/symlink-registry.d.ts.map +1 -0
  84. package/dist/resolver/symlink-registry.js +98 -0
  85. package/dist/resolver/url-resolver.d.ts +11 -0
  86. package/dist/resolver/url-resolver.d.ts.map +1 -0
  87. package/dist/resolver/url-resolver.js +268 -0
  88. package/dist/resolver/workspace-package-resolver.d.ts +10 -0
  89. package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
  90. package/dist/resolver/workspace-package-resolver.js +185 -0
  91. package/dist/resolver.d.ts +17 -0
  92. package/dist/resolver.d.ts.map +1 -0
  93. package/dist/resolver.js +191 -0
  94. package/dist/router/file-router.d.ts +19 -0
  95. package/dist/router/file-router.d.ts.map +1 -0
  96. package/dist/router/file-router.js +114 -0
  97. package/dist/server.d.ts +22 -0
  98. package/dist/server.d.ts.map +1 -0
  99. package/dist/server.js +122 -0
  100. package/dist/utils/cdn-fallback.d.ts +14 -0
  101. package/dist/utils/cdn-fallback.d.ts.map +1 -0
  102. package/dist/utils/cdn-fallback.js +36 -0
  103. package/dist/utils/file-path-resolver.d.ts +9 -0
  104. package/dist/utils/file-path-resolver.d.ts.map +1 -0
  105. package/dist/utils/file-path-resolver.js +187 -0
  106. package/dist/utils/generate-import-map-cli.d.ts +3 -0
  107. package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
  108. package/dist/utils/generate-import-map-cli.js +32 -0
  109. package/dist/utils/generate-import-map.d.ts +21 -0
  110. package/dist/utils/generate-import-map.d.ts.map +1 -0
  111. package/dist/utils/generate-import-map.js +119 -0
  112. package/dist/utils/package-finder.d.ts +24 -0
  113. package/dist/utils/package-finder.d.ts.map +1 -0
  114. package/dist/utils/package-finder.js +161 -0
  115. package/dist/utils/package-registry.d.ts +36 -0
  116. package/dist/utils/package-registry.d.ts.map +1 -0
  117. package/dist/utils/package-registry.js +159 -0
  118. package/dist/utils/workspace.d.ts +6 -0
  119. package/dist/utils/workspace.d.ts.map +1 -0
  120. package/dist/utils/workspace.js +65 -0
  121. package/docs/IMPORT_REWRITING.md +164 -0
  122. package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
  123. package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
  124. package/package.json +49 -0
  125. package/src/adapters/proxy/SwiteProxyError.ts +12 -0
  126. package/src/adapters/proxy/proxyToPython.ts +88 -0
  127. package/src/build-engine/builder.ts +960 -0
  128. package/src/cli.ts +109 -0
  129. package/src/config/config-loader.ts +46 -0
  130. package/src/config/config.ts +34 -0
  131. package/src/config/env.ts +98 -0
  132. package/src/dev-engine/handlers/base-handler.ts +68 -0
  133. package/src/dev-engine/handlers/js-handler.ts +134 -0
  134. package/src/dev-engine/handlers/mjs-handler.ts +65 -0
  135. package/src/dev-engine/handlers/node-module-handler.ts +339 -0
  136. package/src/dev-engine/handlers/ts-handler.ts +143 -0
  137. package/src/dev-engine/handlers/ui-handler.ts +105 -0
  138. package/src/dev-engine/handlers/uix-handler.ts +90 -0
  139. package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
  140. package/src/dev-engine/hmr/hmr.ts +173 -0
  141. package/src/dev-engine/middleware/hmr-routes.ts +120 -0
  142. package/src/dev-engine/middleware/middleware-setup.ts +351 -0
  143. package/src/dev-engine/middleware/static-files.ts +728 -0
  144. package/src/dev-engine/pythonDevManager.ts +116 -0
  145. package/src/dev-engine/router/file-router.ts +164 -0
  146. package/src/dev-engine/server.ts +152 -0
  147. package/src/index.ts +26 -0
  148. package/src/internal/cache/compilation-cache.ts +182 -0
  149. package/src/internal/generate-import-map-cli.ts +40 -0
  150. package/src/internal/generate-import-map.ts +154 -0
  151. package/src/kernel/package-finder.ts +164 -0
  152. package/src/kernel/package-registry.ts +198 -0
  153. package/src/kernel/workspace.ts +62 -0
  154. package/src/resolution/bare-import-resolver.ts +400 -0
  155. package/src/resolution/cdn/cdn-fallback.ts +37 -0
  156. package/src/resolution/path/file-path-resolver.ts +190 -0
  157. package/src/resolution/path/path-fixup.ts +19 -0
  158. package/src/resolution/resolver.ts +198 -0
  159. package/src/resolution/rewriting/import-rewriter.ts +237 -0
  160. package/src/resolution/symlink-registry.ts +114 -0
  161. package/src/resolution/url-resolver.ts +231 -0
  162. package/src/resolution/workspace-package-resolver.ts +94 -0
  163. 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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=generate-import-map-cli.d.ts.map
@@ -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"}