@jk2908/solas 0.2.3 → 0.3.1
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 +20 -0
- package/README.md +2 -0
- package/dist/cli.js +77 -83
- package/dist/error-boundary.d.ts +1 -1
- package/dist/error-boundary.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +10 -14
- package/dist/internal/build.d.ts +1 -1
- package/dist/internal/build.js +4 -4
- package/dist/internal/codegen/config.d.ts +1 -1
- package/dist/internal/codegen/config.js +20 -11
- package/dist/internal/codegen/environments.js +25 -21
- package/dist/internal/codegen/manifest.d.ts +1 -1
- package/dist/internal/codegen/manifest.js +7 -8
- package/dist/internal/codegen/maps.d.ts +1 -1
- package/dist/internal/codegen/maps.js +35 -28
- package/dist/internal/codegen/utils.d.ts +30 -0
- package/dist/internal/codegen/utils.js +165 -1
- package/dist/internal/env/browser.js +20 -16
- package/dist/internal/env/request-context.d.ts +2 -2
- package/dist/internal/env/request-context.js +1 -1
- package/dist/internal/env/rsc.d.ts +8 -22
- package/dist/internal/env/rsc.js +38 -117
- package/dist/internal/env/ssr.js +9 -9
- package/dist/internal/env/utils.js +2 -2
- package/dist/internal/metadata.d.ts +2 -2
- package/dist/internal/metadata.js +18 -6
- package/dist/internal/navigation/http-exception-boundary.d.ts +2 -2
- package/dist/internal/navigation/http-exception-boundary.js +1 -1
- package/dist/internal/navigation/link.js +1 -1
- package/dist/internal/navigation/redirect-boundary.d.ts +1 -1
- package/dist/internal/navigation/redirect-boundary.js +1 -1
- package/dist/internal/navigation/redirect.js +1 -1
- package/dist/internal/navigation/use-search-params.d.ts +11 -1
- package/dist/internal/navigation/use-search-params.js +24 -3
- package/dist/internal/prerender.d.ts +10 -1
- package/dist/internal/prerender.js +55 -5
- package/dist/internal/render/head.d.ts +4 -1
- package/dist/internal/render/head.js +37 -18
- package/dist/internal/render/tree.d.ts +1 -1
- package/dist/internal/render/tree.js +3 -3
- package/dist/internal/router/create-router.d.ts +2 -2
- package/dist/internal/router/create-router.js +1 -1
- package/dist/internal/router/prefetcher.d.ts +1 -1
- package/dist/internal/router/prefetcher.js +8 -3
- package/dist/internal/router/resolver.d.ts +29 -29
- package/dist/internal/router/resolver.js +4 -4
- package/dist/internal/router/router-context.d.ts +4 -0
- package/dist/internal/router/router-context.js +1 -0
- package/dist/internal/router/router-provider.d.ts +6 -2
- package/dist/internal/router/router-provider.js +38 -22
- package/dist/internal/router/router.d.ts +1 -1
- package/dist/internal/router/router.js +4 -4
- package/dist/internal/router/use-router.d.ts +5 -1
- package/dist/internal/router/use-router.js +1 -1
- package/dist/internal/router/utils.d.ts +1 -1
- package/dist/internal/server/actions.d.ts +30 -0
- package/dist/internal/server/actions.js +107 -0
- package/dist/internal/server/cookies.d.ts +1 -1
- package/dist/internal/server/cookies.js +3 -3
- package/dist/internal/server/dynamic.js +2 -2
- package/dist/internal/server/headers.js +2 -2
- package/dist/internal/server/url.js +14 -3
- package/dist/internal/ui/defaults/error.d.ts +1 -1
- package/dist/internal/ui/error-boundary.d.ts +1 -1
- package/dist/internal/ui/error-boundary.js +1 -1
- package/dist/navigation.d.ts +6 -6
- package/dist/navigation.js +6 -6
- package/dist/prerender.d.ts +1 -1
- package/dist/prerender.js +1 -1
- package/dist/router.d.ts +4 -4
- package/dist/router.js +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +4 -4
- package/dist/solas.d.ts +1 -1
- package/dist/solas.js +1 -0
- package/dist/types.d.ts +6 -6
- package/dist/types.js +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/logger.js +2 -2
- package/package.json +3 -1
- package/dist/utils/format.d.ts +0 -6
- package/dist/utils/format.js +0 -72
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 - 2026-04-07
|
|
4
|
+
|
|
5
|
+
- Fixed `useSearchParams()` client builds.
|
|
6
|
+
- Reworked the code generators to keep the source templates readable while still emitting tidy generated files.
|
|
7
|
+
- Added a shared template dedent helper for generated source and tightened nested object and route map indentation.
|
|
8
|
+
- Made generated config output emit logger code only when a logger level is configured.
|
|
9
|
+
|
|
10
|
+
## 0.3.0 - 2026-04-07
|
|
11
|
+
|
|
12
|
+
- Fixed `useSearchParams()` hydration so query-driven ui uses the initial request url on first render.
|
|
13
|
+
- Switched internal runtime and generated imports to explicit `.js` specifiers, and corrected the router action import path.
|
|
14
|
+
- Simplified generated config, manifest, and route map output to emit source literals directly.
|
|
15
|
+
- Removed the generated-file formatting pass and deleted the internal `Format` helper.
|
|
16
|
+
- Documented that the Solas cli currently requires Bun 1.2+ on `PATH`.
|
|
17
|
+
|
|
18
|
+
## 0.2.3 - 2026-04-02
|
|
19
|
+
|
|
20
|
+
- Previous release.
|
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Solas is a minimal React meta-framework powered by Vite, created for experimenti
|
|
|
4
4
|
|
|
5
5
|
It has not been rigorously tested yet (there are currently no automated tests) ... and broken behaviour should be expected.
|
|
6
6
|
|
|
7
|
+
Solas currently requires Bun 1.2+ on your `PATH`. You can still manage dependencies with `npm`, `pnpm`, or `yarn`, but the Solas CLI and Vite plugin runtime use Bun APIs directly.
|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
```sh
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { Solas } from './solas';
|
|
5
|
-
import { Compress } from './utils/compress';
|
|
6
|
-
import { Logger } from './utils/logger';
|
|
7
|
-
import { Prerender } from './internal/prerender';
|
|
4
|
+
import { Solas } from './solas.js';
|
|
5
|
+
import { Compress } from './utils/compress.js';
|
|
6
|
+
import { Logger } from './utils/logger.js';
|
|
7
|
+
import { Prerender } from './internal/prerender.js';
|
|
8
8
|
const logger = new Logger();
|
|
9
9
|
const DEFAULT_PREVIEW_PORT = 4173;
|
|
10
10
|
/**
|
|
@@ -52,10 +52,21 @@ async function build() {
|
|
|
52
52
|
const concurrency = Prerender.Build.getConcurrency();
|
|
53
53
|
// track the extra prerender files we write for preview
|
|
54
54
|
const artifactManifestRoutes = {};
|
|
55
|
+
// keep in-flight artifact writes bounded so result handling does not block on one route at a time
|
|
56
|
+
const pendingWrites = new Set();
|
|
55
57
|
logger.info('[prerender]', `prerendering ${manifest.prerenderRoutes.length} routes (timeout: ${timeout}ms, concurrency: ${concurrency})...`);
|
|
56
58
|
// load the built server entry and render each prerendered route through it
|
|
57
59
|
const rscEntry = path.join(rscDir, 'index.js');
|
|
58
60
|
const { default: app } = await import(/* @vite-ignore */ rscEntry);
|
|
61
|
+
async function enqueueWrite(task) {
|
|
62
|
+
const write = task().finally(() => {
|
|
63
|
+
pendingWrites.delete(write);
|
|
64
|
+
});
|
|
65
|
+
pendingWrites.add(write);
|
|
66
|
+
if (pendingWrites.size >= concurrency) {
|
|
67
|
+
await Promise.race(pendingWrites);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
59
70
|
// run prerender through the built app so build output uses the same path as preview
|
|
60
71
|
for await (const result of Prerender.Build.run(app, manifest.prerenderRoutes, {
|
|
61
72
|
timeout,
|
|
@@ -63,98 +74,81 @@ async function build() {
|
|
|
63
74
|
origin: manifest.url,
|
|
64
75
|
})) {
|
|
65
76
|
const route = result.route;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
if ('error' in result) {
|
|
78
|
+
logger.error(`[prerender]: Failed ${route}: ${result.error}. This often means unresolved async work (for example external fetches or dynamic rendering in full mode)`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if ('status' in result) {
|
|
82
|
+
logger.warn(`[prerender]: Skipped ${route}: ${result.status}`);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const artifact = result.artifact;
|
|
86
|
+
const artifactDir = Prerender.Artifact.getPath(outDir, route);
|
|
87
|
+
await enqueueWrite(async () => {
|
|
88
|
+
try {
|
|
89
|
+
if (artifact.mode === 'ppr') {
|
|
90
|
+
// for ppr save the shell now and keep the postponed state for later
|
|
91
|
+
await fs.mkdir(artifactDir, { recursive: true });
|
|
92
|
+
const writes = [
|
|
93
|
+
Bun.write(path.join(artifactDir, 'prelude.html'), artifact.html),
|
|
94
|
+
Bun.write(path.join(artifactDir, 'metadata.json'), JSON.stringify({
|
|
95
|
+
schema: artifact.schema,
|
|
96
|
+
route: artifact.route,
|
|
97
|
+
createdAt: artifact.createdAt,
|
|
98
|
+
mode: artifact.mode,
|
|
99
|
+
})),
|
|
100
|
+
];
|
|
101
|
+
if (artifact.postponed !== undefined) {
|
|
102
|
+
writes.push(Bun.write(path.join(artifactDir, 'postponed.json'), JSON.stringify(artifact.postponed)));
|
|
103
|
+
}
|
|
104
|
+
await Promise.all(writes);
|
|
105
|
+
artifactManifestRoutes[route] = {
|
|
106
|
+
mode: artifact.mode,
|
|
107
|
+
createdAt: artifact.createdAt,
|
|
108
|
+
files: artifact.postponed !== undefined
|
|
109
|
+
? ['metadata', 'prelude', 'postponed']
|
|
110
|
+
: ['metadata', 'prelude'],
|
|
111
|
+
};
|
|
112
|
+
logger.info('[prerender:artifacts]', JSON.stringify({
|
|
113
|
+
route,
|
|
114
|
+
prelude: artifact.html,
|
|
115
|
+
postponed: artifact.postponed ?? null,
|
|
116
|
+
metadata: {
|
|
117
|
+
schema: artifact.schema,
|
|
118
|
+
route: artifact.route,
|
|
119
|
+
createdAt: artifact.createdAt,
|
|
120
|
+
mode: artifact.mode,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
logger.info('[prerender]', `${route} (ppr)`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// full prerender still keeps metadata so preview knows to serve saved html
|
|
78
127
|
await fs.mkdir(artifactDir, { recursive: true });
|
|
79
|
-
const
|
|
80
|
-
|
|
128
|
+
const fullPrerenderFilename = Prerender.Artifact.getFullHtmlFileName(artifact.html);
|
|
129
|
+
await Promise.all([
|
|
81
130
|
Bun.write(path.join(artifactDir, 'metadata.json'), JSON.stringify({
|
|
82
131
|
schema: artifact.schema,
|
|
83
132
|
route: artifact.route,
|
|
84
133
|
createdAt: artifact.createdAt,
|
|
85
134
|
mode: artifact.mode,
|
|
86
135
|
})),
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
writes.push(Bun.write(path.join(artifactDir, 'postponed.json'), JSON.stringify(artifact.postponed)));
|
|
90
|
-
}
|
|
91
|
-
await Promise.all(writes);
|
|
136
|
+
Bun.write(Prerender.Artifact.getFilePath(outDir, route, fullPrerenderFilename), artifact.html),
|
|
137
|
+
]);
|
|
92
138
|
artifactManifestRoutes[route] = {
|
|
93
139
|
mode: artifact.mode,
|
|
94
140
|
createdAt: artifact.createdAt,
|
|
95
|
-
files:
|
|
96
|
-
|
|
97
|
-
: ['metadata', 'prelude'],
|
|
141
|
+
files: ['metadata', 'html'],
|
|
142
|
+
fullPrerenderFilename,
|
|
98
143
|
};
|
|
99
|
-
logger.info(
|
|
100
|
-
route,
|
|
101
|
-
prelude: artifact.html,
|
|
102
|
-
postponed: artifact.postponed ?? null,
|
|
103
|
-
metadata: {
|
|
104
|
-
schema: artifact.schema,
|
|
105
|
-
route: artifact.route,
|
|
106
|
-
createdAt: artifact.createdAt,
|
|
107
|
-
mode: artifact.mode,
|
|
108
|
-
},
|
|
109
|
-
}));
|
|
110
|
-
logger.info('[prerender]', `${route} (ppr)`);
|
|
111
|
-
continue;
|
|
144
|
+
logger.info(`[prerender]: ${route} (full)`);
|
|
112
145
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
await fs.mkdir(artifactDir, { recursive: true });
|
|
116
|
-
await Bun.write(path.join(artifactDir, 'metadata.json'), JSON.stringify({
|
|
117
|
-
schema: artifact.schema,
|
|
118
|
-
route: artifact.route,
|
|
119
|
-
createdAt: artifact.createdAt,
|
|
120
|
-
mode: artifact.mode,
|
|
121
|
-
}));
|
|
122
|
-
const routePath = route.replace(/^\//, '').replace(/\/$/, '');
|
|
123
|
-
const outPath = route === '/'
|
|
124
|
-
? path.join(outDir, 'index.html')
|
|
125
|
-
: manifest.trailingSlash === 'always'
|
|
126
|
-
? path.join(outDir, routePath, 'index.html')
|
|
127
|
-
: path.join(outDir, `${routePath}.html`);
|
|
128
|
-
// remove the old file shape for this route so switching trailingSlash mode does not leave
|
|
129
|
-
// both variants behind. we have to do this before writing the new file so that if the
|
|
130
|
-
// route shape changes, we still remove the old one instead of leaving in the output
|
|
131
|
-
const alternateOutPath = route === '/'
|
|
132
|
-
? null
|
|
133
|
-
: manifest.trailingSlash === 'always'
|
|
134
|
-
? path.join(outDir, `${routePath}.html`)
|
|
135
|
-
: path.join(outDir, routePath, 'index.html');
|
|
136
|
-
if (alternateOutPath) {
|
|
137
|
-
// remove the old file shape so switching trailingSlash mode
|
|
138
|
-
// does not leave both variants behind
|
|
139
|
-
await Promise.all([
|
|
140
|
-
fs.rm(alternateOutPath, { force: true }),
|
|
141
|
-
fs.rm(`${alternateOutPath}.br`, { force: true }),
|
|
142
|
-
]);
|
|
143
|
-
await fs.rmdir(path.dirname(alternateOutPath)).catch(() => { });
|
|
146
|
+
catch (err) {
|
|
147
|
+
logger.error(`[prerender]: Failed ${route}: ${err}. This often means unresolved async work (for example external fetches or dynamic rendering in full mode).`);
|
|
144
148
|
}
|
|
145
|
-
|
|
146
|
-
await Bun.write(outPath, artifact.html);
|
|
147
|
-
artifactManifestRoutes[route] = {
|
|
148
|
-
mode: artifact.mode,
|
|
149
|
-
createdAt: artifact.createdAt,
|
|
150
|
-
files: ['metadata', 'html'],
|
|
151
|
-
};
|
|
152
|
-
logger.info('[prerender]', `${route} (full)`);
|
|
153
|
-
}
|
|
154
|
-
catch (err) {
|
|
155
|
-
logger.error('[prerender]', `failed ${route}: ${err}. This often means unresolved async work (for example external fetches or dynamic rendering in full mode).`);
|
|
156
|
-
}
|
|
149
|
+
});
|
|
157
150
|
}
|
|
151
|
+
await Promise.all(pendingWrites);
|
|
158
152
|
// write one manifest for the saved prerender files after all routes finish
|
|
159
153
|
await fs.mkdir(artifactRoot, { recursive: true });
|
|
160
154
|
await Bun.write(Prerender.Artifact.getManifestPath(outDir), JSON.stringify({
|
|
@@ -224,7 +218,7 @@ async function preview() {
|
|
|
224
218
|
await fs.access(rscEntry);
|
|
225
219
|
}
|
|
226
220
|
catch (err) {
|
|
227
|
-
logger.error(`[preview] missing ${path.relative(cwd, rscEntry)} - run \`${Solas.Config.SLUG} build\` first`, err);
|
|
221
|
+
logger.error(`[preview] missing ${path.relative(cwd, rscEntry)} - run \`${Solas.Config.SLUG} build\` from this project directory first`, err);
|
|
228
222
|
process.exit(1);
|
|
229
223
|
}
|
|
230
224
|
const { default: app } = await import(/* @vite-ignore */ rscEntry);
|
package/dist/error-boundary.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ErrorBoundary } from './internal/ui/error-boundary';
|
|
1
|
+
export { ErrorBoundary } from './internal/ui/error-boundary.js';
|
package/dist/error-boundary.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ErrorBoundary } from './internal/ui/error-boundary';
|
|
1
|
+
export { ErrorBoundary } from './internal/ui/error-boundary.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginOption } from 'vite';
|
|
2
|
-
import type { PluginConfig } from './types';
|
|
2
|
+
import type { PluginConfig } from './types.js';
|
|
3
3
|
declare function solas(c: PluginConfig): PluginOption[];
|
|
4
4
|
export default solas;
|
|
5
|
-
export { Solas } from './solas';
|
|
6
5
|
export type * from './solas.d.ts';
|
|
7
|
-
export
|
|
6
|
+
export { Solas } from './solas.js';
|
|
7
|
+
export type * from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -2,16 +2,15 @@ import fsSync from 'node:fs';
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import rsc from '@vitejs/plugin-rsc';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { writeMaps } from './internal/codegen/maps';
|
|
5
|
+
import { ExportReader } from './utils/export-reader.js';
|
|
6
|
+
import { Logger } from './utils/logger.js';
|
|
7
|
+
import { Time } from './utils/time.js';
|
|
8
|
+
import { Build } from './internal/build.js';
|
|
9
|
+
import { writeConfig } from './internal/codegen/config.js';
|
|
10
|
+
import { writeBrowserEntry, writeRSCEntry, writeSSREntry, } from './internal/codegen/environments.js';
|
|
11
|
+
import { writeManifest } from './internal/codegen/manifest.js';
|
|
12
|
+
import { writeMaps } from './internal/codegen/maps.js';
|
|
13
|
+
import { Solas } from './solas.js';
|
|
15
14
|
const DEFAULT_CONFIG = {
|
|
16
15
|
precompress: true,
|
|
17
16
|
prerender: false,
|
|
@@ -104,9 +103,6 @@ function solas(c) {
|
|
|
104
103
|
// early return if nothing has changed
|
|
105
104
|
if (!changed.length)
|
|
106
105
|
return;
|
|
107
|
-
await Promise.all(changed.map(filePath => Format.run(filePath).catch(err => {
|
|
108
|
-
logger.error(`[build] Failed to format file: ${filePath}`, err);
|
|
109
|
-
})));
|
|
110
106
|
return changed;
|
|
111
107
|
}
|
|
112
108
|
let rebuildRunning = false;
|
|
@@ -248,4 +244,4 @@ function solas(c) {
|
|
|
248
244
|
];
|
|
249
245
|
}
|
|
250
246
|
export default solas;
|
|
251
|
-
export { Solas } from './solas';
|
|
247
|
+
export { Solas } from './solas.js';
|
package/dist/internal/build.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BuildContext, Endpoint, PluginConfig, Segment } from '../types';
|
|
1
|
+
import type { BuildContext, Endpoint, PluginConfig, Segment } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Types, constants, and the Finder class for route discovery and manifest generation.
|
|
4
4
|
* The Finder walks the app directory, builds inheritance chains, and transforms
|
package/dist/internal/build.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { Solas } from '../solas';
|
|
4
|
-
import { Logger } from '../utils/logger';
|
|
5
|
-
import { normalisePathname } from './router/utils';
|
|
6
|
-
import { Prerender } from './prerender';
|
|
3
|
+
import { Solas } from '../solas.js';
|
|
4
|
+
import { Logger } from '../utils/logger.js';
|
|
5
|
+
import { normalisePathname } from './router/utils.js';
|
|
6
|
+
import { Prerender } from './prerender.js';
|
|
7
7
|
export { Build };
|
|
8
8
|
/**
|
|
9
9
|
* Types, constants, and the Finder class for route discovery and manifest generation.
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import { Solas } from '../../solas';
|
|
2
|
-
import { AUTOGEN_MSG } from './utils';
|
|
1
|
+
import { Solas } from '../../solas.js';
|
|
2
|
+
import { AUTOGEN_MSG, source, toSourceLiteral } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates the code to create an exported config object
|
|
5
5
|
*/
|
|
6
6
|
export function writeConfig(config) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const loggerLevel = config.logger?.level;
|
|
8
|
+
const importLines = [
|
|
9
|
+
`import type { PluginConfig } from '${Solas.Config.PKG_NAME}'`,
|
|
10
|
+
loggerLevel ? `import { Logger } from '${Solas.Config.PKG_NAME}/utils/logger'` : '',
|
|
11
|
+
]
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.join('\n');
|
|
14
|
+
const configStatement = `const config = ${toSourceLiteral(config)} as const satisfies PluginConfig`;
|
|
15
|
+
const loggerStatement = loggerLevel
|
|
16
|
+
? `Logger.defaultLevel = ${toSourceLiteral(loggerLevel)}`
|
|
17
|
+
: '';
|
|
18
|
+
return source `
|
|
19
|
+
${AUTOGEN_MSG}
|
|
9
20
|
|
|
10
|
-
|
|
11
|
-
import { Logger } from '${Solas.Config.PKG_NAME}/utils/logger'
|
|
21
|
+
${importLines}
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
${configStatement}
|
|
24
|
+
${loggerStatement}
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export { config }
|
|
18
|
-
`.trim();
|
|
26
|
+
export { config }
|
|
27
|
+
`;
|
|
19
28
|
}
|
|
@@ -1,42 +1,46 @@
|
|
|
1
|
-
import { Solas } from '../../solas';
|
|
2
|
-
import { AUTOGEN_MSG } from './utils';
|
|
1
|
+
import { Solas } from '../../solas.js';
|
|
2
|
+
import { AUTOGEN_MSG, source } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates the RSC entry code
|
|
5
5
|
*/
|
|
6
6
|
export function writeRSCEntry() {
|
|
7
|
-
return `
|
|
8
|
-
|
|
7
|
+
return source `
|
|
8
|
+
${AUTOGEN_MSG}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
import { createHandler } from '${Solas.Config.PKG_NAME}/env/rsc'
|
|
11
|
+
import { Prerender } from '${Solas.Config.PKG_NAME}/prerender'
|
|
12
|
+
import { Solas } from '${Solas.Config.PKG_NAME}'
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
import { manifest } from './manifest.js'
|
|
15
|
+
import { importMap } from './maps.js'
|
|
16
|
+
import { config } from './config.js'
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
const artifactManifest = await Prerender.Artifact.loadManifest(Solas.Config.OUT_DIR)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
export default createHandler(config, manifest, importMap, artifactManifest)
|
|
21
|
+
|
|
22
|
+
import.meta.hot?.accept()
|
|
23
|
+
`;
|
|
20
24
|
}
|
|
21
25
|
/**
|
|
22
26
|
* Generates the SSR entry code
|
|
23
27
|
*/
|
|
24
28
|
export function writeSSREntry() {
|
|
25
|
-
return `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
return source `
|
|
30
|
+
${AUTOGEN_MSG}
|
|
31
|
+
|
|
32
|
+
export { prerender, resume, ssr } from '${Solas.Config.PKG_NAME}/env/ssr'
|
|
33
|
+
`;
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
36
|
* Generates the browser entry code
|
|
33
37
|
*/
|
|
34
38
|
export function writeBrowserEntry() {
|
|
35
|
-
return `
|
|
36
|
-
|
|
39
|
+
return source `
|
|
40
|
+
${AUTOGEN_MSG}
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
import { browser } from '${Solas.Config.PKG_NAME}/env/browser'
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
browser()
|
|
45
|
+
`;
|
|
42
46
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { Solas } from '../../solas';
|
|
2
|
-
import { AUTOGEN_MSG } from './utils';
|
|
1
|
+
import { Solas } from '../../solas.js';
|
|
2
|
+
import { AUTOGEN_MSG, source, toSourceLiteral } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates the code to create an exported manifest object
|
|
5
5
|
*/
|
|
6
6
|
export function writeManifest(manifest) {
|
|
7
|
-
return `
|
|
8
|
-
|
|
7
|
+
return source `
|
|
8
|
+
${AUTOGEN_MSG}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
import type { Manifest } from '${Solas.Config.PKG_NAME}'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`.trim();
|
|
12
|
+
export const manifest = ${toSourceLiteral(manifest)} as const satisfies Manifest
|
|
13
|
+
`;
|
|
15
14
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Solas } from '../../solas';
|
|
2
|
-
import { AUTOGEN_MSG } from './utils';
|
|
1
|
+
import { Solas } from '../../solas.js';
|
|
2
|
+
import { AUTOGEN_MSG, indent, source, toIdentifier, toIdentifierList, toRelativeModuleSpecifier, toStringLiteral, } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates the import map for all route components, endpoints, layouts, shells, and middlewares
|
|
5
5
|
*/
|
|
@@ -7,69 +7,76 @@ export function writeMaps(imports, modules) {
|
|
|
7
7
|
const statics = [
|
|
8
8
|
...imports.endpoints.static.entries().map(([k, v]) => {
|
|
9
9
|
const [, method] = k.split('_');
|
|
10
|
-
return `import { ${method.toUpperCase()} as ${k}} from ${
|
|
10
|
+
return `import { ${toIdentifier(method.toUpperCase(), `endpoint export for ${k}`)} as ${toIdentifier(k, `endpoint alias for ${v}`)} } from ${toRelativeModuleSpecifier(v, `endpoint import for ${k}`)}`;
|
|
11
11
|
}),
|
|
12
12
|
...imports.components.static
|
|
13
13
|
.entries()
|
|
14
|
-
.map(([k, v]) => `import * as ${k} from ${
|
|
14
|
+
.map(([k, v]) => `import * as ${toIdentifier(k, `component alias for ${v}`)} from ${toRelativeModuleSpecifier(v, `component import for ${k}`)}`),
|
|
15
15
|
...imports.middlewares.static
|
|
16
16
|
.entries()
|
|
17
|
-
.map(([k, v]) => `import { middleware as ${k} } from ${
|
|
17
|
+
.map(([k, v]) => `import { middleware as ${toIdentifier(k, `middleware alias for ${v}`)} } from ${toRelativeModuleSpecifier(v, `middleware import for ${k}`)}`),
|
|
18
18
|
];
|
|
19
19
|
const dynamics = [
|
|
20
20
|
...imports.components.dynamic
|
|
21
21
|
.entries()
|
|
22
|
-
.map(([k, v]) => `export const ${k} = () => import(${
|
|
22
|
+
.map(([k, v]) => `export const ${toIdentifier(k, `dynamic component alias for ${v}`)} = () => import(${toRelativeModuleSpecifier(v, `dynamic import for ${k}`)})`),
|
|
23
23
|
];
|
|
24
|
-
const map = Object.entries(modules).map(([
|
|
24
|
+
const map = Object.entries(modules).map(([moduleId, m]) => {
|
|
25
25
|
const parts = [];
|
|
26
|
-
if (m.shellId)
|
|
27
|
-
parts.push(`shell: ${m.shellId}`);
|
|
26
|
+
if (m.shellId) {
|
|
27
|
+
parts.push(`shell: ${toIdentifier(m.shellId, `shell id for ${moduleId}`)}`);
|
|
28
|
+
}
|
|
28
29
|
if (m.layoutIds?.length) {
|
|
29
|
-
const layouts = m.layoutIds
|
|
30
|
+
const layouts = toIdentifierList(m.layoutIds, `layouts for ${moduleId}`);
|
|
30
31
|
parts.push(`layouts: [${layouts}]`);
|
|
31
32
|
}
|
|
32
|
-
if (m.pageId)
|
|
33
|
-
parts.push(`page: ${m.pageId}`);
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (m.pageId) {
|
|
34
|
+
parts.push(`page: ${toIdentifier(m.pageId, `page id for ${moduleId}`)}`);
|
|
35
|
+
}
|
|
36
|
+
if (m.endpointId) {
|
|
37
|
+
parts.push(`endpoint: ${toIdentifier(m.endpointId, `endpoint id for ${moduleId}`)}`);
|
|
38
|
+
}
|
|
36
39
|
if (m['401Ids']?.length) {
|
|
37
|
-
const unauthorized = m['401Ids']
|
|
40
|
+
const unauthorized = toIdentifierList(m['401Ids'], `401s for ${moduleId}`);
|
|
38
41
|
parts.push(`'401s': [${unauthorized}]`);
|
|
39
42
|
}
|
|
40
43
|
if (m['403Ids']?.length) {
|
|
41
|
-
const forbidden = m['403Ids']
|
|
44
|
+
const forbidden = toIdentifierList(m['403Ids'], `403s for ${moduleId}`);
|
|
42
45
|
parts.push(`'403s': [${forbidden}]`);
|
|
43
46
|
}
|
|
44
47
|
if (m['404Ids']?.length) {
|
|
45
|
-
const notFounds = m['404Ids']
|
|
48
|
+
const notFounds = toIdentifierList(m['404Ids'], `404s for ${moduleId}`);
|
|
46
49
|
parts.push(`'404s': [${notFounds}]`);
|
|
47
50
|
}
|
|
48
51
|
if (m['500Ids']?.length) {
|
|
49
|
-
const serverErrors = m['500Ids']
|
|
52
|
+
const serverErrors = toIdentifierList(m['500Ids'], `500s for ${moduleId}`);
|
|
50
53
|
parts.push(`'500s': [${serverErrors}]`);
|
|
51
54
|
}
|
|
52
55
|
if (m.loadingIds?.length) {
|
|
53
|
-
const loaders = m.loadingIds
|
|
56
|
+
const loaders = toIdentifierList(m.loadingIds, `loaders for ${moduleId}`);
|
|
54
57
|
parts.push(`loaders: [${loaders}]`);
|
|
55
58
|
}
|
|
56
59
|
if (m.middlewareIds?.length) {
|
|
57
|
-
const middleware = m.middlewareIds
|
|
60
|
+
const middleware = toIdentifierList(m.middlewareIds, `middlewares for ${moduleId}`);
|
|
58
61
|
parts.push(`middlewares: [${middleware}]`);
|
|
59
62
|
}
|
|
60
|
-
|
|
63
|
+
if (parts.length === 0)
|
|
64
|
+
return `${toStringLiteral(moduleId)}: {}`;
|
|
65
|
+
return `${toStringLiteral(moduleId)}: {\n${parts.map(part => indent(part, 1)).join(',\n')}\n}`;
|
|
61
66
|
});
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const importLines = [...statics, ...dynamics].join('\n');
|
|
68
|
+
const entries = map.map(entry => indent(entry, 1)).join(',\n');
|
|
69
|
+
return source `
|
|
70
|
+
${AUTOGEN_MSG}
|
|
64
71
|
|
|
65
72
|
import type { ImportMap } from '${Solas.Config.PKG_NAME}'
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
${importLines
|
|
74
|
+
? `
|
|
75
|
+
${importLines}`
|
|
76
|
+
: ''}
|
|
70
77
|
|
|
71
78
|
export const importMap = {
|
|
72
|
-
|
|
79
|
+
${entries}
|
|
73
80
|
} as const satisfies ImportMap
|
|
74
81
|
`;
|
|
75
82
|
}
|
|
@@ -1 +1,31 @@
|
|
|
1
1
|
export declare const AUTOGEN_MSG = "// auto-generated by Solas";
|
|
2
|
+
/**
|
|
3
|
+
* Validate an identifier before writing it into generated source unquoted
|
|
4
|
+
*/
|
|
5
|
+
export declare function toIdentifier(value: string, label: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Validate and quote a relative specifier before embedding it in generated imports
|
|
8
|
+
*/
|
|
9
|
+
export declare function toRelativeModuleSpecifier(value: string, label: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Validate a nullable identifier list whilst preserving explicit null holes
|
|
12
|
+
*/
|
|
13
|
+
export declare function toIdentifierList(values: readonly (string | null)[], label: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Escape text into a safe string literal for generated source
|
|
16
|
+
*/
|
|
17
|
+
export declare function toStringLiteral(value: string, quoteStyle?: "'" | '"'): string;
|
|
18
|
+
/**
|
|
19
|
+
* Dedent an interpolated template literal while preserving indentation for
|
|
20
|
+
* multiline substitutions
|
|
21
|
+
*/
|
|
22
|
+
export declare function source(strings: TemplateStringsArray, ...values: Array<string | number | boolean | false | null | undefined>): string;
|
|
23
|
+
/**
|
|
24
|
+
* Emit readable ts source for generated config and manifest data
|
|
25
|
+
*/
|
|
26
|
+
export declare function toSourceLiteral(value: unknown, level?: number): string;
|
|
27
|
+
/**
|
|
28
|
+
* Indent each line of a block of source code by the specified level for embedding in
|
|
29
|
+
* generated output
|
|
30
|
+
*/
|
|
31
|
+
export declare function indent(value: string, level?: number): string;
|