@swissjs/swite 0.4.1 → 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 +7 -0
- package/__tests__/import-rewriter-bug.test.ts +122 -122
- package/__tests__/security-r001-r002.test.ts +190 -190
- package/dist/cli.js +0 -0
- package/dist/dev-engine/hmr/hmr-client-template.js +111 -111
- package/dist/dev-engine/middleware/middleware-setup.js +4 -3
- 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 +10 -9
- package/src/config/env.ts +98 -98
- package/src/dev-engine/handlers/ui-handler.ts +30 -30
- package/src/dev-engine/handlers/uix-handler.ts +21 -21
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -122
- package/src/dev-engine/middleware/middleware-setup.ts +354 -354
- package/src/dev-engine/middleware/static-files.ts +813 -813
- 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
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Copyright (c) 2024 Themba Mzumara
|
|
3
|
-
SWITE - SWISS Development Server
|
|
4
|
-
Licensed under the MIT License.
|
|
5
|
-
-->
|
|
6
|
-
|
|
7
|
-
# Project Structure
|
|
8
|
-
|
|
9
|
-
Conventions for a SWITE-powered application.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Application layout
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
my-app/
|
|
17
|
-
├── src/
|
|
18
|
-
│ ├── index.ui # entry point — referenced by public/index.html
|
|
19
|
-
│ ├── App.ui # root component
|
|
20
|
-
│ ├── components/ # reusable UI components
|
|
21
|
-
│ └── types/ # shared TypeScript types
|
|
22
|
-
├── public/
|
|
23
|
-
│ └── index.html # HTML shell — SWITE injects import map and CSS links
|
|
24
|
-
├── .swite/
|
|
25
|
-
│ └── import-map.json # generated at build time; optional at dev time
|
|
26
|
-
├── dist/ # production build output (gitignored)
|
|
27
|
-
├── package.json
|
|
28
|
-
└── swiss.config.ts # optional config
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Source file conventions
|
|
34
|
-
|
|
35
|
-
| Extension | Handler | Description |
|
|
36
|
-
|-----------|---------|-------------|
|
|
37
|
-
| `.ui` | UIHandler | SwissJS component syntax; compiled by `@swissjs/compiler` then TypeScript-stripped by esbuild |
|
|
38
|
-
| `.uix` | UIXHandler | JSX variant of SwissJS component syntax; same pipeline as `.ui` |
|
|
39
|
-
| `.ts` | TSHandler | Plain TypeScript; TypeScript-stripped by esbuild only |
|
|
40
|
-
| `.js` | JSHandler | Plain JavaScript; import-rewritten but not compiled |
|
|
41
|
-
| `.mjs` | MJSHandler | Same as `.js`; falls back to `.js` if `.mjs` does not exist |
|
|
42
|
-
|
|
43
|
-
SWITE handles extension negotiation: a request for `/src/Foo.js` will be served from `Foo.ts`, `Foo.ui`, or `Foo.uix` if the `.js` file does not exist on disk. Similarly, a request for `/src/Foo.ts` falls back to `.ui` or `.uix`.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## public/ directory
|
|
48
|
-
|
|
49
|
-
The `public/` directory is served as static files. SWITE's SPA fallback reads `public/index.html` and:
|
|
50
|
-
|
|
51
|
-
- Injects cache-busting query parameters on the entry point `<script>` tag
|
|
52
|
-
- Injects an `<script type="importmap">` block from `.swite/import-map.json` (merging with any existing importmap in HTML)
|
|
53
|
-
- Injects `<link rel="stylesheet">` tags for CSS files discovered in the entry point source
|
|
54
|
-
|
|
55
|
-
Files under `public/` are served with `no-cache` headers during development.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Config file
|
|
60
|
-
|
|
61
|
-
SWITE looks for `swiss.config.ts` (preferred) or `swiss.config.js` at the project root. Both are optional. If neither exists, SWITE starts with defaults: port 3000, host `localhost`, no Python service.
|
|
62
|
-
|
|
63
|
-
See [Configuration](./configuration.md) for the full field reference.
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Workspace layout
|
|
68
|
-
|
|
69
|
-
When SWITE detects a monorepo (by walking up the directory tree looking for `pnpm-workspace.yaml` or a `package.json` with `workspaces`), it:
|
|
70
|
-
|
|
71
|
-
- Scans workspace `node_modules` for package resolution
|
|
72
|
-
- Serves `lib/`, `libraries/`, and `modules/` directories as static file prefixes
|
|
73
|
-
- Compiles source files under `packages/` through the handler pipeline
|
|
74
|
-
|
|
75
|
-
The workspace root is detected automatically; it can also be set explicitly via `SwiteConfig.rootDir`.
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Project Structure
|
|
8
|
+
|
|
9
|
+
Conventions for a SWITE-powered application.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Application layout
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
my-app/
|
|
17
|
+
├── src/
|
|
18
|
+
│ ├── index.ui # entry point — referenced by public/index.html
|
|
19
|
+
│ ├── App.ui # root component
|
|
20
|
+
│ ├── components/ # reusable UI components
|
|
21
|
+
│ └── types/ # shared TypeScript types
|
|
22
|
+
├── public/
|
|
23
|
+
│ └── index.html # HTML shell — SWITE injects import map and CSS links
|
|
24
|
+
├── .swite/
|
|
25
|
+
│ └── import-map.json # generated at build time; optional at dev time
|
|
26
|
+
├── dist/ # production build output (gitignored)
|
|
27
|
+
├── package.json
|
|
28
|
+
└── swiss.config.ts # optional config
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Source file conventions
|
|
34
|
+
|
|
35
|
+
| Extension | Handler | Description |
|
|
36
|
+
|-----------|---------|-------------|
|
|
37
|
+
| `.ui` | UIHandler | SwissJS component syntax; compiled by `@swissjs/compiler` then TypeScript-stripped by esbuild |
|
|
38
|
+
| `.uix` | UIXHandler | JSX variant of SwissJS component syntax; same pipeline as `.ui` |
|
|
39
|
+
| `.ts` | TSHandler | Plain TypeScript; TypeScript-stripped by esbuild only |
|
|
40
|
+
| `.js` | JSHandler | Plain JavaScript; import-rewritten but not compiled |
|
|
41
|
+
| `.mjs` | MJSHandler | Same as `.js`; falls back to `.js` if `.mjs` does not exist |
|
|
42
|
+
|
|
43
|
+
SWITE handles extension negotiation: a request for `/src/Foo.js` will be served from `Foo.ts`, `Foo.ui`, or `Foo.uix` if the `.js` file does not exist on disk. Similarly, a request for `/src/Foo.ts` falls back to `.ui` or `.uix`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## public/ directory
|
|
48
|
+
|
|
49
|
+
The `public/` directory is served as static files. SWITE's SPA fallback reads `public/index.html` and:
|
|
50
|
+
|
|
51
|
+
- Injects cache-busting query parameters on the entry point `<script>` tag
|
|
52
|
+
- Injects an `<script type="importmap">` block from `.swite/import-map.json` (merging with any existing importmap in HTML)
|
|
53
|
+
- Injects `<link rel="stylesheet">` tags for CSS files discovered in the entry point source
|
|
54
|
+
|
|
55
|
+
Files under `public/` are served with `no-cache` headers during development.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Config file
|
|
60
|
+
|
|
61
|
+
SWITE looks for `swiss.config.ts` (preferred) or `swiss.config.js` at the project root. Both are optional. If neither exists, SWITE starts with defaults: port 3000, host `localhost`, no Python service.
|
|
62
|
+
|
|
63
|
+
See [Configuration](./configuration.md) for the full field reference.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Workspace layout
|
|
68
|
+
|
|
69
|
+
When SWITE detects a monorepo (by walking up the directory tree looking for `pnpm-workspace.yaml` or a `package.json` with `workspaces`), it:
|
|
70
|
+
|
|
71
|
+
- Scans workspace `node_modules` for package resolution
|
|
72
|
+
- Serves `lib/`, `libraries/`, and `modules/` directories as static file prefixes
|
|
73
|
+
- Compiles source files under `packages/` through the handler pipeline
|
|
74
|
+
|
|
75
|
+
The workspace root is detected automatically; it can also be set explicitly via `SwiteConfig.rootDir`.
|
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.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "SWITE - SWISS Development Server (Vite replacement)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"swite": "dist/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "tsc
|
|
13
|
-
"dev": "tsc
|
|
12
|
+
"build": "tsc --noEmitOnError false",
|
|
13
|
+
"dev": "tsc --noEmitOnError false --watch",
|
|
14
14
|
"clean": "rm -rf dist node_modules && rm -f tsconfig.tsbuildinfo",
|
|
15
15
|
"generate-import-map": "tsx src/internal/generate-import-map-cli.ts",
|
|
16
16
|
"test": "node --import tsx --test __tests__/import-rewriter-bug.test.ts",
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
"release:publish": "changeset publish"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@swissjs/core": "
|
|
23
|
-
"@swissjs/compiler": "
|
|
24
|
-
"@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",
|
|
25
25
|
"chalk": "^5.3.0",
|
|
26
26
|
"chokidar": "^3.5.3",
|
|
27
27
|
"es-module-lexer": "^1.3.1",
|
|
28
28
|
"esbuild": "^0.25.0",
|
|
29
29
|
"express": "^4.18.2",
|
|
30
|
-
"ws": "^8.
|
|
30
|
+
"ws": "^8.21.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/express": "^4.17.21",
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"path-to-regexp": "^0.1.13",
|
|
51
51
|
"picomatch": "^2.3.2",
|
|
52
52
|
"qs": ">=6.15.2",
|
|
53
|
-
"esbuild": ">=0.
|
|
54
|
-
"vite": ">=6.4.
|
|
53
|
+
"esbuild": ">=0.28.1",
|
|
54
|
+
"vite": ">=6.4.3",
|
|
55
|
+
"js-yaml": "^3.15.0"
|
|
55
56
|
}
|
|
56
57
|
},
|
|
57
58
|
"repository": {
|
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
|
+
}
|