@inglorious/ssx 1.5.12 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ssx",
3
- "version": "1.5.12",
3
+ "version": "1.6.0",
4
4
  "description": "Server-Side-X. Xecution? Xperience? Who knows.",
5
5
  "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -57,7 +57,7 @@
57
57
  "svgo": "^4.0.0",
58
58
  "vite": "^7.1.3",
59
59
  "vite-plugin-image-optimizer": "^2.0.3",
60
- "@inglorious/web": "4.1.5"
60
+ "@inglorious/web": "4.2.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "prettier": "^3.6.2",
@@ -1,3 +1,7 @@
1
+ /** @typedef {import('lit-html').TemplateResult} TemplateResult */
2
+ /** @typedef {import('@inglorious/store').Store} Store */
3
+ /** @typedef {import('@inglorious/web').Api} Api */
4
+
1
5
  import { html } from "@inglorious/web"
2
6
  import { render as ssrRender } from "@lit-labs/ssr"
3
7
  import { collectResult } from "@lit-labs/ssr/lib/render-result.js"
@@ -8,8 +12,8 @@ import { layout as defaultLayout } from "./layout.js"
8
12
  * Renders a page or component to HTML using the store state.
9
13
  * It handles SSR rendering of Lit templates and optional HTML wrapping.
10
14
  *
11
- * @param {Object} store - The application store instance.
12
- * @param {Function} renderFn - A function that returns a Lit template. Receives `api` as argument.
15
+ * @param {Store} store - The application store instance.
16
+ * @param {(api: Api) => TemplateResult | null} renderFn - The root render function.
13
17
  * @param {Object} [options] - Rendering options.
14
18
  * @param {boolean} [options.wrap=false] - Whether to wrap the output in a full HTML document.
15
19
  * @param {Function} [options.layout] - Custom layout function.
@@ -52,26 +56,17 @@ function stripLitMarkers(html) {
52
56
 
53
57
  // TODO: this was copied from @inglorious/web, maybe expose it?
54
58
  /**
55
- * Creates a render function bound to the store API.
56
- * This mimics the `api.render` behavior but for SSR context.
57
- *
58
- * @param {Object} api - The store API.
59
- * @returns {Function} The render function.
59
+ * Creates a render function for the mount API.
60
+ * @param {Api} api - The mount API.
61
+ * @returns {Api['render']} A `render` function that can render an entity or a type by its ID.
62
+ * @private
60
63
  */
61
64
  function createRender(api) {
62
- return function (id, options = {}) {
65
+ return function (id) {
63
66
  const entity = api.getEntity(id)
64
67
 
65
68
  if (!entity) {
66
- const { allowType } = options
67
- if (!allowType) return ""
68
-
69
- const type = api.getType(id)
70
- if (!type?.render) {
71
- console.warn(`No entity or type found: ${id}`)
72
- return html`<div>Not found: ${id}</div>`
73
- }
74
- return type.render(api)
69
+ return ""
75
70
  }
76
71
 
77
72
  const type = api.getType(entity.type)
@@ -20,6 +20,7 @@ describe("renderPage", () => {
20
20
  types: { index: module.index },
21
21
  updateMode: "manual",
22
22
  })
23
+ store.update()
23
24
 
24
25
  const html = await renderPage(store, page, undefined, DEFAULT_OPTIONS)
25
26
 
@@ -56,7 +56,7 @@ const module = await getRoute(page.pattern)()
56
56
  const type = module[page.moduleName]
57
57
  types[page.moduleName] = type
58
58
 
59
- const store = createStore({ types, entities, middlewares })
59
+ const store = createStore({ types, entities, middlewares, autoCreateEntities: true })
60
60
 
61
61
  const root = document.getElementById("root")
62
62
 
@@ -1,3 +1,4 @@
1
+ import { existsSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { pathToFileURL } from "node:url"
3
4
 
@@ -33,14 +34,18 @@ export async function generateStore(pages = [], options = {}, loader) {
33
34
  const extensions = ["js", "ts"]
34
35
 
35
36
  for (const ext of extensions) {
36
- try {
37
- const module = await load(path.join(srcDir, "store", `entities.${ext}`))
38
- entities = module.entities
39
- break
40
- } catch {
41
- // ignore and try next extension
37
+ const fullPath = path.join(srcDir, "store", `entities.${ext}`)
38
+
39
+ if (existsSync(fullPath)) {
40
+ try {
41
+ const module = await load(fullPath)
42
+ entities = module.entities
43
+ break
44
+ } catch {
45
+ // ignore and try next extension
46
+ }
42
47
  }
43
48
  }
44
49
 
45
- return createStore({ types, entities, updateMode: "manual" })
50
+ return createStore({ types, entities, autoCreateEntities: true })
46
51
  }
@@ -1,3 +1,4 @@
1
+ import { existsSync } from "node:fs"
1
2
  import path from "node:path"
2
3
 
3
4
  import { describe, expect, it, vi } from "vitest"
@@ -7,6 +8,8 @@ import { generateStore } from "."
7
8
  const ROOT_DIR = path.join(import.meta.dirname, "..", "__fixtures__")
8
9
  const PAGES_DIR = path.join(ROOT_DIR, "src", "pages")
9
10
 
11
+ vi.mock("node:fs")
12
+
10
13
  describe("generateStore", () => {
11
14
  it("should generate the proper types and entities from a static page", async () => {
12
15
  const page = {
@@ -55,25 +58,32 @@ describe("generateStore", () => {
55
58
  expect(store.getState()).not.toHaveProperty("about")
56
59
  })
57
60
 
58
- it("should attempt to load entities.js and entities.ts", async () => {
61
+ it("should check for entities.js and entities.ts and load them if they exist", async () => {
62
+ vi.mocked(existsSync).mockReturnValue(true)
63
+
59
64
  const loader = vi.fn(async (p) => {
60
- // Mock a successful page load
61
- if (p.endsWith("index.js")) {
62
- return { index: { render: () => {} } }
65
+ if (p.includes("index.js")) {
66
+ return {
67
+ index: { render: () => "" },
68
+ }
69
+ }
70
+
71
+ if (p.includes("entities.js") || p.includes("entities.ts")) {
72
+ return {
73
+ entities: {},
74
+ }
63
75
  }
64
- // Mock entity files not being found
65
- throw new Error("MODULE_NOT_FOUND")
76
+
77
+ return {}
66
78
  })
67
79
 
68
80
  const page = { filePath: path.join(PAGES_DIR, "index.js") }
69
- await generateStore([page], { rootDir: "." }, loader)
81
+
82
+ await generateStore([page], { rootDir: ROOT_DIR }, loader)
70
83
 
71
84
  expect(loader).toHaveBeenCalledWith(page.filePath)
72
85
  expect(loader).toHaveBeenCalledWith(
73
- path.join("src", "store", "entities.js"),
74
- )
75
- expect(loader).toHaveBeenCalledWith(
76
- path.join("src", "store", "entities.ts"),
86
+ path.join(ROOT_DIR, "src", "store", "entities.js"),
77
87
  )
78
88
  })
79
89
  })