@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.
Files changed (49) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.js +0 -0
  3. package/dist/config/config.d.ts +11 -0
  4. package/dist/config/config.d.ts.map +1 -1
  5. package/dist/dev-engine/handlers/base-handler.d.ts +3 -1
  6. package/dist/dev-engine/handlers/base-handler.d.ts.map +1 -1
  7. package/dist/dev-engine/handlers/base-handler.js +1 -1
  8. package/dist/dev-engine/handlers/node-module-handler.d.ts.map +1 -1
  9. package/dist/dev-engine/handlers/node-module-handler.js +30 -41
  10. package/dist/dev-engine/middleware/middleware-setup.d.ts +1 -0
  11. package/dist/dev-engine/middleware/middleware-setup.d.ts.map +1 -1
  12. package/dist/dev-engine/server.d.ts.map +1 -1
  13. package/dist/dev-engine/server.js +4 -0
  14. package/dist/kernel/package-finder.d.ts +7 -7
  15. package/dist/kernel/package-finder.d.ts.map +1 -1
  16. package/dist/kernel/package-finder.js +56 -40
  17. package/dist/resolution/bare-import-resolver.d.ts.map +1 -1
  18. package/dist/resolution/bare-import-resolver.js +13 -4
  19. package/dist/resolution/path/file-path-resolver.d.ts +2 -1
  20. package/dist/resolution/path/file-path-resolver.d.ts.map +1 -1
  21. package/dist/resolution/path/file-path-resolver.js +36 -9
  22. package/docs/architecture/build-pipeline.md +97 -0
  23. package/docs/architecture/dev-server.md +87 -0
  24. package/docs/architecture/hmr.md +78 -0
  25. package/docs/architecture/import-rewriting.md +101 -0
  26. package/docs/architecture/index.md +16 -0
  27. package/docs/architecture/python-integration.md +93 -0
  28. package/docs/architecture/resolution.md +92 -0
  29. package/docs/cli/build.md +78 -0
  30. package/docs/cli/dev.md +90 -0
  31. package/docs/cli/index.md +15 -0
  32. package/docs/cli/start.md +45 -0
  33. package/docs/development/contributing.md +74 -0
  34. package/docs/development/index.md +12 -0
  35. package/docs/development/internals.md +101 -0
  36. package/docs/guide/configuration.md +89 -0
  37. package/docs/guide/index.md +13 -0
  38. package/docs/guide/project-structure.md +75 -0
  39. package/docs/guide/quickstart.md +113 -0
  40. package/docs/index.md +16 -0
  41. package/package.json +15 -24
  42. package/src/config/config.ts +11 -0
  43. package/src/dev-engine/handlers/base-handler.ts +4 -2
  44. package/src/dev-engine/handlers/node-module-handler.ts +51 -78
  45. package/src/dev-engine/middleware/middleware-setup.ts +1 -0
  46. package/src/dev-engine/server.ts +38 -33
  47. package/src/kernel/package-finder.ts +59 -43
  48. package/src/resolution/bare-import-resolver.ts +14 -4
  49. 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.1",
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.5",
23
- "@swissjs/compiler": "0.1.4",
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/alpine-erp-core.git"
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
+ }
@@ -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: Record<string, string>;
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> {