@inglorious/ssx 1.1.0 → 1.1.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/package.json +4 -1
- package/src/build/build.test.js +117 -4
- package/src/build/index.js +37 -20
- package/src/build/manifest.js +23 -15
- package/src/build/manifest.test.js +153 -0
- package/src/build/metadata.js +1 -1
- package/src/build/pages.js +12 -0
- package/src/build/pages.test.js +83 -0
- package/src/build/public.js +15 -0
- package/src/build/public.test.js +59 -0
- package/src/build/rss.js +30 -11
- package/src/build/rss.test.js +104 -0
- package/src/build/sitemap.js +15 -6
- package/src/build/sitemap.test.js +84 -0
- package/src/dev/index.js +10 -20
- package/src/dev/vite-config.js +20 -0
- package/src/dev/vite-config.test.js +46 -0
- package/src/render/html.js +27 -1
- package/src/render/index.js +11 -1
- package/src/render/layout.js +15 -0
- package/src/render/layout.test.js +58 -0
- package/src/render/render.test.js +87 -84
- package/src/router/index.js +113 -53
- package/src/router/router.test.js +33 -2
- package/src/scripts/app.js +6 -2
- package/src/scripts/app.test.js +46 -44
- package/src/{store.js → store/index.js} +11 -1
- package/src/store/store.test.js +56 -0
- package/src/{module.js → utils/module.js} +8 -0
- package/src/utils/module.test.js +64 -0
- package/src/utils/page-options.js +17 -0
- package/src/utils/page-options.test.js +57 -0
- package/src/module.test.js +0 -45
- package/src/page-options.js +0 -8
- package/src/store.test.js +0 -40
- /package/src/{config.js → utils/config.js} +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest"
|
|
4
|
+
|
|
5
|
+
import { generateStore } from "."
|
|
6
|
+
|
|
7
|
+
const ROOT_DIR = path.join(__dirname, "..", "__fixtures__")
|
|
8
|
+
|
|
9
|
+
describe("generateStore", () => {
|
|
10
|
+
it("should generate the proper types and entities from a static page", async () => {
|
|
11
|
+
const page = {
|
|
12
|
+
filePath: path.join(ROOT_DIR, "pages", "index.js"),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
16
|
+
|
|
17
|
+
expect(store.getType("index").render).toBeDefined()
|
|
18
|
+
expect(store.getState()).toMatchSnapshot()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("should generate the proper types and entities from a page with an entity", async () => {
|
|
22
|
+
const page = {
|
|
23
|
+
filePath: path.join(ROOT_DIR, "pages", "about.js"),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
27
|
+
|
|
28
|
+
expect(store.getType("about").render).toBeDefined()
|
|
29
|
+
expect(store.getState()).toMatchSnapshot()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("should generate the proper types and entities from a page that has metadata", async () => {
|
|
33
|
+
const page = {
|
|
34
|
+
filePath: path.join(ROOT_DIR, "pages", "blog.js"),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
38
|
+
|
|
39
|
+
expect(store.getType("blog").render).toBeDefined()
|
|
40
|
+
expect(store.getState()).toMatchSnapshot()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("should handle missing entities.js gracefully", async () => {
|
|
44
|
+
const page = {
|
|
45
|
+
filePath: path.join(ROOT_DIR, "pages", "index.js"),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Point to a directory that doesn't contain entities.js
|
|
49
|
+
const store = await generateStore([page], {
|
|
50
|
+
rootDir: path.join(ROOT_DIR, "pages"),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Should initialize with empty entities (or at least not the ones from fixtures)
|
|
54
|
+
expect(store.getState()).not.toHaveProperty("about")
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the name of the exported entity from a page module.
|
|
3
|
+
* It looks for an export that is an object containing a `render` function.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} pageModule - The module object imported from a page file.
|
|
6
|
+
* @returns {string} The name of the export that represents the entity.
|
|
7
|
+
* @throws {Error} If no valid entity export is found.
|
|
8
|
+
*/
|
|
1
9
|
export function getModuleName(pageModule) {
|
|
2
10
|
const name = Object.keys(pageModule).find((key) => {
|
|
3
11
|
const value = pageModule[key]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { getModuleName } from "./module"
|
|
4
|
+
|
|
5
|
+
describe("getModuleName", () => {
|
|
6
|
+
it("should get the name when it's the only export", () => {
|
|
7
|
+
const module = {
|
|
8
|
+
about: {
|
|
9
|
+
render: () => {},
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
expect(getModuleName(module)).toBe("about")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("should get the name when there's other exports", () => {
|
|
17
|
+
const module = {
|
|
18
|
+
about: {
|
|
19
|
+
render: () => {},
|
|
20
|
+
},
|
|
21
|
+
title: "About",
|
|
22
|
+
meta: {
|
|
23
|
+
description: "About page",
|
|
24
|
+
},
|
|
25
|
+
scripts: ["/script.js"],
|
|
26
|
+
styles: ["/style.css"],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expect(getModuleName(module)).toBe("about")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("should get the name when the exports are functions", () => {
|
|
33
|
+
const module = {
|
|
34
|
+
about: {
|
|
35
|
+
render: () => {},
|
|
36
|
+
},
|
|
37
|
+
title: () => "About",
|
|
38
|
+
meta: () => ({
|
|
39
|
+
description: "About page",
|
|
40
|
+
}),
|
|
41
|
+
scripts: ["/script.js"],
|
|
42
|
+
styles: ["/style.css"],
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
expect(getModuleName(module)).toBe("about")
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("should throw an error when no entity with render method is found", () => {
|
|
49
|
+
const module = {
|
|
50
|
+
title: "Just title",
|
|
51
|
+
meta: {},
|
|
52
|
+
}
|
|
53
|
+
expect(() => getModuleName(module)).toThrow(
|
|
54
|
+
/Page module must export an entity/,
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("should throw an error for empty module", () => {
|
|
59
|
+
const module = {}
|
|
60
|
+
expect(() => getModuleName(module)).toThrow(
|
|
61
|
+
/Page module must export an entity/,
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a helper function to retrieve page options (metadata) with fallback defaults.
|
|
3
|
+
* Handles both static metadata objects and dynamic metadata functions.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} store - The application store instance.
|
|
6
|
+
* @param {Object} module - The imported page module.
|
|
7
|
+
* @param {Object} entity - The entity associated with the page.
|
|
8
|
+
* @returns {function(string, Object): any} A function that retrieves a specific option by name.
|
|
9
|
+
*/
|
|
10
|
+
export function createGetPageOption(store, module, entity) {
|
|
11
|
+
let { metadata = {} } = module
|
|
12
|
+
if (typeof metadata === "function") {
|
|
13
|
+
metadata = metadata(entity, store._api)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (name, defaults) => metadata[name] ?? defaults[name]
|
|
17
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { createGetPageOption } from "./page-options"
|
|
4
|
+
|
|
5
|
+
describe("createGetPageOption", () => {
|
|
6
|
+
const mockStore = { _api: { someApi: true } }
|
|
7
|
+
const mockEntity = { id: 1, title: "Entity Title" }
|
|
8
|
+
|
|
9
|
+
it("should retrieve value from static metadata object", () => {
|
|
10
|
+
const module = {
|
|
11
|
+
metadata: {
|
|
12
|
+
title: "Static Title",
|
|
13
|
+
priority: 0.8,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const getOption = createGetPageOption(mockStore, module, mockEntity)
|
|
18
|
+
const defaults = { title: "Default", priority: 0.5, author: "Me" }
|
|
19
|
+
|
|
20
|
+
expect(getOption("title", defaults)).toBe("Static Title")
|
|
21
|
+
expect(getOption("priority", defaults)).toBe(0.8)
|
|
22
|
+
expect(getOption("author", defaults)).toBe("Me")
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("should retrieve value from dynamic metadata function", () => {
|
|
26
|
+
const module = {
|
|
27
|
+
metadata: vi.fn((entity, api) => ({
|
|
28
|
+
title: `Dynamic ${entity.title}`,
|
|
29
|
+
apiAvailable: !!api,
|
|
30
|
+
})),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getOption = createGetPageOption(mockStore, module, mockEntity)
|
|
34
|
+
const defaults = { title: "Default", apiAvailable: false }
|
|
35
|
+
|
|
36
|
+
expect(module.metadata).toHaveBeenCalledWith(mockEntity, mockStore._api)
|
|
37
|
+
expect(getOption("title", defaults)).toBe("Dynamic Entity Title")
|
|
38
|
+
expect(getOption("apiAvailable", defaults)).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("should use defaults when metadata is missing", () => {
|
|
42
|
+
const module = {} // No metadata export
|
|
43
|
+
|
|
44
|
+
const getOption = createGetPageOption(mockStore, module, mockEntity)
|
|
45
|
+
const defaults = { title: "Default Title" }
|
|
46
|
+
|
|
47
|
+
expect(getOption("title", defaults)).toBe("Default Title")
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("should use defaults when specific key is missing in metadata", () => {
|
|
51
|
+
const module = { metadata: { other: "value" } }
|
|
52
|
+
const getOption = createGetPageOption(mockStore, module, mockEntity)
|
|
53
|
+
const defaults = { title: "Default Title" }
|
|
54
|
+
|
|
55
|
+
expect(getOption("title", defaults)).toBe("Default Title")
|
|
56
|
+
})
|
|
57
|
+
})
|
package/src/module.test.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { expect, it } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { getModuleName } from "./module"
|
|
4
|
-
|
|
5
|
-
it("should get the name when it's the only export", () => {
|
|
6
|
-
const module = {
|
|
7
|
-
about: {
|
|
8
|
-
render: () => {},
|
|
9
|
-
},
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
expect(getModuleName(module)).toBe("about")
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it("should get the name when there's other exports", () => {
|
|
16
|
-
const module = {
|
|
17
|
-
about: {
|
|
18
|
-
render: () => {},
|
|
19
|
-
},
|
|
20
|
-
title: "About",
|
|
21
|
-
meta: {
|
|
22
|
-
description: "About page",
|
|
23
|
-
},
|
|
24
|
-
scripts: ["/script.js"],
|
|
25
|
-
styles: ["/style.css"],
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
expect(getModuleName(module)).toBe("about")
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("should get the name when the exports are functions", () => {
|
|
32
|
-
const module = {
|
|
33
|
-
about: {
|
|
34
|
-
render: () => {},
|
|
35
|
-
},
|
|
36
|
-
title: () => "About",
|
|
37
|
-
meta: () => ({
|
|
38
|
-
description: "About page",
|
|
39
|
-
}),
|
|
40
|
-
scripts: ["/script.js"],
|
|
41
|
-
styles: ["/style.css"],
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
expect(getModuleName(module)).toBe("about")
|
|
45
|
-
})
|
package/src/page-options.js
DELETED
package/src/store.test.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
|
|
3
|
-
import { expect, it } from "vitest"
|
|
4
|
-
|
|
5
|
-
import { generateStore } from "./store"
|
|
6
|
-
|
|
7
|
-
const ROOT_DIR = path.join(__dirname, "__fixtures__")
|
|
8
|
-
|
|
9
|
-
it("should generate the proper types and entities from a static page", async () => {
|
|
10
|
-
const page = {
|
|
11
|
-
filePath: path.join(ROOT_DIR, "pages", "index.js"),
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
15
|
-
|
|
16
|
-
expect(store.getType("index").render).toBeDefined()
|
|
17
|
-
expect(store.getState()).toMatchSnapshot()
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it("should generate the proper types and entities from a page with an entity", async () => {
|
|
21
|
-
const page = {
|
|
22
|
-
filePath: path.join(ROOT_DIR, "pages", "about.js"),
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
26
|
-
|
|
27
|
-
expect(store.getType("about").render).toBeDefined()
|
|
28
|
-
expect(store.getState()).toMatchSnapshot()
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("should generate the proper types and entities from a page that has metadata", async () => {
|
|
32
|
-
const page = {
|
|
33
|
-
filePath: path.join(ROOT_DIR, "pages", "blog.js"),
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const store = await generateStore([page], { rootDir: ROOT_DIR })
|
|
37
|
-
|
|
38
|
-
expect(store.getType("blog").render).toBeDefined()
|
|
39
|
-
expect(store.getState()).toMatchSnapshot()
|
|
40
|
-
})
|
|
File without changes
|