@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,159 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Package Registry - Dynamic package discovery
|
|
3
|
+
* Scans workspace to find all packages and caches their locations
|
|
4
|
+
* No hardcoded paths - discovers packages by scanning package.json files
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
export class PackageRegistry {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.packages = new Map();
|
|
12
|
+
this.scanned = false;
|
|
13
|
+
this.scanRoots = [];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Scan workspace for all packages
|
|
17
|
+
*/
|
|
18
|
+
async scanWorkspace(workspaceRoot, additionalRoots = []) {
|
|
19
|
+
if (this.scanned) {
|
|
20
|
+
return; // Already scanned
|
|
21
|
+
}
|
|
22
|
+
// Validate workspace root exists
|
|
23
|
+
if (!workspaceRoot) {
|
|
24
|
+
console.warn(chalk.yellow("[PackageRegistry] No workspace root provided, skipping scan"));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const rootStat = await fs.stat(workspaceRoot);
|
|
29
|
+
if (!rootStat.isDirectory()) {
|
|
30
|
+
console.warn(chalk.yellow(`[PackageRegistry] Workspace root is not a directory: ${workspaceRoot}`));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.warn(chalk.yellow(`[PackageRegistry] Cannot access workspace root ${workspaceRoot}:`, error.message));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.scanRoots = [workspaceRoot, ...additionalRoots.filter(root => root && root !== workspaceRoot)];
|
|
39
|
+
console.log(chalk.blue(`[PackageRegistry] Scanning workspace for packages...`));
|
|
40
|
+
console.log(chalk.gray(`[PackageRegistry] Roots: ${this.scanRoots.join(", ")}`));
|
|
41
|
+
for (const root of this.scanRoots) {
|
|
42
|
+
if (root) {
|
|
43
|
+
await this.scanDirectory(root);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
this.scanned = true;
|
|
47
|
+
console.log(chalk.green(`[PackageRegistry] ✅ Found ${this.packages.size} packages`));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Recursively scan directory for package.json files
|
|
51
|
+
*/
|
|
52
|
+
async scanDirectory(dir, depth = 0) {
|
|
53
|
+
if (depth > 15)
|
|
54
|
+
return; // Prevent infinite recursion
|
|
55
|
+
// Validate directory exists and is accessible
|
|
56
|
+
try {
|
|
57
|
+
const dirStat = await fs.stat(dir);
|
|
58
|
+
if (!dirStat.isDirectory()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Directory doesn't exist or permission denied, skip silently
|
|
64
|
+
if (error.code === "ENOENT" || error.code === "EACCES") {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.warn(chalk.yellow(`[PackageRegistry] Cannot access ${dir}:`, error.message));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
// Skip common directories that shouldn't be scanned
|
|
75
|
+
if (entry.name === "node_modules" ||
|
|
76
|
+
entry.name === "dist" ||
|
|
77
|
+
entry.name === ".git" ||
|
|
78
|
+
entry.name === ".swite" ||
|
|
79
|
+
entry.name.startsWith(".")) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const packageJsonPath = path.join(dir, entry.name, "package.json");
|
|
83
|
+
try {
|
|
84
|
+
const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
|
|
85
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
86
|
+
if (packageJson.name) {
|
|
87
|
+
const packagePath = path.join(dir, entry.name);
|
|
88
|
+
const packageInfo = {
|
|
89
|
+
name: packageJson.name,
|
|
90
|
+
path: packagePath,
|
|
91
|
+
packageJson,
|
|
92
|
+
};
|
|
93
|
+
// Store package - if duplicate name, prefer the one found first (or closest to workspace root)
|
|
94
|
+
if (!this.packages.has(packageJson.name)) {
|
|
95
|
+
this.packages.set(packageJson.name, packageInfo);
|
|
96
|
+
console.log(chalk.gray(`[PackageRegistry] Found: ${packageJson.name} at ${packagePath}`));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Log duplicate but don't overwrite (first found wins)
|
|
100
|
+
console.log(chalk.yellow(`[PackageRegistry] Duplicate package ${packageJson.name} found at ${packagePath}, keeping first`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Not a package.json or invalid JSON, continue scanning
|
|
106
|
+
// Silently ignore - this is expected for non-package directories
|
|
107
|
+
}
|
|
108
|
+
// Recurse into subdirectories (but skip if we found a package.json here)
|
|
109
|
+
// This allows nested package layouts (e.g. packages/foo/modules/bar)
|
|
110
|
+
await this.scanDirectory(path.join(dir, entry.name), depth + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Directory read error, log but don't fail
|
|
116
|
+
if (error.code !== "ENOENT" && error.code !== "EACCES") {
|
|
117
|
+
console.warn(chalk.yellow(`[PackageRegistry] Error reading directory ${dir}:`, error.message));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Find package by name
|
|
123
|
+
*/
|
|
124
|
+
findPackage(packageName) {
|
|
125
|
+
return this.packages.get(packageName) || null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get all packages
|
|
129
|
+
*/
|
|
130
|
+
getAllPackages() {
|
|
131
|
+
return Array.from(this.packages.values());
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Clear cache and rescan
|
|
135
|
+
*/
|
|
136
|
+
async rescan() {
|
|
137
|
+
const roots = [...this.scanRoots];
|
|
138
|
+
this.packages.clear();
|
|
139
|
+
this.scanned = false;
|
|
140
|
+
this.scanRoots = [];
|
|
141
|
+
if (roots.length > 0) {
|
|
142
|
+
await this.scanWorkspace(roots[0], roots.slice(1));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get package count
|
|
147
|
+
*/
|
|
148
|
+
getPackageCount() {
|
|
149
|
+
return this.packages.size;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Singleton instance
|
|
153
|
+
let registryInstance = null;
|
|
154
|
+
export function getPackageRegistry() {
|
|
155
|
+
if (!registryInstance) {
|
|
156
|
+
registryInstance = new PackageRegistry();
|
|
157
|
+
}
|
|
158
|
+
return registryInstance;
|
|
159
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the workspace root by looking for pnpm-workspace.yaml or package.json with workspaces
|
|
3
|
+
* Updated: Now also checks for lib/ directory to ensure we find the correct SWS root
|
|
4
|
+
*/
|
|
5
|
+
export declare function findWorkspaceRoot(root: string): Promise<string | null>;
|
|
6
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/utils/workspace.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgD5E"}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 the workspace root by looking for pnpm-workspace.yaml or package.json with workspaces
|
|
10
|
+
* Updated: Now also checks for lib/ directory to ensure we find the correct SWS root
|
|
11
|
+
*/
|
|
12
|
+
export async function findWorkspaceRoot(root) {
|
|
13
|
+
let current = root;
|
|
14
|
+
for (let i = 0; i < 10; i++) { // Increased from 5 to 10 to go higher up
|
|
15
|
+
const workspaceFile = path.join(current, "pnpm-workspace.yaml");
|
|
16
|
+
const packageJson = path.join(current, "package.json");
|
|
17
|
+
const libDir = path.join(current, "lib");
|
|
18
|
+
try {
|
|
19
|
+
await fs.access(workspaceFile);
|
|
20
|
+
// Accept root if it has lib/ (SWS with lib/) or packages/ (SWS with packages/ at root)
|
|
21
|
+
const packagesDir = path.join(current, "packages");
|
|
22
|
+
try {
|
|
23
|
+
await fs.access(libDir);
|
|
24
|
+
console.log(`[workspace] Found workspace root with lib/: ${current}`);
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
try {
|
|
29
|
+
await fs.access(packagesDir);
|
|
30
|
+
console.log(`[workspace] Found workspace root with packages/: ${current}`);
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Workspace file exists but no lib/ or packages/, continue searching up
|
|
35
|
+
console.log(`[workspace] Found workspace file at ${current} but no lib/ or packages/, continuing search...`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
try {
|
|
41
|
+
const pkgJson = JSON.parse(await fs.readFile(packageJson, "utf-8"));
|
|
42
|
+
if (pkgJson?.workspaces) {
|
|
43
|
+
// Also check for lib/ when package.json has workspaces
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(libDir);
|
|
46
|
+
console.log(`[workspace] Found workspace root with lib/ (via package.json): ${current}`);
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Has workspaces but no lib/, continue searching
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Continue searching
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const parent = path.dirname(current);
|
|
59
|
+
if (parent === current)
|
|
60
|
+
break;
|
|
61
|
+
current = parent;
|
|
62
|
+
}
|
|
63
|
+
console.warn(`[workspace] No workspace root found starting from: ${root}`);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Import Rewriting in SWITE
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
SWITE automatically rewrites bare module specifiers (e.g., `@swissjs/core`) to valid URLs that the browser can resolve. This document explains how it works and common issues.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
### Static Imports
|
|
10
|
+
|
|
11
|
+
Static imports like `import { X } from '@swissjs/core'` are automatically rewritten to use the resolved path:
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// Before
|
|
15
|
+
import { SwissApp } from '@swissjs/core';
|
|
16
|
+
|
|
17
|
+
// After (rewritten)
|
|
18
|
+
import { SwissApp } from '/swiss-packages/core/src/index.ts';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Dynamic Imports
|
|
22
|
+
|
|
23
|
+
Dynamic imports with **string literals** are rewritten:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// ✅ This WILL be rewritten
|
|
27
|
+
const module = await import('@swissjs/router');
|
|
28
|
+
|
|
29
|
+
// ❌ This will NOT be rewritten (variable reference)
|
|
30
|
+
const module = await import(def.componentUrl);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Critical Rule: Variable References Are NOT Rewritten
|
|
34
|
+
|
|
35
|
+
**IMPORTANT**: SWITE only rewrites **string literal** module specifiers. Variable references in dynamic imports are left unchanged.
|
|
36
|
+
|
|
37
|
+
### Why?
|
|
38
|
+
|
|
39
|
+
Variable references like `import(def.componentUrl)` are runtime values that cannot be statically analyzed. Rewriting them would break the code:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// ❌ WRONG - This would break
|
|
43
|
+
const module = await import(https://esm.sh/def.componentUrl); // Syntax error!
|
|
44
|
+
|
|
45
|
+
// ✅ CORRECT - Variable references are left as-is
|
|
46
|
+
const module = await import(def.componentUrl);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### How SWITE Detects This
|
|
50
|
+
|
|
51
|
+
1. **es-module-lexer path**: Only processes imports with quoted string literals
|
|
52
|
+
2. **Regex fallback path**: Only matches patterns like `import('"specifier"')` with quotes
|
|
53
|
+
|
|
54
|
+
## Common Issues
|
|
55
|
+
|
|
56
|
+
### SyntaxError: Unexpected token ':'
|
|
57
|
+
|
|
58
|
+
**Symptom**: Browser error like `ShellRouter.js:41 Uncaught SyntaxError: Unexpected token ':'`
|
|
59
|
+
|
|
60
|
+
**Cause**: The import rewriter incorrectly rewrote a variable reference in a dynamic import.
|
|
61
|
+
|
|
62
|
+
**Solution**: Use an intermediate variable to ensure the import rewriter skips it:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// ✅ REQUIRED - Use intermediate variable
|
|
66
|
+
const url = def.componentUrl;
|
|
67
|
+
const module = await import(url);
|
|
68
|
+
|
|
69
|
+
// ❌ Wrong - Property access may be incorrectly processed
|
|
70
|
+
const module = await import(def.componentUrl);
|
|
71
|
+
|
|
72
|
+
// ❌ Wrong - Template literals are still processed
|
|
73
|
+
const module = await import(`${def.componentUrl}`);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Module Not Found After Rewriting
|
|
77
|
+
|
|
78
|
+
**Symptom**: Module resolves but file doesn't exist
|
|
79
|
+
|
|
80
|
+
**Cause**: The resolver returned a path that doesn't exist (e.g., pointing to `dist/` when only `src/` exists)
|
|
81
|
+
|
|
82
|
+
**Solution**: SWITE automatically prefers `src/` over `dist/` in development. If issues persist, check:
|
|
83
|
+
1. The package's `package.json` exports field
|
|
84
|
+
2. The file actually exists at the resolved path
|
|
85
|
+
3. Server logs for resolution details
|
|
86
|
+
|
|
87
|
+
## Debugging
|
|
88
|
+
|
|
89
|
+
Enable verbose logging to see what's being rewritten:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Check server logs for:
|
|
93
|
+
[SWITE] import-rewriter: Resolved <specifier> -> <resolved>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If you see a variable being resolved (e.g., `def.componentUrl`), that's a bug - report it.
|
|
97
|
+
|
|
98
|
+
## Implementation Details
|
|
99
|
+
|
|
100
|
+
### es-module-lexer Path (Primary)
|
|
101
|
+
|
|
102
|
+
1. Parses the code to find all imports
|
|
103
|
+
2. Extracts specifiers (with quotes)
|
|
104
|
+
3. Only processes specifiers that have quotes (string literals)
|
|
105
|
+
4. Skips variable references automatically
|
|
106
|
+
|
|
107
|
+
### Regex Fallback Path
|
|
108
|
+
|
|
109
|
+
Used when es-module-lexer fails to parse (e.g., complex template literals):
|
|
110
|
+
|
|
111
|
+
1. Matches only quoted string literals: `/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g`
|
|
112
|
+
2. Validates specifier looks like a module path
|
|
113
|
+
3. Skips property access patterns (e.g., `def.componentUrl`)
|
|
114
|
+
|
|
115
|
+
## Best Practices
|
|
116
|
+
|
|
117
|
+
1. **Use string literals for static module paths**:
|
|
118
|
+
```javascript
|
|
119
|
+
const module = await import('@swissjs/router');
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
2. **Use intermediate variables for runtime-determined paths**:
|
|
123
|
+
```javascript
|
|
124
|
+
// ✅ REQUIRED PATTERN - Use intermediate variable
|
|
125
|
+
const url = def.componentUrl;
|
|
126
|
+
const module = await import(url);
|
|
127
|
+
|
|
128
|
+
// ❌ WRONG - Property access may be incorrectly processed
|
|
129
|
+
const module = await import(def.componentUrl);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
3. **Why intermediate variables?**
|
|
133
|
+
- Import rewriter skips simple identifiers (like `url`, `x`, etc.)
|
|
134
|
+
- Property access patterns (like `def.componentUrl`) may be incorrectly processed
|
|
135
|
+
- This ensures the variable is treated as a variable, not a module specifier
|
|
136
|
+
|
|
137
|
+
## Required Pattern for Dynamic Imports with Variables
|
|
138
|
+
|
|
139
|
+
**ALWAYS** use this pattern when importing from a variable:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Extract to simple identifier first
|
|
143
|
+
const url = def.componentUrl;
|
|
144
|
+
const module = await import(url);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Why this works:**
|
|
148
|
+
- Import rewriter validates specifiers and skips simple identifiers
|
|
149
|
+
- Simple identifiers (single word, no dots, no @) are treated as variables
|
|
150
|
+
- Property access patterns may pass validation and be incorrectly processed
|
|
151
|
+
- Using an intermediate variable ensures the import rewriter skips it
|
|
152
|
+
|
|
153
|
+
**What NOT to do:**
|
|
154
|
+
```typescript
|
|
155
|
+
// ❌ Don't use property access directly
|
|
156
|
+
const module = await import(def.componentUrl);
|
|
157
|
+
|
|
158
|
+
// ❌ Don't use template literals
|
|
159
|
+
const module = await import(`${def.componentUrl}`);
|
|
160
|
+
|
|
161
|
+
// ❌ Don't use complex expressions
|
|
162
|
+
const module = await import(config.paths.component);
|
|
163
|
+
```
|
|
164
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Import Rewriting Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
**Browser Error:**
|
|
6
|
+
```
|
|
7
|
+
Failed to resolve module specifier "@swiss-enterprise/ai-agents".
|
|
8
|
+
Relative references must start with either "/", "./", or "../".
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Root Cause:**
|
|
12
|
+
The browser cannot resolve bare module specifiers (like `@swiss-enterprise/ai-agents`). SWITE must rewrite them to valid paths (like `/EnterpriseRepo/packages/ai-agents/src/index.ui`) before sending code to the browser.
|
|
13
|
+
|
|
14
|
+
## Why This Keeps Happening
|
|
15
|
+
|
|
16
|
+
### 1. **es-module-lexer Returns Unquoted Specifiers**
|
|
17
|
+
|
|
18
|
+
`es-module-lexer` sometimes returns import positions WITHOUT including the surrounding quotes. For example:
|
|
19
|
+
- Code: `import { X } from '@swiss-enterprise/ai-agents'`
|
|
20
|
+
- Lexer returns: `@swiss-enterprise/ai-agents` (no quotes)
|
|
21
|
+
- Our code must detect the quotes in the original code
|
|
22
|
+
|
|
23
|
+
### 2. **Quote Detection Logic Fails**
|
|
24
|
+
|
|
25
|
+
If quote detection fails, the import rewriter skips the import, leaving bare imports in the code sent to the browser.
|
|
26
|
+
|
|
27
|
+
### 3. **Browser Can't Resolve Bare Imports**
|
|
28
|
+
|
|
29
|
+
Browsers require:
|
|
30
|
+
- Absolute paths: `/path/to/file.js`
|
|
31
|
+
- Relative paths: `./file.js` or `../file.js`
|
|
32
|
+
- NOT bare specifiers: `@package/name`
|
|
33
|
+
|
|
34
|
+
## The Fix
|
|
35
|
+
|
|
36
|
+
### Detection Strategy
|
|
37
|
+
|
|
38
|
+
1. **Check if lexer included quotes:**
|
|
39
|
+
```typescript
|
|
40
|
+
const hasQuotes = (firstChar === '"' || firstChar === "'") && firstChar === lastChar;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. **If no quotes, check surrounding code:**
|
|
44
|
+
```typescript
|
|
45
|
+
const codeBefore = code.slice(start - 1, start);
|
|
46
|
+
const codeAfter = code.slice(end, end + 1);
|
|
47
|
+
const hasQuotesInCode = codeBefore === codeAfter && (codeBefore === '"' || codeBefore === "'");
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. **If still no quotes, search for quoted version:**
|
|
51
|
+
```typescript
|
|
52
|
+
const quotedPattern = new RegExp(`(['"])${specifier}\\1`);
|
|
53
|
+
const match = quotedPattern.exec(code);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
4. **If found, use those positions for replacement**
|
|
57
|
+
|
|
58
|
+
### Replacement Strategy
|
|
59
|
+
|
|
60
|
+
- Always preserve the original quote style (`'` or `"`)
|
|
61
|
+
- Replace the ENTIRE quoted string, not just the specifier
|
|
62
|
+
- Ensure the resolved path is also properly quoted
|
|
63
|
+
|
|
64
|
+
## What Breaks
|
|
65
|
+
|
|
66
|
+
### ❌ What Breaks:
|
|
67
|
+
1. **Skipping unquoted specifiers** - Leaves bare imports in browser code
|
|
68
|
+
2. **Wrong quote detection** - Replaces wrong parts of code
|
|
69
|
+
3. **Not finding quoted version** - Skips valid imports
|
|
70
|
+
4. **Variable references** - Accidentally rewriting `import(variable)` as module specifiers
|
|
71
|
+
|
|
72
|
+
### ✅ What Works:
|
|
73
|
+
1. **Proper quote detection** - Finds quotes even when lexer doesn't include them
|
|
74
|
+
2. **Regex fallback** - Searches code for quoted version if direct detection fails
|
|
75
|
+
3. **Variable validation** - Skips property access patterns (`def.componentUrl`)
|
|
76
|
+
4. **Package name validation** - Only processes valid package names (`@scope/name`)
|
|
77
|
+
|
|
78
|
+
## How to Avoid This
|
|
79
|
+
|
|
80
|
+
### For Developers:
|
|
81
|
+
|
|
82
|
+
1. **Always use quoted imports:**
|
|
83
|
+
```typescript
|
|
84
|
+
// ✅ Good
|
|
85
|
+
import { X } from '@swiss-enterprise/ai-agents';
|
|
86
|
+
|
|
87
|
+
// ❌ Bad (if compiler removes quotes)
|
|
88
|
+
import { X } from @swiss-enterprise/ai-agents;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
2. **Check server logs:**
|
|
92
|
+
```
|
|
93
|
+
[SWITE] import-rewriter: ⚠️ SKIPPING unquoted specifier
|
|
94
|
+
[.ui] ERROR: Bare imports still present after rewriting
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
3. **Verify rewritten code:**
|
|
98
|
+
```bash
|
|
99
|
+
curl http://localhost:3001/src/index.ui | grep "@swiss-enterprise"
|
|
100
|
+
# Should return nothing (all imports rewritten)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### For SWITE Maintainers:
|
|
104
|
+
|
|
105
|
+
1. **Test with real code:**
|
|
106
|
+
- Don't just test with simple strings
|
|
107
|
+
- Test with actual compiled `.ui` files
|
|
108
|
+
- Test with `es-module-lexer` output
|
|
109
|
+
|
|
110
|
+
2. **Handle edge cases:**
|
|
111
|
+
- Unquoted specifiers from lexer
|
|
112
|
+
- Mixed quote styles
|
|
113
|
+
- Dynamic imports with variables
|
|
114
|
+
|
|
115
|
+
3. **Add comprehensive logging:**
|
|
116
|
+
- Log when quotes are detected
|
|
117
|
+
- Log when quotes are NOT detected
|
|
118
|
+
- Log when regex fallback is used
|
|
119
|
+
|
|
120
|
+
## Current Status
|
|
121
|
+
|
|
122
|
+
✅ **Fixed:** Quote detection now uses regex fallback to find quoted versions in code
|
|
123
|
+
✅ **Fixed:** Properly adjusts positions when quotes are found
|
|
124
|
+
✅ **Fixed:** Preserves original quote style during replacement
|
|
125
|
+
|
|
126
|
+
## Testing
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Test import rewriter
|
|
130
|
+
cd SWS/SWISS/packages/swite
|
|
131
|
+
node test-import-rewriter.mjs
|
|
132
|
+
|
|
133
|
+
# Check server logs
|
|
134
|
+
tail -f /tmp/alpine-dev.log | grep "import-rewriter"
|
|
135
|
+
|
|
136
|
+
# Verify browser code
|
|
137
|
+
curl http://localhost:3001/src/index.ui | grep "@swiss-enterprise"
|
|
138
|
+
```
|
|
139
|
+
|