@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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# swite start
|
|
8
|
+
|
|
9
|
+
Starts the SWITE server in production mode.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
swite start
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
`swite start` starts the same `SwiteServer` as `swite dev` but with one critical difference: it calls `setProductionMode()` before the server starts. Production mode changes how `proxyToPython` resolves the Python service URL:
|
|
20
|
+
|
|
21
|
+
- **Dev mode**: if `PYTHON_SERVICE_URL` is not set, `proxyToPython` falls back to `http://localhost:{python.port}` from the config.
|
|
22
|
+
- **Production mode**: if `PYTHON_SERVICE_URL` is not set, `proxyToPython` throws immediately with an error rather than attempting a localhost connection.
|
|
23
|
+
|
|
24
|
+
`swite start` does not spawn the Python process. It does not start HMR. The HMR WebSocket server is still initialized (as part of `SwiteServer`), but since no file watcher is started and the client script is still served, the client will connect and wait — which is harmless in a production container.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## PYTHON_SERVICE_URL warning
|
|
29
|
+
|
|
30
|
+
If `services.python` is configured in `swiss.config.ts` and `PYTHON_SERVICE_URL` is not set in the environment at startup, SWITE prints a warning:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
[swite] WARNING: services.python is configured but PYTHON_SERVICE_URL is not set.
|
|
34
|
+
Proxy calls to Python will fail. Set PYTHON_SERVICE_URL to the running service URL.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The server starts regardless. The warning is there because proxy calls that happen before `PYTHON_SERVICE_URL` is set will throw at request time.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Production deployment notes
|
|
42
|
+
|
|
43
|
+
`swite start` serves source files through the same compilation and import-rewriting middleware as `swite dev`. It is not a static file server for pre-built output. If you want to serve the output of `swite build`, use a regular static HTTP server pointed at `dist/`.
|
|
44
|
+
|
|
45
|
+
`swite start` is intended for fullstack deployments where the Node server handles server-side logic and proxies to a separately deployed Python service, while continuing to serve the SwissJS frontend from source.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Contributing
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Build
|
|
12
|
+
|
|
13
|
+
SWITE is compiled from TypeScript to `dist/` using the TypeScript compiler:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm build # single build
|
|
17
|
+
pnpm dev # watch mode (tsc -b --watch)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The `bin.swite` field in `package.json` points to `dist/cli.js`. After a build, the CLI is runnable as `node dist/cli.js dev` from the package root, or via the `swite` binary when the package is installed.
|
|
21
|
+
|
|
22
|
+
To clean build artifacts and `node_modules`:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm clean
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Running tests
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm test
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Tests run via Node's built-in test runner (`node --import tsx --test`). Test files live in `__tests__/`. Add new test files with a `.test.ts` extension.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Generating an import map
|
|
41
|
+
|
|
42
|
+
The import map generation CLI is separate from the main build:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm generate-import-map
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This runs `src/internal/generate-import-map-cli.ts` via `tsx` and writes `.swite/import-map.json` in the current directory. Run this from the project root of an application, not from the SWITE package root.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Changesets and releases
|
|
53
|
+
|
|
54
|
+
SWITE uses `@changesets/cli` for versioning.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm changeset # create a changeset for your changes
|
|
58
|
+
pnpm release:version # bump versions from pending changesets
|
|
59
|
+
pnpm release:publish # publish to npm
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Branch conventions
|
|
65
|
+
|
|
66
|
+
- Work in feature branches named `feat/<topic>` or `fix/<topic>`.
|
|
67
|
+
- Do not push directly to the default branch.
|
|
68
|
+
- Each PR should include a changeset unless the change is documentation-only.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Package scope
|
|
73
|
+
|
|
74
|
+
SWITE is published as `@swissjs/swite`. The npm registry is `registry.npmjs.org` with `access: public`. The repository is part of the `kibologic/alpine-erp-core` GitHub repo.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Development
|
|
8
|
+
|
|
9
|
+
Notes for contributors and maintainers working on SWITE itself.
|
|
10
|
+
|
|
11
|
+
- [Contributing](./contributing.md) — local build, running tests, branch conventions
|
|
12
|
+
- [Internals](./internals.md) — workspace root detection algorithm, import map generation, symlink registry, package registry
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Internals
|
|
8
|
+
|
|
9
|
+
Implementation notes for the non-obvious parts of SWITE.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Workspace root detection algorithm
|
|
14
|
+
|
|
15
|
+
Two separate implementations exist: one in `SwiteServer` (used at server startup) and one in `src/kernel/workspace.ts` (used by handlers and resolution code at request time). They are slightly different.
|
|
16
|
+
|
|
17
|
+
### SwiteServer.findWorkspaceRoot
|
|
18
|
+
|
|
19
|
+
Walks up to six directory levels from `startDir`. At each level it looks for:
|
|
20
|
+
1. A `pnpm-workspace.yaml` file, or
|
|
21
|
+
2. A `package.json` with a `workspaces` field
|
|
22
|
+
|
|
23
|
+
The first directory matching either condition is returned. `SwiteConfig.rootDir` short-circuits this search if set.
|
|
24
|
+
|
|
25
|
+
### kernel/workspace.findWorkspaceRoot
|
|
26
|
+
|
|
27
|
+
Walks up to ten directory levels from `root`. At each level it additionally requires the candidate directory to have either a `lib/` or `packages/` subdirectory alongside the workspace marker file. This stricter check ensures the resolved workspace root is the SWS root (the monorepo that owns the `lib/` directory with compiled packages), not an intermediate workspace.
|
|
28
|
+
|
|
29
|
+
If a directory has `pnpm-workspace.yaml` but no `lib/` or `packages/`, the walk continues upward. This handles nested monorepo layouts where an inner workspace exists inside an outer SWS root.
|
|
30
|
+
|
|
31
|
+
### file-path-resolver /lib/ path resolution
|
|
32
|
+
|
|
33
|
+
For `/lib/` URLs specifically, `resolveFilePath` walks from the app root upward looking for a directory that has both `pnpm-workspace.yaml` and a `lib/` subdirectory present simultaneously. This is the most precise check and handles the case where `findWorkspaceRoot` returns an intermediate directory.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Import map generation
|
|
38
|
+
|
|
39
|
+
`generateImportMap` (`src/internal/generate-import-map.ts`) runs at the request of the CLI or the `generate-import-map` script, not automatically at dev server startup.
|
|
40
|
+
|
|
41
|
+
The generated file format:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"version": "1.0",
|
|
46
|
+
"generated": 1716211234567,
|
|
47
|
+
"imports": {
|
|
48
|
+
"@scope/pkg": "/swiss-packages/pkg/src/index.ts",
|
|
49
|
+
"@scope/pkg/components": "/swiss-packages/pkg/src/components/index.ts"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
At dev server startup, `setupMiddleware` calls `loadImportMap` to read this file. If found, it is passed to `ModuleResolver.setImportMap()` and becomes the fast path for all bare import resolutions. A cache miss falls through to the full `resolveBareImport` pipeline.
|
|
55
|
+
|
|
56
|
+
The SPA fallback handler also reads this file directly and merges it into the HTML importmap.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Symlink registry
|
|
61
|
+
|
|
62
|
+
`buildSymlinkRegistry` (`src/resolution/symlink-registry.ts`) is called once during server startup. It scans up to four `node_modules` directories:
|
|
63
|
+
|
|
64
|
+
1. `<appRoot>/node_modules`
|
|
65
|
+
2. `<parentOfAppRoot>/node_modules`
|
|
66
|
+
3. `<workspaceRoot>/node_modules`
|
|
67
|
+
4. `<frameworkMonorepoRoot>/node_modules`
|
|
68
|
+
|
|
69
|
+
For each directory, it reads all entries. For scoped entries (starting with `@`), it recurses one level and registers each symlink found there. For unscoped entries, it registers each symlink directly.
|
|
70
|
+
|
|
71
|
+
Registration: `fs.realpath(symlinkPath)` is resolved and stored in a `Map<string, string>` as `realpath → /node_modules/<pkgName>`.
|
|
72
|
+
|
|
73
|
+
`lookupInSymlinkRegistry(absolutePath)` iterates the map and returns the browser URL if the path starts with any registered realpath. This is called by `toUrl` as the very first step when an absolute path is received.
|
|
74
|
+
|
|
75
|
+
The registry is consulted before any other logic in `toUrl` because `fs.realpath()` is called throughout the handler chain (in `resolveFilePath` for `/node_modules/` URLs, in `NodeModuleHandler`, and elsewhere) and the resulting absolute paths would otherwise bypass all the workspace-relative URL conversion logic.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Package registry
|
|
80
|
+
|
|
81
|
+
`PackageRegistry` (`src/kernel/package-registry.ts`) is a process-wide singleton. It is first populated when `resolveWorkspacePackage` is called and the registry is empty. After that, it serves as a cache for the workspace scan.
|
|
82
|
+
|
|
83
|
+
The scan is recursive up to 15 levels deep and skips: `node_modules`, `dist`, `.git`, `.swite`, and hidden directories. Any directory containing a `package.json` with a `name` field is registered. Duplicates keep the first-found entry.
|
|
84
|
+
|
|
85
|
+
`rescan()` clears the cache and re-scans from the same root directories. It is called when a package is not found after the initial scan, in case new packages were added since the server started.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Compilation cache
|
|
90
|
+
|
|
91
|
+
`compilationCache` (`src/internal/cache/compilation-cache.ts`) is an in-memory cache keyed by absolute file path. Each entry stores the compiled source output and tracks the set of dependency URLs. Handlers call `compilationCache.get(filePath, getDependencies)` before compiling; if the file's mtime has not changed since the last compile, the cached output is returned.
|
|
92
|
+
|
|
93
|
+
Cache writes happen after every successful compilation via `compilationCache.set(filePath, rawCompiled, finalCode, getDependencies)`.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## path-fixup rationale
|
|
98
|
+
|
|
99
|
+
`fixSwissLibPaths` exists because `@swissjs/compiler` was originally written when the framework packages directory was named `swiss-lib/packages/`. When the directory was renamed to be served under `/swiss-packages/`, the compiler was not updated at the same time. Rather than forking every handler to patch its own output, a single central fixup is applied once per compilation.
|
|
100
|
+
|
|
101
|
+
The fixup replaces `/swiss-lib/packages/` before `/swiss-lib/` to avoid double-substitution if the function were called more than once on already-patched output.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Configuration
|
|
8
|
+
|
|
9
|
+
SWITE reads `swiss.config.ts` (or `swiss.config.js`) from the project root. The file must export a `SwiteUserConfig` object as its default export, typically via the `defineConfig` helper.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// swiss.config.ts
|
|
13
|
+
import { defineConfig } from '@swissjs/swite';
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
server: {
|
|
17
|
+
port: 3000,
|
|
18
|
+
host: 'localhost',
|
|
19
|
+
},
|
|
20
|
+
services: {
|
|
21
|
+
python: {
|
|
22
|
+
entry: 'server/main.py',
|
|
23
|
+
port: 8000,
|
|
24
|
+
autoStart: true,
|
|
25
|
+
healthCheck: '/health',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The config file is transpiled to ESM by esbuild at startup (bundling disabled, `platform: node`). The temporary output file is cleaned up before the server starts.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## SwiteUserConfig
|
|
36
|
+
|
|
37
|
+
| Field | Type | Default | Description |
|
|
38
|
+
|-------|------|---------|-------------|
|
|
39
|
+
| `server` | `ServerConfig` | `{}` | HTTP server options |
|
|
40
|
+
| `services` | `ServicesConfig` | `{}` | External service options |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ServerConfig
|
|
45
|
+
|
|
46
|
+
| Field | Type | Default | Description |
|
|
47
|
+
|-------|------|---------|-------------|
|
|
48
|
+
| `port` | `number` | `3000` | Port for the HTTP server |
|
|
49
|
+
| `host` | `string` | `"localhost"` | Host to bind. When `"localhost"`, SWITE binds to `0.0.0.0` internally so both IPv4 and IPv6 loopback addresses work |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ServicesConfig
|
|
54
|
+
|
|
55
|
+
| Field | Type | Default | Description |
|
|
56
|
+
|-------|------|---------|-------------|
|
|
57
|
+
| `python` | `PythonServiceConfig` | — | Python service definition. Omit if you have no Python backend |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## PythonServiceConfig
|
|
62
|
+
|
|
63
|
+
| Field | Type | Required | Description |
|
|
64
|
+
|-------|------|----------|-------------|
|
|
65
|
+
| `entry` | `string` | Yes | Path to the Python entry file, relative to the project root (e.g. `"server/main.py"`) |
|
|
66
|
+
| `port` | `number` | Yes | Port the Python service listens on |
|
|
67
|
+
| `autoStart` | `boolean` | Yes | When `true`, `swite dev` spawns the Python process automatically before starting the Node server |
|
|
68
|
+
| `healthCheck` | `string` | Yes | URL path polled to determine when the Python service is ready (e.g. `"/health"`). SWITE polls `http://localhost:{port}{healthCheck}` every 500 ms, with exponential back-off after five attempts, timing out after 15 seconds |
|
|
69
|
+
| `env` | `Record<string, string>` | No | Additional environment variables merged into the Python process environment alongside `PORT={port}` |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## defineConfig
|
|
74
|
+
|
|
75
|
+
`defineConfig(config: SwiteUserConfig): SwiteUserConfig` is a pass-through identity function that provides TypeScript type checking at the call site. Unknown fields are rejected at compile time.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Environment variables
|
|
80
|
+
|
|
81
|
+
In addition to `swiss.config.ts`, SWITE reads `.env` files at the project root for `import.meta.env` inlining. Variables are inlined at compile time into each compiled module; they are not available at runtime via a global object. SWITE loads both a base `.env` and a mode-specific `.env.development` or `.env.production` depending on whether the server is running in dev or production mode.
|
|
82
|
+
|
|
83
|
+
The following environment variables affect SWITE's own runtime behavior:
|
|
84
|
+
|
|
85
|
+
| Variable | Description |
|
|
86
|
+
|----------|-------------|
|
|
87
|
+
| `PYTHON_SERVICE_URL` | Base URL of the Python service in production. Required when running `swite start` with a Python service configured. Overrides the localhost fallback used in dev. |
|
|
88
|
+
| `INTERNAL_API_TOKEN` | Value injected as the `X-Internal-Token` header on every `proxyToPython` call |
|
|
89
|
+
| `SWITE_CDN_FALLBACK_SCOPES` | Comma-separated list of scoped package prefixes (e.g. `@types,@tanstack`) that are allowed to fall back to jsDelivr when not found locally. Unscoped packages always fall back. Scoped packages do not fall back by default. |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
SWITE - SWISS Development Server
|
|
4
|
+
Licensed under the MIT License.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Guide
|
|
8
|
+
|
|
9
|
+
Start here. This guide covers installing SWITE, laying out a project, configuring it, and building for production.
|
|
10
|
+
|
|
11
|
+
- [Quickstart](./quickstart.md) — install, run `swite dev`, write your first component, build
|
|
12
|
+
- [Project structure](./project-structure.md) — recommended directory layout, public dir, config file location
|
|
13
|
+
- [Configuration](./configuration.md) — full `SwiteUserConfig` and `PythonServiceConfig` reference
|
|
@@ -0,0 +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`.
|
|
@@ -0,0 +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
|
package/docs/index.md
ADDED
|
@@ -0,0 +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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swissjs/swite",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "SWITE - SWISS Development Server (Vite replacement)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,19 +8,9 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"swite": "dist/cli.js"
|
|
10
10
|
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc -b",
|
|
13
|
-
"dev": "tsc -b --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
|
-
},
|
|
21
11
|
"dependencies": {
|
|
22
|
-
"@swissjs/core": "0.1.
|
|
23
|
-
"@swissjs/compiler": "0.1.
|
|
12
|
+
"@swissjs/core": "0.1.8",
|
|
13
|
+
"@swissjs/compiler": "0.1.5",
|
|
24
14
|
"@swissjs/plugin-file-router": "1.0.2",
|
|
25
15
|
"chalk": "^5.3.0",
|
|
26
16
|
"chokidar": "^3.5.3",
|
|
@@ -42,17 +32,18 @@
|
|
|
42
32
|
"access": "public"
|
|
43
33
|
},
|
|
44
34
|
"license": "MIT",
|
|
45
|
-
"pnpm": {
|
|
46
|
-
"onlyBuiltDependencies": [
|
|
47
|
-
"esbuild"
|
|
48
|
-
],
|
|
49
|
-
"overrides": {
|
|
50
|
-
"path-to-regexp": "^0.1.13",
|
|
51
|
-
"picomatch": "^2.3.2"
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
35
|
"repository": {
|
|
55
36
|
"type": "git",
|
|
56
|
-
"url": "git+https://github.com/kibologic/
|
|
37
|
+
"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"
|
|
57
48
|
}
|
|
58
|
-
}
|
|
49
|
+
}
|
package/src/config/config.ts
CHANGED
|
@@ -23,6 +23,17 @@ export interface ServerConfig {
|
|
|
23
23
|
export interface SwiteUserConfig {
|
|
24
24
|
server?: ServerConfig;
|
|
25
25
|
services?: ServicesConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Package scopes that should be treated as "internal" or "private".
|
|
28
|
+
* These scopes prioritize local/monorepo resolution and are forbidden from CDN redirects.
|
|
29
|
+
* e.g. ["@kibologic", "@alpine"]
|
|
30
|
+
*/
|
|
31
|
+
internalScopes?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Manual override for sibling repository lookup.
|
|
34
|
+
* Swite will search these directories for local package source code.
|
|
35
|
+
*/
|
|
36
|
+
siblingRepositories?: string[];
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -8,12 +8,14 @@ import type { Response } from "express";
|
|
|
8
8
|
import { promises as fs } from "node:fs";
|
|
9
9
|
import { ModuleResolver } from "../../resolution/resolver.js";
|
|
10
10
|
import { resolveFilePath } from "../../resolution/path/file-path-resolver.js";
|
|
11
|
+
import type { SwiteUserConfig } from "../../config/config.js";
|
|
11
12
|
|
|
12
13
|
export interface HandlerContext {
|
|
13
14
|
resolver: ModuleResolver;
|
|
14
15
|
root: string;
|
|
15
16
|
workspaceRoot: string | null;
|
|
16
|
-
env
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
userConfig?: SwiteUserConfig;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -34,7 +36,7 @@ export class BaseHandler {
|
|
|
34
36
|
constructor(protected context: HandlerContext) {}
|
|
35
37
|
|
|
36
38
|
protected async resolveFilePath(url: string): Promise<string> {
|
|
37
|
-
return resolveFilePath(url, this.context.root, this.context.workspaceRoot);
|
|
39
|
+
return resolveFilePath(url, this.context.root, this.context.workspaceRoot, this.context.userConfig);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
protected async fileExists(filePath: string): Promise<boolean> {
|