@swissjs/swite 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cli.js +0 -0
- package/dist/config/config.d.ts +11 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/dev-engine/handlers/base-handler.d.ts +3 -1
- package/dist/dev-engine/handlers/base-handler.d.ts.map +1 -1
- package/dist/dev-engine/handlers/base-handler.js +1 -1
- package/dist/dev-engine/handlers/node-module-handler.d.ts.map +1 -1
- package/dist/dev-engine/handlers/node-module-handler.js +30 -41
- package/dist/dev-engine/middleware/middleware-setup.d.ts +1 -0
- package/dist/dev-engine/middleware/middleware-setup.d.ts.map +1 -1
- package/dist/dev-engine/server.d.ts.map +1 -1
- package/dist/dev-engine/server.js +4 -0
- package/dist/kernel/package-finder.d.ts +7 -7
- package/dist/kernel/package-finder.d.ts.map +1 -1
- package/dist/kernel/package-finder.js +56 -40
- package/dist/resolution/bare-import-resolver.d.ts.map +1 -1
- package/dist/resolution/bare-import-resolver.js +13 -4
- package/dist/resolution/path/file-path-resolver.d.ts +2 -1
- package/dist/resolution/path/file-path-resolver.d.ts.map +1 -1
- package/dist/resolution/path/file-path-resolver.js +36 -9
- package/docs/architecture/build-pipeline.md +97 -0
- package/docs/architecture/dev-server.md +87 -0
- package/docs/architecture/hmr.md +78 -0
- package/docs/architecture/import-rewriting.md +101 -0
- package/docs/architecture/index.md +16 -0
- package/docs/architecture/python-integration.md +93 -0
- package/docs/architecture/resolution.md +92 -0
- package/docs/cli/build.md +78 -0
- package/docs/cli/dev.md +90 -0
- package/docs/cli/index.md +15 -0
- package/docs/cli/start.md +45 -0
- package/docs/development/contributing.md +74 -0
- package/docs/development/index.md +12 -0
- package/docs/development/internals.md +101 -0
- package/docs/guide/configuration.md +89 -0
- package/docs/guide/index.md +13 -0
- package/docs/guide/project-structure.md +75 -0
- package/docs/guide/quickstart.md +113 -0
- package/docs/index.md +16 -0
- package/package.json +15 -24
- package/src/config/config.ts +11 -0
- package/src/dev-engine/handlers/base-handler.ts +4 -2
- package/src/dev-engine/handlers/node-module-handler.ts +51 -78
- package/src/dev-engine/middleware/middleware-setup.ts +1 -0
- package/src/dev-engine/server.ts +38 -33
- package/src/kernel/package-finder.ts +59 -43
- package/src/resolution/bare-import-resolver.ts +14 -4
- package/src/resolution/path/file-path-resolver.ts +44 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Generalize resolution system and add internalScopes support
|
|
8
|
+
|
|
9
|
+
- Add `findSiblingRepository(startPath, repoName)` — replaces hardcoded swiss-lib discovery with a generic sibling repo finder. `findSwissLibMonorepo` preserved as a backward-compat wrapper.
|
|
10
|
+
- Add `findPackage` with local-first dev precedence — in development, resolves `@scoped/*` packages from local sibling source trees before falling back to `node_modules`.
|
|
11
|
+
- `internalScopes` from `swiss.config.ts` now flows through to all handlers at startup — prevents internal-scoped packages from being routed to jsDelivr CDN.
|
|
12
|
+
- `NodeModuleHandler` rewritten to use `findPackage` — replaces verbose walk-up/swiss-lib fallback chain with unified local-first resolver. Adds dist→src redirect for local sibling packages.
|
|
13
|
+
- CDN blacklist: packages matching `internalScopes` are blocked from jsDelivr fallback with a clear error log.
|
|
14
|
+
- Fix `cli.ts` builder import path (`./build-engine/builder.js`).
|
|
15
|
+
- Fix `url-resolver.ts` dynamic import path for `file-path-resolver`.
|
|
16
|
+
- Update peer deps: `@swissjs/core` → `0.1.8`, `@swissjs/compiler` → `0.1.5`.
|
|
17
|
+
|
|
3
18
|
## 0.2.31
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/config/config.d.ts
CHANGED
|
@@ -20,6 +20,17 @@ export interface ServerConfig {
|
|
|
20
20
|
export interface SwiteUserConfig {
|
|
21
21
|
server?: ServerConfig;
|
|
22
22
|
services?: ServicesConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Package scopes that should be treated as "internal" or "private".
|
|
25
|
+
* These scopes prioritize local/monorepo resolution and are forbidden from CDN redirects.
|
|
26
|
+
* e.g. ["@kibologic", "@alpine"]
|
|
27
|
+
*/
|
|
28
|
+
internalScopes?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Manual override for sibling repository lookup.
|
|
31
|
+
* Swite will search these directories for local package source code.
|
|
32
|
+
*/
|
|
33
|
+
siblingRepositories?: string[];
|
|
23
34
|
}
|
|
24
35
|
/**
|
|
25
36
|
* Define swite configuration with full TypeScript validation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAErE"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { Response } from "express";
|
|
2
2
|
import { ModuleResolver } from "../../resolution/resolver.js";
|
|
3
|
+
import type { SwiteUserConfig } from "../../config/config.js";
|
|
3
4
|
export interface HandlerContext {
|
|
4
5
|
resolver: ModuleResolver;
|
|
5
6
|
root: string;
|
|
6
7
|
workspaceRoot: string | null;
|
|
7
|
-
env
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
userConfig?: SwiteUserConfig;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Set cache-busting headers for development
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-handler.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/handlers/base-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"base-handler.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/handlers/base-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAMjD;AAED;;GAEG;AACH,qBAAa,WAAW;IACV,SAAS,CAAC,OAAO,EAAE,cAAc;gBAAvB,OAAO,EAAE,cAAc;cAE7B,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;cAI7C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;cAS9C,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAmBrE"}
|
|
@@ -24,7 +24,7 @@ export class BaseHandler {
|
|
|
24
24
|
this.context = context;
|
|
25
25
|
}
|
|
26
26
|
async resolveFilePath(url) {
|
|
27
|
-
return resolveFilePath(url, this.context.root, this.context.workspaceRoot);
|
|
27
|
+
return resolveFilePath(url, this.context.root, this.context.workspaceRoot, this.context.userConfig);
|
|
28
28
|
}
|
|
29
29
|
async fileExists(filePath) {
|
|
30
30
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-module-handler.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/handlers/node-module-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKxC,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"node-module-handler.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/handlers/node-module-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKxC,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQrE,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAY;gBAEjB,OAAO,EAAE,cAAc;IAO7B,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgPvD;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;CAmCjC"}
|
|
@@ -12,6 +12,7 @@ import { UIHandler } from "./ui-handler.js";
|
|
|
12
12
|
import { UIXHandler } from "./uix-handler.js";
|
|
13
13
|
import { TSHandler } from "./ts-handler.js";
|
|
14
14
|
import { findWorkspaceRoot } from "../../kernel/workspace.js";
|
|
15
|
+
import { findPackage } from "../../kernel/package-finder.js";
|
|
15
16
|
import { shouldUseCdnFallback } from "../../resolution/cdn/cdn-fallback.js";
|
|
16
17
|
export class NodeModuleHandler extends BaseHandler {
|
|
17
18
|
constructor(context) {
|
|
@@ -64,52 +65,33 @@ export class NodeModuleHandler extends BaseHandler {
|
|
|
64
65
|
current = parent;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
68
|
+
// CONSOLIDATED DISCOVERY: Use the Generalized Package Finder
|
|
69
|
+
// This follows our "Local-First" priority: Siblings > Local node_modules > Workspace node_modules
|
|
67
70
|
if (!filePath) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const { findSwissLibMonorepo } = await import("../../kernel/package-finder.js");
|
|
85
|
-
const swissLib = await findSwissLibMonorepo(this.context.root);
|
|
86
|
-
if (swissLib) {
|
|
87
|
-
const swissNodeModulesPath = path.join(swissLib, urlPath);
|
|
88
|
-
console.log(chalk.blue(`[node_modules] Trying swiss-lib path: ${swissNodeModulesPath}`));
|
|
89
|
-
try {
|
|
90
|
-
// Try to resolve symlinks first (realpath works even if path is a symlink)
|
|
91
|
-
const resolvedPath = await fs.realpath(swissNodeModulesPath);
|
|
92
|
-
console.log(chalk.blue(`[node_modules] Resolved to: ${resolvedPath}`));
|
|
93
|
-
// Verify the resolved path exists
|
|
94
|
-
await fs.access(resolvedPath);
|
|
95
|
-
filePath = resolvedPath;
|
|
96
|
-
console.log(chalk.green(`[node_modules] ✓ Found in swiss-lib monorepo: ${urlPath}`));
|
|
97
|
-
}
|
|
98
|
-
catch (err3) {
|
|
99
|
-
console.log(chalk.yellow(`[node_modules] swiss-lib path failed: ${err3 instanceof Error ? err3.message : String(err3)}`));
|
|
100
|
-
// File not found in any location, will trigger case-insensitive search below
|
|
101
|
-
filePath = path.join(this.context.root, urlPath);
|
|
71
|
+
const urlParts = urlPath.split("/");
|
|
72
|
+
const packageName = urlParts[1].startsWith("@") ? `${urlParts[1]}/${urlParts[2]}` : urlParts[1];
|
|
73
|
+
const remainingPath = urlParts[1].startsWith("@") ? urlParts.slice(3).join("/") : urlParts.slice(2).join("/");
|
|
74
|
+
const location = await findPackage(packageName, this.context.root, workspaceRoot);
|
|
75
|
+
if (location) {
|
|
76
|
+
filePath = path.join(location.path, remainingPath);
|
|
77
|
+
console.log(chalk.green(`[node_modules] ✓ Found ${packageName} via ${location.type}: ${filePath}`));
|
|
78
|
+
// Re-use dist -> src fallback for local siblings
|
|
79
|
+
if (location.type !== 'node_modules' && filePath.includes("/dist/")) {
|
|
80
|
+
const srcPath = filePath.replace("/dist/", "/src/").replace(/\.[mc]?js$/, ".ts");
|
|
81
|
+
try {
|
|
82
|
+
await fs.access(srcPath);
|
|
83
|
+
console.log(chalk.yellow(`[node_modules] Intercept: Serving local source instead of dist: ${srcPath}`));
|
|
84
|
+
filePath = srcPath;
|
|
85
|
+
if (srcPath.endsWith(".ts")) {
|
|
86
|
+
return await this.tsHandler.handle(url.replace(/\.[mc]?js$/, ".ts"), res);
|
|
102
87
|
}
|
|
103
88
|
}
|
|
104
|
-
|
|
105
|
-
// File not found in any location, will trigger case-insensitive search below
|
|
106
|
-
filePath = path.join(this.context.root, urlPath);
|
|
107
|
-
}
|
|
89
|
+
catch { /* Fallback to original filePath */ }
|
|
108
90
|
}
|
|
109
91
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
92
|
+
}
|
|
93
|
+
if (!filePath) {
|
|
94
|
+
filePath = path.join(this.context.root, urlPath);
|
|
113
95
|
}
|
|
114
96
|
console.log(chalk.gray(`[node_modules] Resolving: ${url} -> ${filePath}`));
|
|
115
97
|
// File path is already resolved from above, no need to resolve again
|
|
@@ -259,6 +241,13 @@ export class NodeModuleHandler extends BaseHandler {
|
|
|
259
241
|
}
|
|
260
242
|
if (!pkgName || pkgName === "." || pkgName === "..")
|
|
261
243
|
return null;
|
|
244
|
+
// Never redirect internal/private scoped packages to public CDNs
|
|
245
|
+
const internalScopes = this.context.userConfig?.internalScopes || [];
|
|
246
|
+
const isInternal = internalScopes.some(scope => pkgName === scope || pkgName.startsWith(scope + "/"));
|
|
247
|
+
if (isInternal) {
|
|
248
|
+
console.log(chalk.red(`[node_modules] CDN Blocked: Internal scope package ${pkgName} cannot be served from jsDelivr.`));
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
262
251
|
if (!shouldUseCdnFallback(pkgName))
|
|
263
252
|
return null;
|
|
264
253
|
// jsDelivr +esm serves ESM build; works for reflect-metadata and most npm packages
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/middleware/middleware-setup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAmC,MAAM,SAAS,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAIpE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAa9D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../../src/dev-engine/middleware/middleware-setup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAmC,MAAM,SAAS,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAIpE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAa9D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,SAAS,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,wBAAwB,EAAE,eAAe,CAAC;CAC/D;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC;CACpE;AAuBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,gBAAgB,CAAC,CA2Q3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev-engine/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev-engine/server.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IAKb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,YAAY,CACb;IACP,OAAO,CAAC,MAAM,CAAyB;gBAE3B,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM;YAe/B,iBAAiB;IAuBzB,KAAK;CA0EZ"}
|
|
@@ -14,6 +14,7 @@ import chalk from "chalk";
|
|
|
14
14
|
import { setupMiddleware } from "./middleware/middleware-setup.js";
|
|
15
15
|
import { buildSymlinkRegistry } from "../resolution/symlink-registry.js";
|
|
16
16
|
import { findSwissLibMonorepo } from "../kernel/package-finder.js";
|
|
17
|
+
import { loadUserConfig } from "../config/config-loader.js";
|
|
17
18
|
export class SwiteServer {
|
|
18
19
|
constructor(config = {}) {
|
|
19
20
|
this.app = express();
|
|
@@ -86,6 +87,8 @@ export class SwiteServer {
|
|
|
86
87
|
console.warn(`[SWITE] Symlink registry build failed: ${err.message}`);
|
|
87
88
|
}
|
|
88
89
|
console.timeEnd("Symlink Registry");
|
|
90
|
+
// Load user config (swiss.config.ts) so internalScopes etc. flow into handlers
|
|
91
|
+
const userConfig = await loadUserConfig(this.config.root);
|
|
89
92
|
// Setup middleware
|
|
90
93
|
console.time("Middleware Setup");
|
|
91
94
|
const workspaceRoot = await this.findWorkspaceRoot(this.config.root);
|
|
@@ -95,6 +98,7 @@ export class SwiteServer {
|
|
|
95
98
|
publicDir: this.config.publicDir,
|
|
96
99
|
resolver: this.resolver,
|
|
97
100
|
hmr: this.hmr,
|
|
101
|
+
userConfig,
|
|
98
102
|
});
|
|
99
103
|
this.routes = middlewareResult.routes;
|
|
100
104
|
this.routeScanner = middlewareResult.routeScanner;
|
|
@@ -7,16 +7,16 @@ export interface PackageLocation {
|
|
|
7
7
|
type: 'swiss-lib' | 'workspace' | 'node_modules';
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Find
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
* Find any sibling monorepo by searching for its package.json
|
|
11
|
+
*/
|
|
12
|
+
export declare function findSiblingRepository(startPath: string, repoName: string): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Backward compatibility wrapper for finding swiss-lib
|
|
13
15
|
*/
|
|
14
16
|
export declare function findSwissLibMonorepo(startPath: string): Promise<string | null>;
|
|
15
17
|
/**
|
|
16
|
-
* Find a specific package by name,
|
|
17
|
-
*
|
|
18
|
-
* 2. swiss-lib/packages (if found)
|
|
19
|
-
* 3. workspace packages (lib/, packages/, modules/)
|
|
18
|
+
* Find a specific package by name, with priority given based on environment.
|
|
19
|
+
* In development, we prioritize local sibling source code.
|
|
20
20
|
*/
|
|
21
21
|
export declare function findPackage(packageName: string, startPath: string, workspaceRoot?: string | null): Promise<PackageLocation | null>;
|
|
22
22
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-finder.d.ts","sourceRoot":"","sources":["../../src/kernel/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
|
|
1
|
+
{"version":3,"file":"package-finder.d.ts","sourceRoot":"","sources":["../../src/kernel/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,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyBvG;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEpF;AAED;;;GAGG;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,CAuEjC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B7E"}
|
|
@@ -6,48 +6,47 @@
|
|
|
6
6
|
import { promises as fs } from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
/**
|
|
9
|
-
* Find
|
|
10
|
-
* ancestor level for any workspace root (pnpm-workspace.yaml) that also has a
|
|
11
|
-
* packages/ directory. Works for any framework directory name.
|
|
9
|
+
* Find any sibling monorepo by searching for its package.json
|
|
12
10
|
*/
|
|
13
|
-
export async function
|
|
14
|
-
let current =
|
|
11
|
+
export async function findSiblingRepository(startPath, repoName) {
|
|
12
|
+
let current = startPath;
|
|
15
13
|
for (let i = 0; i < 20; i++) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const siblingPath = path.join(current, repoName);
|
|
15
|
+
const pkgJson = path.join(siblingPath, "package.json");
|
|
16
|
+
if (await fileExists(pkgJson)) {
|
|
17
|
+
return siblingPath;
|
|
18
|
+
}
|
|
20
19
|
try {
|
|
21
|
-
const entries = await fs.readdir(
|
|
20
|
+
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
22
21
|
for (const entry of entries) {
|
|
23
|
-
if (entry.name ===
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (path.resolve(sibling) === path.resolve(current))
|
|
29
|
-
continue; // skip self
|
|
30
|
-
if (await fileExists(path.join(sibling, "pnpm-workspace.yaml")) &&
|
|
31
|
-
await fileExists(path.join(sibling, "packages"))) {
|
|
32
|
-
return sibling;
|
|
22
|
+
if (entry.name === repoName && (entry.isDirectory() || entry.isSymbolicLink())) {
|
|
23
|
+
const subDir = path.join(current, entry.name);
|
|
24
|
+
if (await fileExists(path.join(subDir, "package.json"))) {
|
|
25
|
+
return subDir;
|
|
26
|
+
}
|
|
33
27
|
}
|
|
34
28
|
}
|
|
35
29
|
}
|
|
36
|
-
catch {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
catch { /* Continue */ }
|
|
31
|
+
current = path.dirname(current);
|
|
32
|
+
if (current === path.dirname(current))
|
|
33
|
+
break;
|
|
40
34
|
}
|
|
41
35
|
return null;
|
|
42
36
|
}
|
|
43
37
|
/**
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
* Backward compatibility wrapper for finding swiss-lib
|
|
39
|
+
*/
|
|
40
|
+
export async function findSwissLibMonorepo(startPath) {
|
|
41
|
+
return findSiblingRepository(startPath, 'swiss-lib');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Find a specific package by name, with priority given based on environment.
|
|
45
|
+
* In development, we prioritize local sibling source code.
|
|
48
46
|
*/
|
|
49
47
|
export async function findPackage(packageName, startPath, workspaceRoot) {
|
|
50
|
-
|
|
48
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
49
|
+
// 1. Check local node_modules (Standard resolution) - HIGHEST PRIORITY in Remote-First
|
|
51
50
|
const localNodeModules = path.join(startPath, "node_modules", packageName);
|
|
52
51
|
if (await fileExists(path.join(localNodeModules, "package.json"))) {
|
|
53
52
|
return { path: localNodeModules, type: 'node_modules' };
|
|
@@ -70,23 +69,40 @@ export async function findPackage(packageName, startPath, workspaceRoot) {
|
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
// 4.
|
|
72
|
+
// 4. In dev: broader sibling scan across parent directories
|
|
73
|
+
if (isDev && packageName.includes("/")) {
|
|
74
|
+
const parts = packageName.split("/");
|
|
75
|
+
const unscoped = parts[parts.length - 1];
|
|
76
|
+
const parentDirs = [
|
|
77
|
+
path.join(startPath, ".."),
|
|
78
|
+
path.join(startPath, "../.."),
|
|
79
|
+
path.join(startPath, "../../.."),
|
|
80
|
+
];
|
|
81
|
+
for (const parent of parentDirs) {
|
|
82
|
+
try {
|
|
83
|
+
const potentialRepos = await fs.readdir(parent);
|
|
84
|
+
for (const repo of potentialRepos) {
|
|
85
|
+
const siblingPath = path.join(parent, repo);
|
|
86
|
+
const packagePath = path.join(siblingPath, "packages", unscoped);
|
|
87
|
+
if (await fileExists(path.join(packagePath, "package.json"))) {
|
|
88
|
+
console.log(`[package-finder] Dev Intercept: Serving ${packageName} from local source: ${packagePath}`);
|
|
89
|
+
return { path: packagePath, type: 'swiss-lib' };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch { /* Continue */ }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// 5. Fallback search in internal workspace packages (lib/, packages/, modules/)
|
|
74
97
|
if (workspaceRoot) {
|
|
75
98
|
const packageDirs = ["lib", "packages", "modules", "libraries", "apps"];
|
|
76
99
|
for (const dir of packageDirs) {
|
|
77
100
|
const searchDir = path.join(workspaceRoot, dir);
|
|
78
101
|
if (!(await fileExists(searchDir)))
|
|
79
102
|
continue;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const packagePath = path.join(searchDir, unscoped);
|
|
84
|
-
if (await fileExists(path.join(packagePath, "package.json"))) {
|
|
85
|
-
return { path: packagePath, type: 'workspace' };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Try full package name
|
|
89
|
-
const packagePath = path.join(searchDir, packageName);
|
|
103
|
+
const parts = packageName.split("/");
|
|
104
|
+
const unscoped = parts.length > 1 ? parts[1] : parts[0];
|
|
105
|
+
const packagePath = path.join(searchDir, unscoped);
|
|
90
106
|
if (await fileExists(path.join(packagePath, "package.json"))) {
|
|
91
107
|
return { path: packagePath, type: 'workspace' };
|
|
92
108
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bare-import-resolver.d.ts","sourceRoot":"","sources":["../../src/resolution/bare-import-resolver.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAmC,MAAM,mBAAmB,CAAC;AAI7F,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,uBAAuB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACtE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"bare-import-resolver.d.ts","sourceRoot":"","sources":["../../src/resolution/bare-import-resolver.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAmC,MAAM,mBAAmB,CAAC;AAI7F,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,uBAAuB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACtE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CA4NjB"}
|
|
@@ -32,7 +32,7 @@ export async function resolveBareImport(specifier, context) {
|
|
|
32
32
|
if (workspaceRoot) {
|
|
33
33
|
nodeModulesLocations.push(path.join(workspaceRoot, "node_modules"));
|
|
34
34
|
}
|
|
35
|
-
// Add
|
|
35
|
+
// Add monorepo node_modules if present
|
|
36
36
|
const swissLib = await findSwissLibMonorepo(context.root);
|
|
37
37
|
if (swissLib) {
|
|
38
38
|
const swissNodeModules = path.join(swissLib, "node_modules");
|
|
@@ -159,9 +159,18 @@ export async function resolveBareImport(specifier, context) {
|
|
|
159
159
|
entryPoint = pkgJson.module || pkgJson.main || "index.js";
|
|
160
160
|
}
|
|
161
161
|
const fullPath = path.join(pkgDir, entryPoint);
|
|
162
|
-
// Try the
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
// Try a few Swiss-specific variations of the entry point before CDN fallback
|
|
163
|
+
const swissVariations = [
|
|
164
|
+
fullPath,
|
|
165
|
+
fullPath.replace(/\.(js|mjs|ts)$/, '.ui'),
|
|
166
|
+
fullPath.replace(/\.(js|mjs|ts)$/, '.uix'),
|
|
167
|
+
path.join(pkgDir, 'src/index.ui'),
|
|
168
|
+
path.join(pkgDir, 'src/index.uix'),
|
|
169
|
+
];
|
|
170
|
+
for (const v of swissVariations) {
|
|
171
|
+
if (await context.fileExists(v)) {
|
|
172
|
+
return await toUrl(v, context);
|
|
173
|
+
}
|
|
165
174
|
}
|
|
166
175
|
// Try with extensions
|
|
167
176
|
for (const ext of [".js", ".mjs", ".ts", ".ui", ".uix"]) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export interface PathResolverContext {
|
|
2
2
|
root: string;
|
|
3
3
|
workspaceRoot: string | null;
|
|
4
|
+
userConfig?: any;
|
|
4
5
|
}
|
|
5
6
|
/**
|
|
6
7
|
* Resolve file path from URL, handling SWISS packages, workspace packages, and app files
|
|
7
8
|
*/
|
|
8
|
-
export declare function resolveFilePath(url: string, root: string, workspaceRoot?: string | null): Promise<string>;
|
|
9
|
+
export declare function resolveFilePath(url: string, root: string, workspaceRoot?: string | null, userConfig?: any): Promise<string>;
|
|
9
10
|
//# sourceMappingURL=file-path-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-path-resolver.d.ts","sourceRoot":"","sources":["../../../src/resolution/path/file-path-resolver.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"file-path-resolver.d.ts","sourceRoot":"","sources":["../../../src/resolution/path/file-path-resolver.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,aAAa,GAAE,MAAM,GAAG,IAAW,EACnC,UAAU,CAAC,EAAE,GAAG,GACf,OAAO,CAAC,MAAM,CAAC,CAsMjB"}
|
|
@@ -6,16 +6,48 @@
|
|
|
6
6
|
import { promises as fs } from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { findWorkspaceRoot } from "../../kernel/workspace.js";
|
|
9
|
-
import { findSwissLibMonorepo } from "../../kernel/package-finder.js";
|
|
9
|
+
import { findSwissLibMonorepo, findPackage } from "../../kernel/package-finder.js";
|
|
10
10
|
/**
|
|
11
11
|
* Resolve file path from URL, handling SWISS packages, workspace packages, and app files
|
|
12
12
|
*/
|
|
13
|
-
export async function resolveFilePath(url, root, workspaceRoot = null) {
|
|
13
|
+
export async function resolveFilePath(url, root, workspaceRoot = null, userConfig) {
|
|
14
|
+
// Consolidate workspace root discovery for consistency across resolution blocks
|
|
15
|
+
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
14
16
|
// /node_modules/ URLs: walk up from app root until we find the package.
|
|
15
17
|
// pnpm may place deps at the app root, one level up (workspace pkg), or at
|
|
16
18
|
// the monorepo root depending on hoisting config and pnpm version.
|
|
17
19
|
if (url.startsWith("/node_modules/")) {
|
|
18
20
|
const urlPath = url.startsWith("/") ? url.slice(1) : url;
|
|
21
|
+
const parts = urlPath.split("/");
|
|
22
|
+
// Handle @scoped/package or standard-package
|
|
23
|
+
const packageName = parts[1].startsWith("@") ? `${parts[1]}/${parts[2]}` : parts[1];
|
|
24
|
+
// NEW: PNPM-aware Interceptor. Check if this request is for an "internal" scope package.
|
|
25
|
+
// In development, we always prioritize local siblings if they exist.
|
|
26
|
+
const internalScopes = userConfig?.internalScopes || [];
|
|
27
|
+
const match = internalScopes.length > 0
|
|
28
|
+
? url.match(new RegExp(`(${internalScopes.join("|")})\/([^/]+)`))
|
|
29
|
+
: null;
|
|
30
|
+
if (process.env.NODE_ENV !== 'production' && match) {
|
|
31
|
+
const packageName = match[0];
|
|
32
|
+
const remainingPath = url.split(match[0])[1];
|
|
33
|
+
const localLoc = await findPackage(packageName, root, wsRoot);
|
|
34
|
+
if (localLoc && localLoc.type !== 'node_modules') {
|
|
35
|
+
// Found local source! Redirect the base path
|
|
36
|
+
const fullPath = path.join(localLoc.path, remainingPath);
|
|
37
|
+
// Re-use workspace fallback logic for dist -> src transition
|
|
38
|
+
if (fullPath.includes("/dist/")) {
|
|
39
|
+
const srcPath = fullPath.replace("/dist/", "/src/").replace(/\.[mc]?js$/, ".ts");
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(srcPath);
|
|
42
|
+
console.log(`[file-path-resolver] Intercept: ${packageName} redirecting to local src: ${srcPath}`);
|
|
43
|
+
return srcPath;
|
|
44
|
+
}
|
|
45
|
+
catch { /* Fallback to dist if src not found */ }
|
|
46
|
+
}
|
|
47
|
+
console.log(`[file-path-resolver] Intercept: ${packageName} redirecting to local source: ${fullPath}`);
|
|
48
|
+
return fullPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
19
51
|
// Walk up the directory tree from root, trying node_modules at each level
|
|
20
52
|
let current = path.resolve(root);
|
|
21
53
|
const visited = new Set();
|
|
@@ -38,7 +70,6 @@ export async function resolveFilePath(url, root, workspaceRoot = null) {
|
|
|
38
70
|
current = parent;
|
|
39
71
|
}
|
|
40
72
|
// Explicit workspace root (covers hoisted-to-root installs)
|
|
41
|
-
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
42
73
|
if (wsRoot) {
|
|
43
74
|
const wsPath = path.join(wsRoot, urlPath);
|
|
44
75
|
if (!visited.has(wsPath)) {
|
|
@@ -78,13 +109,10 @@ export async function resolveFilePath(url, root, workspaceRoot = null) {
|
|
|
78
109
|
url.startsWith("/libraries/") ||
|
|
79
110
|
url.startsWith("/packages/") ||
|
|
80
111
|
url.startsWith("/modules/")) {
|
|
81
|
-
|
|
82
|
-
if (!wsRoot) {
|
|
83
|
-
wsRoot = await findWorkspaceRoot(root);
|
|
84
|
-
console.log(`[file-path-resolver] Detected workspace root: ${wsRoot} (from app root: ${root})`);
|
|
85
|
-
}
|
|
112
|
+
// Already detected wsRoot at function start
|
|
86
113
|
// Normalize URL: path.join with leading slash is wrong on Windows (treats as drive root)
|
|
87
114
|
const urlPath = url.startsWith("/") ? url.slice(1) : url;
|
|
115
|
+
// ...
|
|
88
116
|
// CRITICAL: For /lib/ paths, we MUST find the SWS root (which has lib/ directory)
|
|
89
117
|
// Start from app root and walk up until we find a directory with both pnpm-workspace.yaml AND lib/
|
|
90
118
|
if (url.startsWith("/lib/")) {
|
|
@@ -141,7 +169,6 @@ export async function resolveFilePath(url, root, workspaceRoot = null) {
|
|
|
141
169
|
}
|
|
142
170
|
}
|
|
143
171
|
// For app files, check if URL already includes the app path
|
|
144
|
-
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
145
172
|
if (wsRoot) {
|
|
146
173
|
const appRelativeToWorkspace = path
|
|
147
174
|
.relative(wsRoot, root)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Build Pipeline
|
|
8
|
+
|
|
9
|
+
`SwiteBuilder` (`src/build-engine/builder.ts`) handles production builds. It is invoked by `swite build` with a fixed entry point of `src/index.ui` and output directory of `dist/`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The build runs three sequential phases inside a try/finally block. The temporary directory `.swite-build/` is always removed when the build finishes, whether it succeeded or failed.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
swite build
|
|
19
|
+
└── SwiteBuilder.build()
|
|
20
|
+
├── 1. cleanOutputDir() — rm -rf dist/, mkdir dist/
|
|
21
|
+
├── 2. compileSwissFiles() — @swissjs/compiler → .tsx in .swite-build/
|
|
22
|
+
├── 3. bundle() — esbuild bundles from .swite-build/
|
|
23
|
+
└── 4. copyPublicAssets() — cp -r public/ dist/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Phase 1: Compile Swiss files
|
|
29
|
+
|
|
30
|
+
`compileSwissFiles` traverses `src/` and every workspace dependency's `src/` directory.
|
|
31
|
+
|
|
32
|
+
For each `.ui` or `.uix` file:
|
|
33
|
+
|
|
34
|
+
1. `UiCompiler.compileAsync(source, filePath)` — transforms SwissJS component syntax to TypeScript/JSX.
|
|
35
|
+
2. Relative `.ui`/`.uix` imports in the compiled output are rewritten to `.tsx` (esbuild needs `.tsx` for JSX, not `.ui`).
|
|
36
|
+
3. If the compiled output ends with `export { Foo }`, a `export default Foo` line is appended so default imports resolve at bundle time.
|
|
37
|
+
4. Written to `.swite-build/<relative-path>.tsx`.
|
|
38
|
+
|
|
39
|
+
For each `.ts` file, `.ui`/`.uix` imports in `from` clauses are rewritten to `.tsx`, then the file is copied as-is.
|
|
40
|
+
|
|
41
|
+
CSS files are copied verbatim so that any CSS import stubs in the bundle phase can resolve.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Phase 2: Bundle with esbuild
|
|
46
|
+
|
|
47
|
+
esbuild is invoked with:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
{
|
|
51
|
+
bundle: true,
|
|
52
|
+
format: 'esm',
|
|
53
|
+
target: 'es2020',
|
|
54
|
+
minify: true,
|
|
55
|
+
sourcemap: false,
|
|
56
|
+
splitting: true, // ESM code splitting
|
|
57
|
+
metafile: true,
|
|
58
|
+
platform: 'node',
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Three plugins are registered (see [CLI / build](../cli/build.md) for descriptions). Node built-in modules are marked external. The `absWorkingDir` is set to the workspace root (or app root if no workspace is detected) so esbuild resolves node_modules correctly.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Phase 3: Copy public assets
|
|
67
|
+
|
|
68
|
+
`public/` is copied recursively to `dist/`. If `public/` does not exist, this phase is skipped silently.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Workspace dependency discovery
|
|
73
|
+
|
|
74
|
+
`discoverWorkspaceDependencies()` reads the app's `package.json` and collects all `workspace:*` entries from `dependencies`, `devDependencies`, and `peerDependencies`. It also scans source files for `@scope/pkg` import patterns to catch transitive workspace imports not listed in `package.json`.
|
|
75
|
+
|
|
76
|
+
For each candidate package name, it searches these directories under the workspace root:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
lib/<pkgName>
|
|
80
|
+
packages/<pkgName>
|
|
81
|
+
packages/runtime/<pkgName>
|
|
82
|
+
packages/plugins/<pkgName>
|
|
83
|
+
packages/domain/<pkgName>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A match requires the directory to have a `package.json` whose `name` field matches the expected package name, and a `src/` subdirectory.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Workspace resolver plugin
|
|
91
|
+
|
|
92
|
+
The `workspace-resolver` esbuild plugin handles `@scope/pkg` imports during bundling. It maps each import to the compiled `.tsx` files in `.swite-build/` by:
|
|
93
|
+
|
|
94
|
+
1. Finding the matching workspace dependency from the discovery step.
|
|
95
|
+
2. Reading the package's `package.json` exports field to resolve the subpath.
|
|
96
|
+
3. Converting the export path from `./src/Foo.uix` to `.swite-build/<depRelPath>/src/Foo.tsx`.
|
|
97
|
+
4. Falling back to `src/index.js` if exports resolution fails.
|