@swissjs/swite 0.3.5 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/DIRECTIVE.md +57 -2
- package/__tests__/import-rewriter-bug.test.ts +100 -113
- package/__tests__/security-r001-r002.test.ts +190 -0
- package/dist/build-engine/builder.js +9 -9
- package/dist/cli.js +0 -0
- package/dist/config/config.d.ts +0 -5
- package/dist/config/config.d.ts.map +1 -1
- package/dist/dev-engine/handlers/base-handler.d.ts +6 -0
- package/dist/dev-engine/handlers/base-handler.d.ts.map +1 -1
- package/dist/dev-engine/handlers/base-handler.js +91 -0
- package/dist/dev-engine/handlers/ui-handler.d.ts +0 -1
- package/dist/dev-engine/handlers/ui-handler.d.ts.map +1 -1
- package/dist/dev-engine/handlers/ui-handler.js +2 -64
- package/dist/dev-engine/handlers/uix-handler.d.ts +0 -1
- package/dist/dev-engine/handlers/uix-handler.d.ts.map +1 -1
- package/dist/dev-engine/handlers/uix-handler.js +2 -58
- package/dist/dev-engine/hmr/hmr-client-template.js +111 -111
- package/dist/dev-engine/hmr/hmr.d.ts +10 -1
- package/dist/dev-engine/hmr/hmr.d.ts.map +1 -1
- package/dist/dev-engine/hmr/hmr.js +40 -2
- package/dist/dev-engine/middleware/middleware-setup.js +4 -3
- package/dist/dev-engine/middleware/static-files.d.ts.map +1 -1
- package/dist/dev-engine/middleware/static-files.js +145 -62
- package/dist/dev-engine/pythonDevManager.js +1 -1
- package/dist/dev-engine/router/file-router.d.ts.map +1 -1
- package/dist/dev-engine/router/file-router.js +2 -29
- package/dist/dev-engine/server.d.ts +7 -0
- package/dist/dev-engine/server.d.ts.map +1 -1
- package/dist/dev-engine/server.js +31 -3
- package/dist/kernel/package-finder.d.ts +0 -8
- package/dist/kernel/package-finder.d.ts.map +1 -1
- package/dist/kernel/package-finder.js +2 -2
- package/dist/kernel/package-registry.d.ts +6 -0
- package/dist/kernel/package-registry.d.ts.map +1 -1
- package/dist/kernel/package-registry.js +8 -0
- package/dist/kernel/workspace.d.ts.map +1 -1
- package/dist/kernel/workspace.js +12 -9
- package/docs/architecture/build-pipeline.md +97 -97
- package/docs/architecture/dev-server.md +87 -87
- package/docs/architecture/hmr.md +78 -78
- package/docs/architecture/import-rewriting.md +101 -101
- package/docs/architecture/index.md +16 -16
- package/docs/architecture/python-integration.md +93 -93
- package/docs/architecture/resolution.md +92 -92
- package/docs/cli/build.md +78 -78
- package/docs/cli/dev.md +90 -90
- package/docs/cli/index.md +15 -15
- package/docs/cli/start.md +45 -45
- package/docs/development/contributing.md +74 -74
- package/docs/development/index.md +12 -12
- package/docs/development/internals.md +101 -101
- package/docs/guide/configuration.md +89 -89
- package/docs/guide/index.md +13 -13
- package/docs/guide/project-structure.md +75 -75
- package/docs/guide/quickstart.md +113 -113
- package/docs/index.md +16 -16
- package/package.json +29 -16
- package/src/build-engine/builder.ts +9 -9
- package/src/config/config.ts +0 -5
- package/src/config/env.ts +98 -98
- package/src/dev-engine/handlers/base-handler.ts +109 -0
- package/src/dev-engine/handlers/ui-handler.ts +30 -110
- package/src/dev-engine/handlers/uix-handler.ts +21 -95
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -122
- package/src/dev-engine/hmr/hmr.ts +46 -1
- package/src/dev-engine/middleware/middleware-setup.ts +354 -354
- package/src/dev-engine/middleware/static-files.ts +203 -121
- package/src/dev-engine/pythonDevManager.ts +1 -1
- package/src/dev-engine/router/file-router.ts +2 -45
- package/src/dev-engine/server.ts +33 -3
- package/src/kernel/package-finder.ts +2 -2
- package/src/kernel/package-registry.ts +9 -0
- package/src/kernel/workspace.ts +8 -10
- package/src/resolution/cdn/cdn-fallback.ts +40 -40
- package/src/resolution/path/path-fixup.ts +27 -27
- package/src/resolution/rewriting/import-rewriter.ts +237 -237
- package/src/resolution/symlink-registry.ts +114 -114
package/docs/guide/quickstart.md
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Copyright (c) 2024 Themba Mzumara
|
|
3
|
-
SWITE - SWISS Development Server
|
|
4
|
-
Licensed under the MIT License.
|
|
5
|
-
-->
|
|
6
|
-
|
|
7
|
-
# Quickstart
|
|
8
|
-
|
|
9
|
-
Get a SwissJS application running under SWITE in a few minutes.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Prerequisites
|
|
14
|
-
|
|
15
|
-
- Node.js 18.19.x or later
|
|
16
|
-
- pnpm 10.x
|
|
17
|
-
- A SwissJS application (`src/index.ui` as entry point)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 1. Install SWITE
|
|
22
|
-
|
|
23
|
-
In your application package:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
pnpm add -D @swissjs/swite
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Add the CLI commands to `package.json`:
|
|
30
|
-
|
|
31
|
-
```json
|
|
32
|
-
{
|
|
33
|
-
"scripts": {
|
|
34
|
-
"dev": "swite dev",
|
|
35
|
-
"build": "swite build",
|
|
36
|
-
"start": "swite start"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 2. Start the dev server
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
pnpm dev
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
SWITE starts on `http://localhost:3000` by default. Every `.ui`, `.uix`, `.ts`, and `.js` request is compiled and import-rewritten on demand. HMR is active on port 24678.
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## 3. Project layout
|
|
54
|
-
|
|
55
|
-
A minimal SWITE project:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
my-app/
|
|
59
|
-
├── src/
|
|
60
|
-
│ ├── index.ui # entry point — mounted by public/index.html
|
|
61
|
-
│ ├── App.ui # root component
|
|
62
|
-
│ └── components/
|
|
63
|
-
├── public/
|
|
64
|
-
│ └── index.html # shell HTML — SWITE injects import map here
|
|
65
|
-
├── package.json
|
|
66
|
-
└── swiss.config.ts # optional
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
SWITE resolves files relative to the directory where `swite dev` is invoked (the project root). The `src/` prefix in URLs maps directly to `<root>/src/` on disk.
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 4. Write a component
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// src/components/Counter.ui
|
|
77
|
-
|
|
78
|
-
component Counter {
|
|
79
|
-
state {
|
|
80
|
-
let count: number = 0;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
render() {
|
|
84
|
-
return html`
|
|
85
|
-
<div>
|
|
86
|
-
<p>Count: ${this.count}</p>
|
|
87
|
-
<button onclick="${() => this.count++}">+1</button>
|
|
88
|
-
</div>
|
|
89
|
-
`;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
SWITE pipes `.ui` files through `@swissjs/compiler` (the SwissJS syntax transformer), then through esbuild's TypeScript stripper, rewrites all bare imports to browser-resolvable URLs, and serves the result as `application/javascript`.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## 5. Build for production
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
pnpm build
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Output goes to `dist/`. The build entry is fixed to `src/index.ui`. See [`swite build`](../cli/build.md) for details.
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Next
|
|
109
|
-
|
|
110
|
-
- [Project structure](./project-structure.md) — layout conventions
|
|
111
|
-
- [Configuration](./configuration.md) — server port, Python service options
|
|
112
|
-
- [CLI reference](../cli/index.md) — all commands
|
|
113
|
-
- [Architecture](../architecture/index.md) — how SWITE works internally
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Quickstart
|
|
8
|
+
|
|
9
|
+
Get a SwissJS application running under SWITE in a few minutes.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- Node.js 18.19.x or later
|
|
16
|
+
- pnpm 10.x
|
|
17
|
+
- A SwissJS application (`src/index.ui` as entry point)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Install SWITE
|
|
22
|
+
|
|
23
|
+
In your application package:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -D @swissjs/swite
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Add the CLI commands to `package.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "swite dev",
|
|
35
|
+
"build": "swite build",
|
|
36
|
+
"start": "swite start"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. Start the dev server
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pnpm dev
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
SWITE starts on `http://localhost:3000` by default. Every `.ui`, `.uix`, `.ts`, and `.js` request is compiled and import-rewritten on demand. HMR is active on port 24678.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 3. Project layout
|
|
54
|
+
|
|
55
|
+
A minimal SWITE project:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
my-app/
|
|
59
|
+
├── src/
|
|
60
|
+
│ ├── index.ui # entry point — mounted by public/index.html
|
|
61
|
+
│ ├── App.ui # root component
|
|
62
|
+
│ └── components/
|
|
63
|
+
├── public/
|
|
64
|
+
│ └── index.html # shell HTML — SWITE injects import map here
|
|
65
|
+
├── package.json
|
|
66
|
+
└── swiss.config.ts # optional
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
SWITE resolves files relative to the directory where `swite dev` is invoked (the project root). The `src/` prefix in URLs maps directly to `<root>/src/` on disk.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 4. Write a component
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// src/components/Counter.ui
|
|
77
|
+
|
|
78
|
+
component Counter {
|
|
79
|
+
state {
|
|
80
|
+
let count: number = 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
render() {
|
|
84
|
+
return html`
|
|
85
|
+
<div>
|
|
86
|
+
<p>Count: ${this.count}</p>
|
|
87
|
+
<button onclick="${() => this.count++}">+1</button>
|
|
88
|
+
</div>
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
SWITE pipes `.ui` files through `@swissjs/compiler` (the SwissJS syntax transformer), then through esbuild's TypeScript stripper, rewrites all bare imports to browser-resolvable URLs, and serves the result as `application/javascript`.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 5. Build for production
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm build
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Output goes to `dist/`. The build entry is fixed to `src/index.ui`. See [`swite build`](../cli/build.md) for details.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Next
|
|
109
|
+
|
|
110
|
+
- [Project structure](./project-structure.md) — layout conventions
|
|
111
|
+
- [Configuration](./configuration.md) — server port, Python service options
|
|
112
|
+
- [CLI reference](../cli/index.md) — all commands
|
|
113
|
+
- [Architecture](../architecture/index.md) — how SWITE works internally
|
package/docs/index.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Copyright (c) 2024 Themba Mzumara
|
|
3
|
-
SWITE - SWISS Development Server
|
|
4
|
-
Licensed under the MIT License.
|
|
5
|
-
-->
|
|
6
|
-
|
|
7
|
-
# SWITE Documentation
|
|
8
|
-
|
|
9
|
-
SWITE (`@swissjs/swite`) is the development server and production build tool for SwissJS applications. It provides on-demand TypeScript and SwissJS source compilation, bare-import rewriting, HMR, Python service lifecycle management, and an esbuild-powered production pipeline.
|
|
10
|
-
|
|
11
|
-
## Sections
|
|
12
|
-
|
|
13
|
-
- [Guide](./guide/index.md) — Installation, project layout, configuration reference, and first steps
|
|
14
|
-
- [CLI](./cli/index.md) — `swite dev`, `swite build`, and `swite start` command reference
|
|
15
|
-
- [Architecture](./architecture/index.md) — Internals: dev server, import resolution, HMR, build pipeline, Python integration
|
|
16
|
-
- [Development](./development/index.md) — Contributing, local build, and internal implementation notes
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# SWITE Documentation
|
|
8
|
+
|
|
9
|
+
SWITE (`@swissjs/swite`) is the development server and production build tool for SwissJS applications. It provides on-demand TypeScript and SwissJS source compilation, bare-import rewriting, HMR, Python service lifecycle management, and an esbuild-powered production pipeline.
|
|
10
|
+
|
|
11
|
+
## Sections
|
|
12
|
+
|
|
13
|
+
- [Guide](./guide/index.md) — Installation, project layout, configuration reference, and first steps
|
|
14
|
+
- [CLI](./cli/index.md) — `swite dev`, `swite build`, and `swite start` command reference
|
|
15
|
+
- [Architecture](./architecture/index.md) — Internals: dev server, import resolution, HMR, build pipeline, Python integration
|
|
16
|
+
- [Development](./development/index.md) — Contributing, local build, and internal implementation notes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swissjs/swite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "SWITE - SWISS Development Server (Vite replacement)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,16 +8,26 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"swite": "dist/cli.js"
|
|
10
10
|
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc --noEmitOnError false",
|
|
13
|
+
"dev": "tsc --noEmitOnError false --watch",
|
|
14
|
+
"clean": "rm -rf dist node_modules && rm -f tsconfig.tsbuildinfo",
|
|
15
|
+
"generate-import-map": "tsx src/internal/generate-import-map-cli.ts",
|
|
16
|
+
"test": "node --import tsx --test __tests__/import-rewriter-bug.test.ts",
|
|
17
|
+
"changeset": "changeset",
|
|
18
|
+
"release:version": "changeset version",
|
|
19
|
+
"release:publish": "changeset publish"
|
|
20
|
+
},
|
|
11
21
|
"dependencies": {
|
|
12
|
-
"@swissjs/core": "
|
|
13
|
-
"@swissjs/compiler": "
|
|
14
|
-
"@swissjs/plugin-file-router": "1.
|
|
22
|
+
"@swissjs/core": "1.2.1",
|
|
23
|
+
"@swissjs/compiler": "1.2.1",
|
|
24
|
+
"@swissjs/plugin-file-router": "1.2.3",
|
|
15
25
|
"chalk": "^5.3.0",
|
|
16
26
|
"chokidar": "^3.5.3",
|
|
17
27
|
"es-module-lexer": "^1.3.1",
|
|
18
28
|
"esbuild": "^0.25.0",
|
|
19
29
|
"express": "^4.18.2",
|
|
20
|
-
"ws": "^8.
|
|
30
|
+
"ws": "^8.21.0"
|
|
21
31
|
},
|
|
22
32
|
"devDependencies": {
|
|
23
33
|
"@types/express": "^4.17.21",
|
|
@@ -32,18 +42,21 @@
|
|
|
32
42
|
"access": "public"
|
|
33
43
|
},
|
|
34
44
|
"license": "MIT",
|
|
45
|
+
"pnpm": {
|
|
46
|
+
"onlyBuiltDependencies": [
|
|
47
|
+
"esbuild"
|
|
48
|
+
],
|
|
49
|
+
"overrides": {
|
|
50
|
+
"path-to-regexp": "^0.1.13",
|
|
51
|
+
"picomatch": "^2.3.2",
|
|
52
|
+
"qs": ">=6.15.2",
|
|
53
|
+
"esbuild": ">=0.28.1",
|
|
54
|
+
"vite": ">=6.4.3",
|
|
55
|
+
"js-yaml": "^3.15.0"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
35
58
|
"repository": {
|
|
36
59
|
"type": "git",
|
|
37
60
|
"url": "git+https://github.com/kibologic/swite.git"
|
|
38
|
-
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "tsc -b",
|
|
41
|
-
"dev": "tsc -b --watch",
|
|
42
|
-
"clean": "rm -rf dist node_modules && rm -f tsconfig.tsbuildinfo",
|
|
43
|
-
"generate-import-map": "tsx src/internal/generate-import-map-cli.ts",
|
|
44
|
-
"test": "node --import tsx --test __tests__/import-rewriter-bug.test.ts",
|
|
45
|
-
"changeset": "changeset",
|
|
46
|
-
"release:version": "changeset version",
|
|
47
|
-
"release:publish": "changeset publish"
|
|
48
61
|
}
|
|
49
|
-
}
|
|
62
|
+
}
|
|
@@ -56,9 +56,9 @@ export class SwiteBuilder {
|
|
|
56
56
|
await this.copyPublicAssets();
|
|
57
57
|
|
|
58
58
|
const duration = Date.now() - startTime;
|
|
59
|
-
console.log(chalk.green(`\n
|
|
59
|
+
console.log(chalk.green(`\n[OK] Build completed in ${duration}ms\n`));
|
|
60
60
|
} catch (error) {
|
|
61
|
-
console.error(chalk.red("\n
|
|
61
|
+
console.error(chalk.red("\n[FAIL] Build failed:"), error);
|
|
62
62
|
throw error;
|
|
63
63
|
} finally {
|
|
64
64
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
@@ -66,13 +66,13 @@ export class SwiteBuilder {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
private async cleanOutputDir(): Promise<void> {
|
|
69
|
-
console.log(chalk.blue("
|
|
69
|
+
console.log(chalk.blue("[clean] Cleaning output directory..."));
|
|
70
70
|
await fs.rm(this.config.outDir, { recursive: true, force: true });
|
|
71
71
|
await fs.mkdir(this.config.outDir, { recursive: true });
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
private async compileSwissFiles(tempDir: string): Promise<void> {
|
|
75
|
-
console.log(chalk.blue("
|
|
75
|
+
console.log(chalk.blue("[compile] Compiling Swiss files..."));
|
|
76
76
|
await fs.mkdir(tempDir, { recursive: true });
|
|
77
77
|
|
|
78
78
|
const workspaceRoot = await this.findWorkspaceRoot(this.config.root);
|
|
@@ -90,7 +90,7 @@ export class SwiteBuilder {
|
|
|
90
90
|
// Step 2: Discover and compile workspace dependencies
|
|
91
91
|
const workspaceDeps = await this.discoverWorkspaceDependencies();
|
|
92
92
|
for (const dep of workspaceDeps) {
|
|
93
|
-
console.log(chalk.blue(
|
|
93
|
+
console.log(chalk.blue(`[bundle] Compiling dependency: ${dep.name}`));
|
|
94
94
|
// Preserve workspace structure: libraries/skltn/src or packages/cart/src or modules/cart/src
|
|
95
95
|
const depRelativeToWorkspace = workspaceRoot
|
|
96
96
|
? path.relative(workspaceRoot, dep.pkgDir)
|
|
@@ -269,7 +269,7 @@ export class SwiteBuilder {
|
|
|
269
269
|
pkgDir,
|
|
270
270
|
});
|
|
271
271
|
console.log(
|
|
272
|
-
chalk.gray(`
|
|
272
|
+
chalk.gray(` [dep] Found workspace dependency: ${depName}`),
|
|
273
273
|
);
|
|
274
274
|
break;
|
|
275
275
|
}
|
|
@@ -315,7 +315,7 @@ export class SwiteBuilder {
|
|
|
315
315
|
});
|
|
316
316
|
console.log(
|
|
317
317
|
chalk.gray(
|
|
318
|
-
`
|
|
318
|
+
` [dep] Discovered transitive dependency: ${pkgName}`,
|
|
319
319
|
),
|
|
320
320
|
);
|
|
321
321
|
break;
|
|
@@ -325,14 +325,14 @@ export class SwiteBuilder {
|
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
327
|
} catch (error) {
|
|
328
|
-
console.warn(chalk.yellow("
|
|
328
|
+
console.warn(chalk.yellow("[warn] Could not discover dependencies:"), error);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
return deps;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
private async bundle(tempDir: string): Promise<void> {
|
|
335
|
-
console.log(chalk.blue("
|
|
335
|
+
console.log(chalk.blue("[bundle] Bundling with esbuild..."));
|
|
336
336
|
|
|
337
337
|
const workspaceRoot = await this.findWorkspaceRoot(this.config.root);
|
|
338
338
|
const appRelativeToWorkspace = workspaceRoot
|
package/src/config/config.ts
CHANGED
|
@@ -37,11 +37,6 @@ export interface SwiteUserConfig {
|
|
|
37
37
|
* Path is relative to the project root.
|
|
38
38
|
*/
|
|
39
39
|
entry?: string;
|
|
40
|
-
/**
|
|
41
|
-
* Module aliases resolved during bare import resolution.
|
|
42
|
-
* e.g. { "@/": "src/" } maps @/ imports to the src/ directory.
|
|
43
|
-
*/
|
|
44
|
-
aliases?: Record<string, string>;
|
|
45
40
|
/**
|
|
46
41
|
* Glob patterns to exclude from HMR watching in addition to the defaults
|
|
47
42
|
* (node_modules, .git, dist). Useful for generated files or large assets.
|
package/src/config/env.ts
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Environment Variable Support for SWITE
|
|
3
|
-
* Provides import.meta.env replacement for SWITE
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
|
|
9
|
-
export interface EnvConfig {
|
|
10
|
-
mode?: "development" | "production";
|
|
11
|
-
prefix?: string; // Default: "SWITE_"
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Load environment variables from .env files.
|
|
16
|
-
* Supports .env, .env.local, .env.[mode], .env.[mode].local
|
|
17
|
-
*/
|
|
18
|
-
export function loadEnv(
|
|
19
|
-
root: string,
|
|
20
|
-
mode: string = "development",
|
|
21
|
-
prefix: string = "SWITE_",
|
|
22
|
-
): Record<string, string> {
|
|
23
|
-
const env: Record<string, string> = {};
|
|
24
|
-
const envFiles = [`.env.${mode}.local`, `.env.${mode}`, `.env.local`, `.env`];
|
|
25
|
-
|
|
26
|
-
for (const file of envFiles) {
|
|
27
|
-
const envPath = join(root, file);
|
|
28
|
-
if (!existsSync(envPath)) continue;
|
|
29
|
-
const content = readFileSync(envPath, "utf-8");
|
|
30
|
-
for (const line of content.split("\n")) {
|
|
31
|
-
const trimmed = line.trim();
|
|
32
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
33
|
-
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
34
|
-
if (!match) continue;
|
|
35
|
-
const key = match[1].trim();
|
|
36
|
-
const value = match[2].replace(/^["']|["']$/g, "");
|
|
37
|
-
if (key.startsWith(prefix) || key.startsWith("PUBLIC_")) {
|
|
38
|
-
env[key] = value;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return env;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Replace all import.meta.env references in compiled code with their literal values.
|
|
48
|
-
*
|
|
49
|
-
* This is the only correct approach for ES modules — import.meta is sealed and
|
|
50
|
-
* import.meta.env cannot be assigned at runtime. All substitution must happen at
|
|
51
|
-
* transform time (here, after esbuild strips TypeScript).
|
|
52
|
-
*
|
|
53
|
-
* Handles:
|
|
54
|
-
* - import.meta.env.KEY → JSON.stringify(env[KEY]) or "undefined"
|
|
55
|
-
* - import.meta.env.DEV → true/false literal
|
|
56
|
-
* - import.meta.env.PROD → true/false literal
|
|
57
|
-
* - import.meta.env.MODE → "development"/"production" literal
|
|
58
|
-
* - bare import.meta.env → serialized object literal (for spread, typeof, etc.)
|
|
59
|
-
*/
|
|
60
|
-
export function inlineEnvReferences(
|
|
61
|
-
code: string,
|
|
62
|
-
env: Record<string, string>,
|
|
63
|
-
mode: string = "development",
|
|
64
|
-
): string {
|
|
65
|
-
if (!code.includes("import.meta.env")) return code;
|
|
66
|
-
|
|
67
|
-
const isDev = mode !== "production";
|
|
68
|
-
|
|
69
|
-
// Named key access first (most specific)
|
|
70
|
-
code = code.replace(/\bimport\.meta\.env\.([A-Z_][A-Z0-9_]*)\b/g, (_, key: string) => {
|
|
71
|
-
if (key === "DEV") return String(isDev);
|
|
72
|
-
if (key === "PROD") return String(!isDev);
|
|
73
|
-
if (key === "MODE") return JSON.stringify(mode);
|
|
74
|
-
if (key === "SSR") return "false";
|
|
75
|
-
if (key in env) return JSON.stringify(env[key]);
|
|
76
|
-
return "undefined";
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Bare import.meta.env (spread/typeof patterns)
|
|
80
|
-
if (code.includes("import.meta.env")) {
|
|
81
|
-
const envLiteral = buildEnvLiteral(env, mode);
|
|
82
|
-
code = code.replace(/\bimport\.meta\.env\b/g, envLiteral);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return code;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function buildEnvLiteral(env: Record<string, string>, mode: string): string {
|
|
89
|
-
const isDev = mode !== "production";
|
|
90
|
-
const entries: string[] = [
|
|
91
|
-
`MODE:${JSON.stringify(mode)}`,
|
|
92
|
-
`DEV:${isDev}`,
|
|
93
|
-
`PROD:${!isDev}`,
|
|
94
|
-
`SSR:false`,
|
|
95
|
-
...Object.entries(env).map(([k, v]) => `${JSON.stringify(k)}:${JSON.stringify(v)}`),
|
|
96
|
-
];
|
|
97
|
-
return `({${entries.join(",")}})`;
|
|
98
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Environment Variable Support for SWITE
|
|
3
|
+
* Provides import.meta.env replacement for SWITE
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
export interface EnvConfig {
|
|
10
|
+
mode?: "development" | "production";
|
|
11
|
+
prefix?: string; // Default: "SWITE_"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load environment variables from .env files.
|
|
16
|
+
* Supports .env, .env.local, .env.[mode], .env.[mode].local
|
|
17
|
+
*/
|
|
18
|
+
export function loadEnv(
|
|
19
|
+
root: string,
|
|
20
|
+
mode: string = "development",
|
|
21
|
+
prefix: string = "SWITE_",
|
|
22
|
+
): Record<string, string> {
|
|
23
|
+
const env: Record<string, string> = {};
|
|
24
|
+
const envFiles = [`.env.${mode}.local`, `.env.${mode}`, `.env.local`, `.env`];
|
|
25
|
+
|
|
26
|
+
for (const file of envFiles) {
|
|
27
|
+
const envPath = join(root, file);
|
|
28
|
+
if (!existsSync(envPath)) continue;
|
|
29
|
+
const content = readFileSync(envPath, "utf-8");
|
|
30
|
+
for (const line of content.split("\n")) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
33
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
34
|
+
if (!match) continue;
|
|
35
|
+
const key = match[1].trim();
|
|
36
|
+
const value = match[2].replace(/^["']|["']$/g, "");
|
|
37
|
+
if (key.startsWith(prefix) || key.startsWith("PUBLIC_")) {
|
|
38
|
+
env[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Replace all import.meta.env references in compiled code with their literal values.
|
|
48
|
+
*
|
|
49
|
+
* This is the only correct approach for ES modules — import.meta is sealed and
|
|
50
|
+
* import.meta.env cannot be assigned at runtime. All substitution must happen at
|
|
51
|
+
* transform time (here, after esbuild strips TypeScript).
|
|
52
|
+
*
|
|
53
|
+
* Handles:
|
|
54
|
+
* - import.meta.env.KEY → JSON.stringify(env[KEY]) or "undefined"
|
|
55
|
+
* - import.meta.env.DEV → true/false literal
|
|
56
|
+
* - import.meta.env.PROD → true/false literal
|
|
57
|
+
* - import.meta.env.MODE → "development"/"production" literal
|
|
58
|
+
* - bare import.meta.env → serialized object literal (for spread, typeof, etc.)
|
|
59
|
+
*/
|
|
60
|
+
export function inlineEnvReferences(
|
|
61
|
+
code: string,
|
|
62
|
+
env: Record<string, string>,
|
|
63
|
+
mode: string = "development",
|
|
64
|
+
): string {
|
|
65
|
+
if (!code.includes("import.meta.env")) return code;
|
|
66
|
+
|
|
67
|
+
const isDev = mode !== "production";
|
|
68
|
+
|
|
69
|
+
// Named key access first (most specific)
|
|
70
|
+
code = code.replace(/\bimport\.meta\.env\.([A-Z_][A-Z0-9_]*)\b/g, (_, key: string) => {
|
|
71
|
+
if (key === "DEV") return String(isDev);
|
|
72
|
+
if (key === "PROD") return String(!isDev);
|
|
73
|
+
if (key === "MODE") return JSON.stringify(mode);
|
|
74
|
+
if (key === "SSR") return "false";
|
|
75
|
+
if (key in env) return JSON.stringify(env[key]);
|
|
76
|
+
return "undefined";
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Bare import.meta.env (spread/typeof patterns)
|
|
80
|
+
if (code.includes("import.meta.env")) {
|
|
81
|
+
const envLiteral = buildEnvLiteral(env, mode);
|
|
82
|
+
code = code.replace(/\bimport\.meta\.env\b/g, envLiteral);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return code;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildEnvLiteral(env: Record<string, string>, mode: string): string {
|
|
89
|
+
const isDev = mode !== "production";
|
|
90
|
+
const entries: string[] = [
|
|
91
|
+
`MODE:${JSON.stringify(mode)}`,
|
|
92
|
+
`DEV:${isDev}`,
|
|
93
|
+
`PROD:${!isDev}`,
|
|
94
|
+
`SSR:false`,
|
|
95
|
+
...Object.entries(env).map(([k, v]) => `${JSON.stringify(k)}:${JSON.stringify(v)}`),
|
|
96
|
+
];
|
|
97
|
+
return `({${entries.join(",")}})`;
|
|
98
|
+
}
|