@jk2908/solas 0.5.4 → 0.6.0

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 (66) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +81 -85
  3. package/dist/config.d.ts +25 -0
  4. package/dist/config.js +152 -0
  5. package/dist/events.d.ts +4 -0
  6. package/dist/events.js +5 -0
  7. package/dist/index.d.ts +1 -2
  8. package/dist/index.js +59 -37
  9. package/dist/internal/boundary-error.d.ts +4 -0
  10. package/dist/internal/boundary-error.js +8 -0
  11. package/dist/internal/browser-router/history.d.ts +1 -1
  12. package/dist/internal/browser-router/link.d.ts +1 -1
  13. package/dist/internal/browser-router/link.js +1 -1
  14. package/dist/internal/browser-router/router.d.ts +2 -2
  15. package/dist/internal/browser-router/router.js +5 -5
  16. package/dist/internal/browser-router/shared.d.ts +182 -184
  17. package/dist/internal/browser-router/shared.js +49 -56
  18. package/dist/internal/browser-router/use-router.d.ts +1 -1
  19. package/dist/internal/browser-router/use-search-params.js +3 -3
  20. package/dist/internal/build.d.ts +108 -110
  21. package/dist/internal/build.js +569 -581
  22. package/dist/internal/codegen/config.js +3 -3
  23. package/dist/internal/codegen/environments.js +5 -6
  24. package/dist/internal/codegen/format.d.ts +6 -0
  25. package/dist/internal/codegen/format.js +24 -0
  26. package/dist/internal/codegen/manifest.js +2 -2
  27. package/dist/internal/codegen/maps.d.ts +1 -1
  28. package/dist/internal/codegen/maps.js +2 -2
  29. package/dist/internal/codegen/types.js +4 -4
  30. package/dist/internal/codegen/utils.js +2 -2
  31. package/dist/internal/env/request-context.js +1 -1
  32. package/dist/internal/env/rsc.d.ts +3 -2
  33. package/dist/internal/env/rsc.js +26 -25
  34. package/dist/internal/env/ssr.js +4 -4
  35. package/dist/internal/http-router/router.js +8 -8
  36. package/dist/internal/metadata.d.ts +1 -1
  37. package/dist/internal/metadata.js +1 -1
  38. package/dist/internal/navigation/http-exception-boundary.d.ts +1 -1
  39. package/dist/internal/navigation/redirect-boundary.d.ts +1 -1
  40. package/dist/internal/navigation/redirect.js +6 -6
  41. package/dist/internal/postbuild.js +18 -17
  42. package/dist/internal/prerender.d.ts +130 -138
  43. package/dist/internal/prerender.js +326 -354
  44. package/dist/internal/public-files.js +2 -4
  45. package/dist/internal/render/head.js +2 -2
  46. package/dist/internal/resolver.js +1 -1
  47. package/dist/internal/server/actions.js +2 -2
  48. package/dist/internal/ui/error-boundary.d.ts +1 -1
  49. package/dist/manifest.d.ts +7 -0
  50. package/dist/manifest.js +85 -0
  51. package/dist/prerender.d.ts +1 -1
  52. package/dist/prerender.js +1 -1
  53. package/dist/solas.d.ts +2 -46
  54. package/dist/solas.js +6 -266
  55. package/dist/types.d.ts +14 -21
  56. package/dist/types.js +1 -1
  57. package/dist/utils/base-path.d.ts +12 -14
  58. package/dist/utils/base-path.js +75 -82
  59. package/dist/utils/compress.d.ts +9 -11
  60. package/dist/utils/compress.js +59 -64
  61. package/dist/utils/context.d.ts +4 -6
  62. package/dist/utils/context.js +17 -22
  63. package/dist/utils/logger.js +2 -2
  64. package/dist/utils/time.d.ts +2 -4
  65. package/dist/utils/time.js +23 -29
  66. package/package.json +18 -8
package/dist/index.js CHANGED
@@ -5,10 +5,12 @@ import { createServer, loadConfigFromFile, } from 'vite';
5
5
  import rsc from '@vitejs/plugin-rsc';
6
6
  import { ExportReader } from './utils/export-reader.js';
7
7
  import { Logger } from './utils/logger.js';
8
- import { Time } from './utils/time.js';
9
- import { Build } from './internal/build.js';
8
+ import { debounce } from './utils/time.js';
9
+ import * as Config from './config.js';
10
+ import * as Build from './internal/build.js';
10
11
  import { writeConfig } from './internal/codegen/config.js';
11
12
  import { writeBrowserEntry, writeRSCEntry, writeSSREntry, } from './internal/codegen/environments.js';
13
+ import { format as formatSource } from './internal/codegen/format.js';
12
14
  import { writeManifest } from './internal/codegen/manifest.js';
13
15
  import { writeMaps } from './internal/codegen/maps.js';
14
16
  import { writeTypes } from './internal/codegen/types.js';
@@ -16,7 +18,6 @@ import { postbuild } from './internal/postbuild.js';
16
18
  import { collect as collectPublicFiles } from './internal/public-files.js';
17
19
  import { createRuntime } from './internal/runtimes/create.js';
18
20
  import { Runtime } from './internal/runtimes/runtime.js';
19
- import { Solas } from './solas.js';
20
21
  const DEFAULT_CONFIG = {
21
22
  runtime: 'auto',
22
23
  precompress: false,
@@ -25,15 +26,14 @@ const DEFAULT_CONFIG = {
25
26
  trailingSlash: 'never',
26
27
  };
27
28
  function solas(c) {
28
- const validatedConfig = Solas.Config.validate({
29
- ...c,
30
- url: c?.url ?? process.env.VITE_APP_URL?.toString(),
31
- });
29
+ const validatedConfig = Config.validate(c);
32
30
  const config = {
33
31
  ...DEFAULT_CONFIG,
34
32
  ...validatedConfig,
35
33
  runtime: validatedConfig.runtime ?? DEFAULT_CONFIG.runtime,
36
34
  };
35
+ const envUrl = process.env.VITE_APP_URL?.toString();
36
+ let resolvedUrl = c?.url ?? (envUrl && envUrl.length > 0 ? envUrl : undefined);
37
37
  Runtime.runtime = createRuntime(config.runtime);
38
38
  if (config.logger?.level)
39
39
  Logger.defaultLevel = config.logger.level;
@@ -93,8 +93,8 @@ function solas(c) {
93
93
  }
94
94
  async function build() {
95
95
  const cwd = process.cwd();
96
- const routesDir = path.join(cwd, Solas.Config.APP_DIR);
97
- const generatedDir = path.join(cwd, Solas.Config.GENERATED_DIR);
96
+ const routesDir = path.join(cwd, Config.APP_DIR);
97
+ const generatedDir = path.join(cwd, Config.GENERATED_DIR);
98
98
  await Promise.all([
99
99
  fs.mkdir(routesDir, { recursive: true }),
100
100
  fs.mkdir(generatedDir, { recursive: true }),
@@ -107,12 +107,27 @@ function solas(c) {
107
107
  ['config.ts', writeConfig(config)],
108
108
  ['manifest.ts', writeManifest(manifest)],
109
109
  ['maps.ts', writeMaps(imports, modules)],
110
- [`${Solas.Config.SLUG}.d.ts`, writeTypes(manifest)],
111
- [Solas.Config.ENTRY_RSC, writeRSCEntry(config)],
112
- [Solas.Config.ENTRY_SSR, writeSSREntry()],
113
- [Solas.Config.ENTRY_BROWSER, writeBrowserEntry()],
110
+ [`${Config.SLUG}.d.ts`, writeTypes(manifest)],
111
+ [Config.ENTRY_RSC, writeRSCEntry(config)],
112
+ [Config.ENTRY_SSR, writeSSREntry()],
113
+ [Config.ENTRY_BROWSER, writeBrowserEntry()],
114
114
  ];
115
- const writes = await Promise.all(files.map(([file, content]) => maybeWrite(path.join(generatedDir, file), content)));
115
+ // remove generated files no longer emitted by the current codegen, so
116
+ // old artifacts don't linger and break module resolution. keep
117
+ // closeBundle's build.json, which lives in the same directory
118
+ const currentFiles = new Set(files.map(([file]) => file));
119
+ const existing = await fs
120
+ .readdir(generatedDir, { withFileTypes: true })
121
+ .catch(() => []);
122
+ await Promise.all(existing
123
+ .filter(entry => entry.isFile() &&
124
+ entry.name !== 'build.json' &&
125
+ !currentFiles.has(entry.name))
126
+ .map(entry => fs.rm(path.join(generatedDir, entry.name), { force: true })));
127
+ const writes = await Promise.all(files.map(async ([file, content]) => {
128
+ const formatted = await formatSource(file, content);
129
+ return maybeWrite(path.join(generatedDir, file), formatted);
130
+ }));
116
131
  const changed = writes.filter(n => n !== null);
117
132
  // early return if nothing has changed
118
133
  if (!changed.length)
@@ -125,7 +140,7 @@ function solas(c) {
125
140
  // normalise all watcher paths to forward slashes so path checks behave the
126
141
  // same on Windows and POSIX
127
142
  const WATCH_CWD = process.cwd().replace(/\\/g, '/');
128
- const WATCH_APP_ROOT = `${WATCH_CWD}/${Solas.Config.APP_DIR}/`;
143
+ const WATCH_APP_ROOT = `${WATCH_CWD}/${Config.APP_DIR}/`;
129
144
  // convert watcher paths to a consistent slash format before comparing them
130
145
  const normaliseWatchPath = (p) => p.replace(/\\/g, '/');
131
146
  // resolve relative watcher paths against the project root so prefix checks are reliable
@@ -136,7 +151,7 @@ function solas(c) {
136
151
  // edits needing special treatment because verb exports can change in-place
137
152
  const routeFile = /\/\+(layout|page|401|403|404|500|loading|middleware|endpoint)\.(t|j)sx?$/;
138
153
  const endpointFile = /\/\+endpoint\.(t|j)sx?$/;
139
- const rebuild = Time.debounce((event, p) => {
154
+ const rebuild = debounce((event, p) => {
140
155
  const queue = () => {
141
156
  void (async () => {
142
157
  // collapse bursts of file events into one active rebuild plus a single
@@ -197,9 +212,7 @@ function solas(c) {
197
212
  return [];
198
213
  if (Array.isArray(plugin))
199
214
  return plugin.flatMap(flatten);
200
- if (typeof plugin === 'object' &&
201
- 'name' in plugin &&
202
- plugin.name === Solas.Config.NAME) {
215
+ if (typeof plugin === 'object' && 'name' in plugin && plugin.name === Config.NAME) {
203
216
  return [];
204
217
  }
205
218
  return [plugin];
@@ -219,26 +232,35 @@ function solas(c) {
219
232
  return utilityServer;
220
233
  }
221
234
  const plugin = {
222
- name: Solas.Config.NAME,
235
+ name: Config.NAME,
223
236
  async config(viteConfig) {
224
237
  const pkg = JSON.parse(fsSync.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
225
238
  if (typeof pkg.name !== 'string' || pkg.name.length === 0) {
226
- throw new Error(`Missing ${Solas.Config.NAME} package name`);
239
+ throw new Error(`Missing ${Config.NAME} package name`);
227
240
  }
228
241
  if (typeof pkg.version !== 'string' || pkg.version.length === 0) {
229
- throw new Error(`Missing ${Solas.Config.NAME} package version`);
242
+ throw new Error(`Missing ${Config.NAME} package version`);
230
243
  }
231
244
  viteConfig.build ??= {};
232
- viteConfig.build.outDir = Solas.Config.OUT_DIR;
245
+ viteConfig.build.outDir = Config.OUT_DIR;
233
246
  // keep framework files under one reserved url prefix
234
- viteConfig.build.assetsDir = Solas.Config.ASSETS_DIR;
247
+ viteConfig.build.assetsDir = Config.ASSETS_DIR;
235
248
  viteConfig.build.emptyOutDir = true;
236
249
  // let users move the source public folder if they want
237
- viteConfig.publicDir ??= Solas.Config.PUBLIC_DIR;
250
+ viteConfig.publicDir ??= Config.PUBLIC_DIR;
238
251
  viteConfig.server ??= {};
239
- viteConfig.server.port = config.port ?? viteConfig.server.port ?? 8787;
252
+ viteConfig.server.port ??= 8787;
253
+ // derive the public url from vite's dev server settings when no
254
+ // origin was set explicitly via the url option or VITE_APP_URL
255
+ if (resolvedUrl === undefined) {
256
+ const { host, port } = viteConfig.server;
257
+ const hostname = typeof host === 'string' && host.length > 0 && host !== '0.0.0.0'
258
+ ? host
259
+ : 'localhost';
260
+ resolvedUrl = `http://${hostname}:${port}`;
261
+ }
240
262
  viteConfig.define ??= {};
241
- viteConfig.define['import.meta.env.VITE_APP_URL'] = JSON.stringify(config.url);
263
+ viteConfig.define['import.meta.env.VITE_APP_URL'] = JSON.stringify(resolvedUrl);
242
264
  viteConfig.define['import.meta.env.SOLAS_VERSION'] = JSON.stringify(pkg.version);
243
265
  viteConfig.optimizeDeps ??= {};
244
266
  viteConfig.optimizeDeps.exclude = [
@@ -255,12 +277,12 @@ function solas(c) {
255
277
  ...viteConfig.resolve.alias,
256
278
  {
257
279
  find: '.solas',
258
- replacement: path.resolve(process.cwd(), Solas.Config.GENERATED_DIR),
280
+ replacement: path.resolve(process.cwd(), Config.GENERATED_DIR),
259
281
  },
260
282
  ]
261
283
  : {
262
284
  ...viteConfig.resolve.alias,
263
- '.solas': path.resolve(process.cwd(), Solas.Config.GENERATED_DIR),
285
+ '.solas': path.resolve(process.cwd(), Config.GENERATED_DIR),
264
286
  };
265
287
  },
266
288
  configResolved(resolvedConfig) {
@@ -268,7 +290,7 @@ function solas(c) {
268
290
  buildContext.command = resolvedConfig.command;
269
291
  },
270
292
  configureServer(server) {
271
- logger.info('[configureServer]', `Watching for changes in ./${Solas.Config.APP_DIR}...`);
293
+ logger.info('[configureServer]', `Watching for changes in ./${Config.APP_DIR}...`);
272
294
  server.watcher
273
295
  .on('add', (p) => rebuild('add', p))
274
296
  .on('change', (p) => rebuild('change', p))
@@ -294,7 +316,7 @@ function solas(c) {
294
316
  }
295
317
  // resolve sitemap routes
296
318
  let sitemapRoutes = [];
297
- if (config.sitemap && config.url) {
319
+ if (config.sitemap && resolvedUrl) {
298
320
  const auto = [
299
321
  ...new Set([...buildContext.knownRoutes, ...buildContext.prerenderRoutes]),
300
322
  ];
@@ -306,7 +328,7 @@ function solas(c) {
306
328
  }
307
329
  }
308
330
  // write build manifest
309
- const generatedDir = path.join(process.cwd(), Solas.Config.GENERATED_DIR);
331
+ const generatedDir = path.join(process.cwd(), Config.GENERATED_DIR);
310
332
  await Runtime.write(path.join(generatedDir, 'build.json'), JSON.stringify({
311
333
  base: resolvedViteConfig?.base ?? '/',
312
334
  publicFiles: await collectPublicFiles(resolvedViteConfig?.publicDir),
@@ -314,7 +336,7 @@ function solas(c) {
314
336
  sitemapRoutes,
315
337
  precompress: config.precompress,
316
338
  trailingSlash: config.trailingSlash,
317
- url: config.url,
339
+ url: resolvedUrl,
318
340
  }));
319
341
  },
320
342
  buildApp: {
@@ -328,12 +350,12 @@ function solas(c) {
328
350
  plugin,
329
351
  rsc({
330
352
  entries: {
331
- rsc: `./${Solas.Config.GENERATED_DIR}/${Solas.Config.ENTRY_RSC}`,
332
- ssr: `./${Solas.Config.GENERATED_DIR}/${Solas.Config.ENTRY_SSR}`,
333
- client: `./${Solas.Config.GENERATED_DIR}/${Solas.Config.ENTRY_BROWSER}`,
353
+ rsc: `./${Config.GENERATED_DIR}/${Config.ENTRY_RSC}`,
354
+ ssr: `./${Config.GENERATED_DIR}/${Config.ENTRY_SSR}`,
355
+ client: `./${Config.GENERATED_DIR}/${Config.ENTRY_BROWSER}`,
334
356
  },
335
357
  }),
336
358
  ];
337
359
  }
338
360
  export default solas;
339
- export { Solas } from './solas.js';
361
+ export * as Solas from './solas.js';
@@ -0,0 +1,4 @@
1
+ export declare class BoundaryError extends Error {
2
+ digest?: string;
3
+ constructor(digest?: string);
4
+ }
@@ -0,0 +1,8 @@
1
+ export class BoundaryError extends Error {
2
+ digest;
3
+ constructor(digest) {
4
+ super();
5
+ if (digest)
6
+ this.digest = digest;
7
+ }
8
+ }
@@ -1,4 +1,4 @@
1
- import type { BrowserRouter } from './shared.js';
1
+ import * as BrowserRouter from './shared.js';
2
2
  /**
3
3
  * A small in-memory mirror of browser history used by BrowserRouter.
4
4
  * It keeps local entries/index in sync with push/replace/go and
@@ -1,4 +1,4 @@
1
- import { BrowserRouter } from './shared.js';
1
+ import * as BrowserRouter from './shared.js';
2
2
  type AnchorProps = React.ComponentPropsWithRef<'a'> & {
3
3
  href: string;
4
4
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useRef } from 'react';
4
- import { BrowserRouter } from './shared.js';
4
+ import * as BrowserRouter from './shared.js';
5
5
  import { useRouter } from './use-router.js';
6
6
  function guard(path, prefetcher) {
7
7
  const connection = window.navigator.connection;
@@ -1,6 +1,6 @@
1
1
  import type { RscPayload } from '../env/rsc.js';
2
- import { BrowserRouter } from './shared.js';
3
- export { BrowserRouter } from './shared.js';
2
+ import * as BrowserRouter from './shared.js';
3
+ export * as BrowserRouter from './shared.js';
4
4
  export declare const BrowserRouterContext: import("react").Context<BrowserRouter.Context>;
5
5
  export declare function BrowserRouterProvider({ children, setPayload, isNavigating, url }: {
6
6
  children: React.ReactNode;
@@ -3,11 +3,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useEffect, useMemo, useRef } from 'react';
4
4
  import { createFromFetch } from '@vitejs/plugin-rsc/browser';
5
5
  import { Logger } from '../../utils/logger.js';
6
- import { Solas } from '../../solas.js';
6
+ import * as Events from '../../events.js';
7
7
  import { BrowserRouterHistory } from './history.js';
8
8
  import { ResponseCache } from './response-cache.js';
9
- import { BrowserRouter } from './shared.js';
10
- export { BrowserRouter } from './shared.js';
9
+ import * as BrowserRouter from './shared.js';
10
+ export * as BrowserRouter from './shared.js';
11
11
  export const BrowserRouterContext = createContext({
12
12
  go: async () => '',
13
13
  prefetch: () => { },
@@ -98,7 +98,7 @@ export function BrowserRouterProvider({ children, setPayload, isNavigating = fal
98
98
  history.current.replaceState(resolvedPath);
99
99
  }
100
100
  setPayload?.(payload);
101
- window.dispatchEvent(new CustomEvent(Solas.Events.names.NAVIGATION, {
101
+ window.dispatchEvent(new CustomEvent(Events.names.NAVIGATION, {
102
102
  detail: { path: resolvedPath },
103
103
  }));
104
104
  return resolvedPath;
@@ -107,7 +107,7 @@ export function BrowserRouterProvider({ children, setPayload, isNavigating = fal
107
107
  if (err instanceof Error && err.name === 'AbortError') {
108
108
  return path;
109
109
  }
110
- window.dispatchEvent(new CustomEvent(Solas.Events.names.NAVIGATION_ERROR, {
110
+ window.dispatchEvent(new CustomEvent(Events.names.NAVIGATION_ERROR, {
111
111
  detail: {
112
112
  path,
113
113
  error: err instanceof Error ? err.message : Logger.print(err),