polen 0.10.0-next.3 → 0.10.0-next.4
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/README.md +49 -376
- package/build/api/api.d.ts +1 -0
- package/build/api/api.d.ts.map +1 -1
- package/build/api/api.js +1 -0
- package/build/api/api.js.map +1 -1
- package/build/api/static/index.d.ts +2 -0
- package/build/api/static/index.d.ts.map +1 -0
- package/build/api/static/index.js +2 -0
- package/build/api/static/index.js.map +1 -0
- package/build/api/static/manifest.d.ts +18 -0
- package/build/api/static/manifest.d.ts.map +1 -0
- package/build/api/static/manifest.js +13 -0
- package/build/api/static/manifest.js.map +1 -0
- package/build/api/static/rebase.d.ts +14 -0
- package/build/api/static/rebase.d.ts.map +1 -0
- package/build/api/static/rebase.js +110 -0
- package/build/api/static/rebase.js.map +1 -0
- package/build/api/static/static.d.ts +3 -0
- package/build/api/static/static.d.ts.map +1 -0
- package/build/api/static/static.js +3 -0
- package/build/api/static/static.js.map +1 -0
- package/build/api/vite/plugins/build.d.ts.map +1 -1
- package/build/api/vite/plugins/build.js +22 -1
- package/build/api/vite/plugins/build.js.map +1 -1
- package/build/cli/commands/static/$default.d.ts +3 -0
- package/build/cli/commands/static/$default.d.ts.map +1 -0
- package/build/cli/commands/static/$default.js +38 -0
- package/build/cli/commands/static/$default.js.map +1 -0
- package/build/cli/commands/static/rebase.d.ts +2 -0
- package/build/cli/commands/static/rebase.d.ts.map +1 -0
- package/build/cli/commands/static/rebase.js +26 -0
- package/build/cli/commands/static/rebase.js.map +1 -0
- package/build/cli/commands/static.d.ts +3 -0
- package/build/cli/commands/static.d.ts.map +1 -0
- package/build/cli/commands/static.js +5 -0
- package/build/cli/commands/static.js.map +1 -0
- package/build/lib/demos/builder.d.ts +83 -0
- package/build/lib/demos/builder.d.ts.map +1 -0
- package/build/lib/demos/builder.js +237 -0
- package/build/lib/demos/builder.js.map +1 -0
- package/build/lib/demos/config-schema.d.ts +243 -0
- package/build/lib/demos/config-schema.d.ts.map +1 -0
- package/build/lib/demos/config-schema.js +52 -0
- package/build/lib/demos/config-schema.js.map +1 -0
- package/build/lib/demos/config.d.ts +40 -0
- package/build/lib/demos/config.d.ts.map +1 -0
- package/build/lib/demos/config.js +180 -0
- package/build/lib/demos/config.js.map +1 -0
- package/build/lib/demos/index.d.ts +9 -0
- package/build/lib/demos/index.d.ts.map +1 -0
- package/build/lib/demos/index.js +8 -0
- package/build/lib/demos/index.js.map +1 -0
- package/build/lib/demos/ui/components.d.ts +33 -0
- package/build/lib/demos/ui/components.d.ts.map +1 -0
- package/build/lib/demos/ui/components.js +699 -0
- package/build/lib/demos/ui/components.js.map +1 -0
- package/build/lib/demos/ui/data-collector.d.ts +88 -0
- package/build/lib/demos/ui/data-collector.d.ts.map +1 -0
- package/build/lib/demos/ui/data-collector.js +174 -0
- package/build/lib/demos/ui/data-collector.js.map +1 -0
- package/build/lib/demos/ui/landing-page-cli.d.ts +3 -0
- package/build/lib/demos/ui/landing-page-cli.d.ts.map +1 -0
- package/build/lib/demos/ui/landing-page-cli.js +21 -0
- package/build/lib/demos/ui/landing-page-cli.js.map +1 -0
- package/build/lib/demos/ui/landing-page.d.ts +32 -0
- package/build/lib/demos/ui/landing-page.d.ts.map +1 -0
- package/build/lib/demos/ui/landing-page.js +83 -0
- package/build/lib/demos/ui/landing-page.js.map +1 -0
- package/build/lib/demos/ui/page-renderer.d.ts +26 -0
- package/build/lib/demos/ui/page-renderer.d.ts.map +1 -0
- package/build/lib/demos/ui/page-renderer.js +104 -0
- package/build/lib/demos/ui/page-renderer.js.map +1 -0
- package/build/lib/demos/utils.d.ts +14 -0
- package/build/lib/demos/utils.d.ts.map +1 -0
- package/build/lib/demos/utils.js +37 -0
- package/build/lib/demos/utils.js.map +1 -0
- package/build/lib/deployment/$$.d.ts +3 -0
- package/build/lib/deployment/$$.d.ts.map +1 -0
- package/build/lib/deployment/$$.js +3 -0
- package/build/lib/deployment/$$.js.map +1 -0
- package/build/lib/deployment/$.d.ts +2 -0
- package/build/lib/deployment/$.d.ts.map +1 -0
- package/build/lib/deployment/$.js +2 -0
- package/build/lib/deployment/$.js.map +1 -0
- package/build/lib/deployment/metadata.d.ts +32 -0
- package/build/lib/deployment/metadata.d.ts.map +1 -0
- package/build/lib/deployment/metadata.js +37 -0
- package/build/lib/deployment/metadata.js.map +1 -0
- package/build/lib/deployment/path-manager.d.ts +41 -0
- package/build/lib/deployment/path-manager.d.ts.map +1 -0
- package/build/lib/deployment/path-manager.js +157 -0
- package/build/lib/deployment/path-manager.js.map +1 -0
- package/build/lib/github-actions/git-controller.d.ts +50 -0
- package/build/lib/github-actions/git-controller.d.ts.map +1 -0
- package/build/lib/github-actions/git-controller.js +90 -0
- package/build/lib/github-actions/git-controller.js.map +1 -0
- package/build/lib/github-actions/github-actions.d.ts +7 -0
- package/build/lib/github-actions/github-actions.d.ts.map +1 -0
- package/build/lib/github-actions/github-actions.js +7 -0
- package/build/lib/github-actions/github-actions.js.map +1 -0
- package/build/lib/github-actions/index.d.ts +2 -0
- package/build/lib/github-actions/index.d.ts.map +1 -0
- package/build/lib/github-actions/index.js +2 -0
- package/build/lib/github-actions/index.js.map +1 -0
- package/build/lib/github-actions/lib/get-pr-deployments.d.ts +12 -0
- package/build/lib/github-actions/lib/get-pr-deployments.d.ts.map +1 -0
- package/build/lib/github-actions/lib/get-pr-deployments.js +51 -0
- package/build/lib/github-actions/lib/get-pr-deployments.js.map +1 -0
- package/build/lib/github-actions/pr-controller.d.ts +39 -0
- package/build/lib/github-actions/pr-controller.d.ts.map +1 -0
- package/build/lib/github-actions/pr-controller.js +122 -0
- package/build/lib/github-actions/pr-controller.js.map +1 -0
- package/build/lib/github-actions/run-step-cli.d.ts +9 -0
- package/build/lib/github-actions/run-step-cli.d.ts.map +1 -0
- package/build/lib/github-actions/run-step-cli.js +71 -0
- package/build/lib/github-actions/run-step-cli.js.map +1 -0
- package/build/lib/github-actions/runner.d.ts +17 -0
- package/build/lib/github-actions/runner.d.ts.map +1 -0
- package/build/lib/github-actions/runner.js +195 -0
- package/build/lib/github-actions/runner.js.map +1 -0
- package/build/lib/github-actions/schemas/context.d.ts +933 -0
- package/build/lib/github-actions/schemas/context.d.ts.map +1 -0
- package/build/lib/github-actions/schemas/context.js +407 -0
- package/build/lib/github-actions/schemas/context.js.map +1 -0
- package/build/lib/github-actions/schemas/index.d.ts +5 -0
- package/build/lib/github-actions/schemas/index.d.ts.map +1 -0
- package/build/lib/github-actions/schemas/index.js +5 -0
- package/build/lib/github-actions/schemas/index.js.map +1 -0
- package/build/lib/github-actions/search-module.d.ts +38 -0
- package/build/lib/github-actions/search-module.d.ts.map +1 -0
- package/build/lib/github-actions/search-module.js +40 -0
- package/build/lib/github-actions/search-module.js.map +1 -0
- package/build/lib/github-actions/step.d.ts +163 -0
- package/build/lib/github-actions/step.d.ts.map +1 -0
- package/build/lib/github-actions/step.js +121 -0
- package/build/lib/github-actions/step.js.map +1 -0
- package/build/lib/helpers.d.ts.map +1 -1
- package/build/lib/helpers.js +5 -3
- package/build/lib/helpers.js.map +1 -1
- package/build/lib/kit-temp.d.ts +54 -0
- package/build/lib/kit-temp.d.ts.map +1 -1
- package/build/lib/kit-temp.js +80 -14
- package/build/lib/kit-temp.js.map +1 -1
- package/build/lib/kit-temp.test-d.d.ts +2 -0
- package/build/lib/kit-temp.test-d.d.ts.map +1 -0
- package/build/lib/kit-temp.test-d.js +75 -0
- package/build/lib/kit-temp.test-d.js.map +1 -0
- package/build/lib/mask/$$.d.ts +3 -0
- package/build/lib/mask/$$.d.ts.map +1 -0
- package/build/lib/mask/$$.js +3 -0
- package/build/lib/mask/$$.js.map +1 -0
- package/build/lib/mask/$.d.ts +2 -0
- package/build/lib/mask/$.d.ts.map +1 -0
- package/build/lib/mask/$.js +2 -0
- package/build/lib/mask/$.js.map +1 -0
- package/build/lib/mask/apply.d.ts +86 -0
- package/build/lib/mask/apply.d.ts.map +1 -0
- package/build/lib/mask/apply.js +86 -0
- package/build/lib/mask/apply.js.map +1 -0
- package/build/lib/mask/mask.d.ts +124 -0
- package/build/lib/mask/mask.d.ts.map +1 -0
- package/build/lib/mask/mask.js +137 -0
- package/build/lib/mask/mask.js.map +1 -0
- package/build/lib/mask/mask.test-d.d.ts +2 -0
- package/build/lib/mask/mask.test-d.d.ts.map +1 -0
- package/build/lib/mask/mask.test-d.js +102 -0
- package/build/lib/mask/mask.test-d.js.map +1 -0
- package/build/lib/task/$$.d.ts +3 -0
- package/build/lib/task/$$.d.ts.map +1 -0
- package/build/lib/task/$$.js +3 -0
- package/build/lib/task/$$.js.map +1 -0
- package/build/lib/task/$.d.ts +2 -0
- package/build/lib/task/$.d.ts.map +1 -0
- package/build/lib/task/$.js +2 -0
- package/build/lib/task/$.js.map +1 -0
- package/build/lib/task/report.d.ts +28 -0
- package/build/lib/task/report.d.ts.map +1 -0
- package/build/lib/task/report.js +33 -0
- package/build/lib/task/report.js.map +1 -0
- package/build/lib/task/task.d.ts +44 -0
- package/build/lib/task/task.d.ts.map +1 -0
- package/build/lib/task/task.js +63 -0
- package/build/lib/task/task.js.map +1 -0
- package/build/lib/version-history/index.d.ts +3 -0
- package/build/lib/version-history/index.d.ts.map +1 -0
- package/build/lib/version-history/index.js +2 -0
- package/build/lib/version-history/index.js.map +1 -0
- package/build/lib/version-history/types.d.ts +64 -0
- package/build/lib/version-history/types.d.ts.map +1 -0
- package/build/lib/version-history/types.js +5 -0
- package/build/lib/version-history/types.js.map +1 -0
- package/build/lib/version-history/version-history.d.ts +85 -0
- package/build/lib/version-history/version-history.d.ts.map +1 -0
- package/build/lib/version-history/version-history.js +248 -0
- package/build/lib/version-history/version-history.js.map +1 -0
- package/build/sandbox.d.ts +2 -0
- package/build/sandbox.d.ts.map +1 -0
- package/build/sandbox.js +3 -0
- package/build/sandbox.js.map +1 -0
- package/build/template/components/Link.jsx +1 -1
- package/package.json +16 -9
- package/src/api/api.ts +1 -0
- package/src/api/singletons/markdown/markdown.test.ts +1 -1
- package/src/api/static/index.ts +1 -0
- package/src/api/static/manifest.test.ts +106 -0
- package/src/api/static/manifest.ts +16 -0
- package/src/api/static/rebase.test.ts +229 -0
- package/src/api/static/rebase.ts +140 -0
- package/src/api/static/static.ts +2 -0
- package/src/api/utils/asset-url/asset-url.test.ts +4 -4
- package/src/api/vite/plugins/build.ts +25 -1
- package/src/api/vite/plugins/core.ts +1 -1
- package/src/cli/commands/static/$default.ts +43 -0
- package/src/cli/commands/static/rebase.ts +37 -0
- package/src/cli/commands/static.ts +6 -0
- package/src/lib/demos/builder.ts +298 -0
- package/src/lib/demos/config-schema.ts +56 -0
- package/src/lib/demos/config.test.ts +193 -0
- package/src/lib/demos/config.ts +205 -0
- package/src/lib/demos/index.ts +9 -0
- package/src/lib/demos/ui/components.ts +739 -0
- package/src/lib/demos/ui/data-collector.ts +246 -0
- package/src/lib/demos/ui/landing-page-cli.ts +23 -0
- package/src/lib/demos/ui/landing-page.ts +126 -0
- package/src/lib/demos/ui/page-renderer.ts +124 -0
- package/src/lib/demos/utils.ts +43 -0
- package/src/lib/deployment/$$.ts +2 -0
- package/src/lib/deployment/$.test.ts +53 -0
- package/src/lib/deployment/$.ts +1 -0
- package/src/lib/deployment/metadata.ts +40 -0
- package/src/lib/deployment/path-manager.ts +186 -0
- package/src/lib/github-actions/git-controller.ts +151 -0
- package/src/lib/github-actions/github-actions.ts +6 -0
- package/src/lib/github-actions/index.ts +1 -0
- package/src/lib/github-actions/lib/get-pr-deployments.ts +76 -0
- package/src/lib/github-actions/pr-controller.test.ts +172 -0
- package/src/lib/github-actions/pr-controller.ts +183 -0
- package/src/lib/github-actions/run-step-cli.ts +84 -0
- package/src/lib/github-actions/runner.test.ts +192 -0
- package/src/lib/github-actions/runner.ts +226 -0
- package/src/lib/github-actions/schemas/context.ts +424 -0
- package/src/lib/github-actions/schemas/index.ts +5 -0
- package/src/lib/github-actions/search-module.test.ts +110 -0
- package/src/lib/github-actions/search-module.ts +76 -0
- package/src/lib/github-actions/step.test.ts +149 -0
- package/src/lib/github-actions/step.ts +232 -0
- package/src/lib/helpers.ts +4 -3
- package/src/lib/kit-temp.test-d.ts +115 -0
- package/src/lib/kit-temp.test.ts +127 -0
- package/src/lib/kit-temp.ts +126 -14
- package/src/lib/mask/$$.ts +2 -0
- package/src/lib/mask/$.test.ts +248 -0
- package/src/lib/mask/$.ts +1 -0
- package/src/lib/mask/apply.ts +134 -0
- package/src/lib/mask/mask.test-d.ts +144 -0
- package/src/lib/mask/mask.ts +244 -0
- package/src/lib/shiki/shiki.test.ts +1 -1
- package/src/lib/task/$$.ts +2 -0
- package/src/lib/task/$.test.ts +209 -0
- package/src/lib/task/$.ts +1 -0
- package/src/lib/task/report.ts +72 -0
- package/src/lib/task/task.ts +112 -0
- package/src/lib/version-history/index.test.ts +188 -0
- package/src/lib/version-history/index.ts +4 -0
- package/src/lib/version-history/types.ts +68 -0
- package/src/lib/version-history/version-history.ts +293 -0
- package/src/sandbox.ts +1 -0
- package/src/template/components/Link.tsx +1 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "polen",
|
3
|
-
"version": "0.10.0-next.
|
3
|
+
"version": "0.10.0-next.4",
|
4
4
|
"type": "module",
|
5
5
|
"description": "A framework for delightful GraphQL developer portals",
|
6
6
|
"author": {
|
@@ -35,7 +35,13 @@
|
|
35
35
|
"#*": {
|
36
36
|
"source": "./src/*.ts",
|
37
37
|
"default": "./build/*.js"
|
38
|
-
}
|
38
|
+
},
|
39
|
+
"#lib/mask": "./src/lib/mask/$.ts",
|
40
|
+
"#lib/mask/mask": "./src/lib/mask/$$.ts",
|
41
|
+
"#lib/task": "./src/lib/task/$.ts",
|
42
|
+
"#lib/task/task": "./src/lib/task/$$.ts",
|
43
|
+
"#lib/deployment": "./src/lib/deployment/$.ts",
|
44
|
+
"#lib/deployment/deployment": "./src/lib/deployment/$$.ts"
|
39
45
|
},
|
40
46
|
"exports": {
|
41
47
|
".": {
|
@@ -86,8 +92,9 @@
|
|
86
92
|
"@types/jsesc": "^3.0.3",
|
87
93
|
"@vitejs/plugin-react": "^4.5.2",
|
88
94
|
"@vitejs/plugin-react-oxc": "^0.2.3",
|
95
|
+
"@vltpkg/semver": "0.0.0-15",
|
89
96
|
"@vue/reactivity": "^3.5.16",
|
90
|
-
"@wollybeard/kit": "^0.
|
97
|
+
"@wollybeard/kit": "^0.40.0",
|
91
98
|
"@wollybeard/projector": "^0.2.0",
|
92
99
|
"ansis": "^4.1.0",
|
93
100
|
"clean-stack": "^5.2.0",
|
@@ -112,6 +119,7 @@
|
|
112
119
|
"resolve.imports": "^2.0.3",
|
113
120
|
"rolldown": "1.0.0-beta.16",
|
114
121
|
"shiki": "^3.6.0",
|
122
|
+
"simple-git": "^3.28.0",
|
115
123
|
"source-map": "^0.7.4",
|
116
124
|
"superjson": "^2.2.2",
|
117
125
|
"tinyglobby": "^0.2.14",
|
@@ -156,6 +164,7 @@
|
|
156
164
|
"eslint-plugin-react-refresh": "^0.4.20",
|
157
165
|
"eslint-plugin-tsdoc": "^0.4.0",
|
158
166
|
"eslint-plugin-unused-imports": "^4.1.4",
|
167
|
+
"fast-check": "^4.1.1",
|
159
168
|
"fs-jetpack": "^5.1.0",
|
160
169
|
"get-port-please": "^3.1.2",
|
161
170
|
"globals": "^16.2.0",
|
@@ -168,14 +177,10 @@
|
|
168
177
|
"typescript-eslint": "^8.34.1",
|
169
178
|
"vite-tsconfig-paths": "^5.1.4",
|
170
179
|
"vitest": "^3.2.3",
|
171
|
-
"zod": "
|
180
|
+
"zod": "3.25.67"
|
172
181
|
},
|
173
182
|
"packageManager": "pnpm@10.11.1",
|
174
183
|
"scripts": {
|
175
|
-
"gh:demos-rebuild": "gh workflow run demos-rebuild.yaml -f since_version=0.9.0-next.8",
|
176
|
-
"gh:demos-rebuild:list": "gh run list --workflow=demos-rebuild.yaml",
|
177
|
-
"gh:demos-rebuild:logs": "gh run view $(gh run list --workflow=demos-rebuild.yaml --limit=1 --json databaseId -q '.[0].databaseId') --log",
|
178
|
-
"gh:demos-rebuild:watch": "gh run watch $(gh run list --workflow=demos-rebuild.yaml --limit=1 --json databaseId -q '.[0].databaseId')",
|
179
184
|
"sandbox": "tsx src/sandbox",
|
180
185
|
"sandbox:watch": "tsx --watch src/sandbox",
|
181
186
|
"sb": "pnpm sandbox",
|
@@ -186,7 +191,9 @@
|
|
186
191
|
"test:unit": "vitest",
|
187
192
|
"dev": "pnpm build:watch:emit",
|
188
193
|
"dev:pokemon": "pnpm polen dev --project examples/pokemon",
|
189
|
-
"dev:
|
194
|
+
"dev:hive": "pnpm polen dev --project examples/hive",
|
195
|
+
"dev:github": "pnpm polen dev --project examples/github",
|
196
|
+
"dev:demo-index": "node --no-warnings --experimental-transform-types --conditions source ./src/lib/demos/ui/landing-page-cli.ts --mode dev --serve",
|
190
197
|
"build:demos": "zx ./scripts/build-demos.mjs",
|
191
198
|
"build:clean": "pnpm tsc --build tsconfig.build.json --clean && rm -rf build",
|
192
199
|
"build": "tsc --build tsconfig.build.json",
|
package/src/api/api.ts
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
export * as Static from './static.ts'
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import { Err, Fs, Path } from '@wollybeard/kit'
|
2
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest'
|
3
|
+
import { buildManifest, type PolenBuildManifest } from './manifest.ts'
|
4
|
+
|
5
|
+
describe('validate-build', () => {
|
6
|
+
let testDir: string
|
7
|
+
let manifestPath: string
|
8
|
+
|
9
|
+
beforeEach(async () => {
|
10
|
+
testDir = await Fs.makeTemporaryDirectory()
|
11
|
+
manifestPath = Path.join(testDir, '.polen', 'build.json')
|
12
|
+
})
|
13
|
+
|
14
|
+
afterEach(async () => {
|
15
|
+
if (testDir && await Fs.exists(testDir)) {
|
16
|
+
await Fs.remove(testDir)
|
17
|
+
}
|
18
|
+
})
|
19
|
+
|
20
|
+
describe('readBuildManifest', () => {
|
21
|
+
test('reads valid manifest', async () => {
|
22
|
+
const manifest: PolenBuildManifest = {
|
23
|
+
type: 'ssr',
|
24
|
+
version: '2.1.0',
|
25
|
+
basePath: '/docs/',
|
26
|
+
}
|
27
|
+
await Fs.write({
|
28
|
+
path: manifestPath,
|
29
|
+
content: JSON.stringify(manifest, null, 2),
|
30
|
+
})
|
31
|
+
|
32
|
+
const result = await buildManifest.read(testDir)
|
33
|
+
expect(Err.is(result)).toBe(false)
|
34
|
+
expect(result).toEqual(manifest)
|
35
|
+
})
|
36
|
+
|
37
|
+
test('returns error when manifest does not exist', async () => {
|
38
|
+
// Verify the file doesn't exist
|
39
|
+
const manifestExists = await Fs.exists(manifestPath)
|
40
|
+
expect(manifestExists).toBe(false)
|
41
|
+
|
42
|
+
const result = await buildManifest.read(testDir)
|
43
|
+
expect(Err.is(result)).toBe(true)
|
44
|
+
})
|
45
|
+
|
46
|
+
test('returns error for invalid manifest structure', async () => {
|
47
|
+
await Fs.write({
|
48
|
+
path: manifestPath,
|
49
|
+
content: JSON.stringify({ invalid: 'data' }, null, 2),
|
50
|
+
})
|
51
|
+
const result = await buildManifest.read(testDir)
|
52
|
+
expect(Err.is(result)).toBe(true)
|
53
|
+
})
|
54
|
+
|
55
|
+
test('returns error for invalid build type', async () => {
|
56
|
+
await Fs.write({
|
57
|
+
path: manifestPath,
|
58
|
+
content: JSON.stringify(
|
59
|
+
{
|
60
|
+
type: 'invalid',
|
61
|
+
version: '1.0.0',
|
62
|
+
basePath: '/',
|
63
|
+
},
|
64
|
+
null,
|
65
|
+
2,
|
66
|
+
),
|
67
|
+
})
|
68
|
+
const result = await buildManifest.read(testDir)
|
69
|
+
expect(Err.is(result)).toBe(true)
|
70
|
+
})
|
71
|
+
|
72
|
+
test('returns error when version is not a string', async () => {
|
73
|
+
await Fs.write({
|
74
|
+
path: manifestPath,
|
75
|
+
content: JSON.stringify(
|
76
|
+
{
|
77
|
+
type: 'ssg',
|
78
|
+
version: 123,
|
79
|
+
basePath: '/',
|
80
|
+
},
|
81
|
+
null,
|
82
|
+
2,
|
83
|
+
),
|
84
|
+
})
|
85
|
+
const result = await buildManifest.read(testDir)
|
86
|
+
expect(Err.is(result)).toBe(true)
|
87
|
+
})
|
88
|
+
|
89
|
+
test('returns error when basePath is not a string', async () => {
|
90
|
+
await Fs.write({
|
91
|
+
path: manifestPath,
|
92
|
+
content: JSON.stringify(
|
93
|
+
{
|
94
|
+
type: 'ssg',
|
95
|
+
version: '1.0.0',
|
96
|
+
basePath: null,
|
97
|
+
},
|
98
|
+
null,
|
99
|
+
2,
|
100
|
+
),
|
101
|
+
})
|
102
|
+
const result = await buildManifest.read(testDir)
|
103
|
+
expect(Err.is(result)).toBe(true)
|
104
|
+
})
|
105
|
+
})
|
106
|
+
})
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { Codec, Resource } from '@wollybeard/kit'
|
2
|
+
import { z } from 'zod/v4'
|
3
|
+
|
4
|
+
export const PolenBuildManifestSchema = z.object({
|
5
|
+
type: z.enum(['ssg', 'ssr']),
|
6
|
+
version: z.string(),
|
7
|
+
basePath: z.string(),
|
8
|
+
}).loose()
|
9
|
+
|
10
|
+
export type PolenBuildManifest = z.infer<typeof PolenBuildManifestSchema>
|
11
|
+
|
12
|
+
export const buildManifest = Resource.create({
|
13
|
+
name: 'polen-build-manifest',
|
14
|
+
path: '.polen/build.json',
|
15
|
+
codec: Codec.fromZod(PolenBuildManifestSchema),
|
16
|
+
})
|
@@ -0,0 +1,229 @@
|
|
1
|
+
import { Fs, Path } from '@wollybeard/kit'
|
2
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest'
|
3
|
+
import { rebase, type RebasePlan } from './rebase.ts'
|
4
|
+
|
5
|
+
const testDir = 'temp/rebase-test'
|
6
|
+
|
7
|
+
const createTestBuild = async (dir: string, basePath: string = '/') => {
|
8
|
+
await Fs.makeDirectory(dir)
|
9
|
+
|
10
|
+
// Create Polen build manifest
|
11
|
+
const polenDir = Path.join(dir, '.polen')
|
12
|
+
await Fs.makeDirectory(polenDir)
|
13
|
+
|
14
|
+
const manifest = {
|
15
|
+
type: 'ssg' as const,
|
16
|
+
version: '1.0.0',
|
17
|
+
basePath,
|
18
|
+
}
|
19
|
+
|
20
|
+
await Fs.write({
|
21
|
+
path: Path.join(polenDir, 'build.json'),
|
22
|
+
content: JSON.stringify(manifest, null, 2),
|
23
|
+
})
|
24
|
+
|
25
|
+
// Create some HTML files
|
26
|
+
await Fs.write({
|
27
|
+
path: Path.join(dir, 'index.html'),
|
28
|
+
content: `<!DOCTYPE html>
|
29
|
+
<html>
|
30
|
+
<head>
|
31
|
+
<base href="${basePath}">
|
32
|
+
<title>Test</title>
|
33
|
+
</head>
|
34
|
+
<body>
|
35
|
+
<h1>Test Page</h1>
|
36
|
+
</body>
|
37
|
+
</html>`,
|
38
|
+
})
|
39
|
+
|
40
|
+
// Create nested HTML file
|
41
|
+
const nestedDir = Path.join(dir, 'docs')
|
42
|
+
await Fs.makeDirectory(nestedDir)
|
43
|
+
|
44
|
+
await Fs.write({
|
45
|
+
path: Path.join(nestedDir, 'page.html'),
|
46
|
+
content: `<!DOCTYPE html>
|
47
|
+
<html>
|
48
|
+
<head>
|
49
|
+
<title>Nested</title>
|
50
|
+
</head>
|
51
|
+
<body>
|
52
|
+
<h1>Nested Page</h1>
|
53
|
+
</body>
|
54
|
+
</html>`,
|
55
|
+
})
|
56
|
+
}
|
57
|
+
|
58
|
+
describe('rebase', () => {
|
59
|
+
beforeEach(async () => {
|
60
|
+
await Fs.remove(testDir)
|
61
|
+
})
|
62
|
+
|
63
|
+
afterEach(async () => {
|
64
|
+
await Fs.remove(testDir)
|
65
|
+
})
|
66
|
+
|
67
|
+
test('mutate mode updates base paths in place', async () => {
|
68
|
+
const buildDir = Path.join(testDir, 'build')
|
69
|
+
await createTestBuild(buildDir, '/old/')
|
70
|
+
|
71
|
+
const plan: RebasePlan = {
|
72
|
+
sourcePath: buildDir,
|
73
|
+
newBasePath: '/new/',
|
74
|
+
changeMode: 'mutate',
|
75
|
+
}
|
76
|
+
|
77
|
+
await rebase(plan)
|
78
|
+
|
79
|
+
// Check updated manifest
|
80
|
+
const manifest = await Fs.readJson(Path.join(buildDir, '.polen', 'build.json'))
|
81
|
+
expect(manifest).toMatchObject({
|
82
|
+
type: 'ssg',
|
83
|
+
version: '1.0.0',
|
84
|
+
basePath: '/new/',
|
85
|
+
})
|
86
|
+
|
87
|
+
// Check updated HTML file
|
88
|
+
const indexContent = await Fs.read(Path.join(buildDir, 'index.html'))
|
89
|
+
expect(indexContent).toContain('<base href="/new/">')
|
90
|
+
|
91
|
+
// Check nested HTML file (should have base tag inserted)
|
92
|
+
const nestedContent = await Fs.read(Path.join(buildDir, 'docs', 'page.html'))
|
93
|
+
expect(nestedContent).toContain('<base href="/new/">')
|
94
|
+
})
|
95
|
+
|
96
|
+
test('copy mode creates new build with updated base paths', async () => {
|
97
|
+
const buildDir = Path.join(testDir, 'build')
|
98
|
+
const copyDir = Path.join(testDir, 'copy')
|
99
|
+
|
100
|
+
await createTestBuild(buildDir, '/old/')
|
101
|
+
|
102
|
+
const plan: RebasePlan = {
|
103
|
+
sourcePath: buildDir,
|
104
|
+
targetPath: copyDir,
|
105
|
+
newBasePath: '/new/',
|
106
|
+
changeMode: 'copy',
|
107
|
+
}
|
108
|
+
|
109
|
+
await rebase(plan)
|
110
|
+
|
111
|
+
// Original should be unchanged
|
112
|
+
const originalManifest = await Fs.readJson(Path.join(buildDir, '.polen', 'build.json'))
|
113
|
+
expect(originalManifest).toMatchObject({
|
114
|
+
basePath: '/old/',
|
115
|
+
})
|
116
|
+
|
117
|
+
// Copy should be updated
|
118
|
+
const copyManifest = await Fs.readJson(Path.join(copyDir, '.polen', 'build.json'))
|
119
|
+
expect(copyManifest).toMatchObject({
|
120
|
+
basePath: '/new/',
|
121
|
+
})
|
122
|
+
|
123
|
+
const copyIndexContent = await Fs.read(Path.join(copyDir, 'index.html'))
|
124
|
+
expect(copyIndexContent).toContain('<base href="/new/">')
|
125
|
+
})
|
126
|
+
|
127
|
+
test('throws error for invalid base path', async () => {
|
128
|
+
const buildDir = Path.join(testDir, 'build')
|
129
|
+
await createTestBuild(buildDir)
|
130
|
+
|
131
|
+
const plan: RebasePlan = {
|
132
|
+
sourcePath: buildDir,
|
133
|
+
newBasePath: 'invalid-path',
|
134
|
+
changeMode: 'mutate',
|
135
|
+
}
|
136
|
+
|
137
|
+
await expect(rebase(plan)).rejects.toThrow('Invalid base path: invalid-path')
|
138
|
+
})
|
139
|
+
|
140
|
+
test('throws error when source is not a Polen build', async () => {
|
141
|
+
const buildDir = Path.join(testDir, 'not-polen')
|
142
|
+
await Fs.makeDirectory(buildDir)
|
143
|
+
|
144
|
+
const plan: RebasePlan = {
|
145
|
+
sourcePath: buildDir,
|
146
|
+
newBasePath: '/new/',
|
147
|
+
changeMode: 'mutate',
|
148
|
+
}
|
149
|
+
|
150
|
+
await expect(rebase(plan)).rejects.toThrow('Polen build manifest not found')
|
151
|
+
})
|
152
|
+
|
153
|
+
test('throws error when copy target exists and is not empty', async () => {
|
154
|
+
const buildDir = Path.join(testDir, 'build')
|
155
|
+
const copyDir = Path.join(testDir, 'copy')
|
156
|
+
|
157
|
+
await createTestBuild(buildDir)
|
158
|
+
|
159
|
+
// Create non-empty target
|
160
|
+
await Fs.makeDirectory(copyDir)
|
161
|
+
await Fs.write({
|
162
|
+
path: Path.join(copyDir, 'existing.txt'),
|
163
|
+
content: 'existing file',
|
164
|
+
})
|
165
|
+
|
166
|
+
const plan: RebasePlan = {
|
167
|
+
sourcePath: buildDir,
|
168
|
+
targetPath: copyDir,
|
169
|
+
newBasePath: '/new/',
|
170
|
+
changeMode: 'copy',
|
171
|
+
}
|
172
|
+
|
173
|
+
await expect(rebase(plan)).rejects.toThrow('Target path already exists and is not empty')
|
174
|
+
})
|
175
|
+
|
176
|
+
test('handles HTML file without existing base tag', async () => {
|
177
|
+
const buildDir = Path.join(testDir, 'build')
|
178
|
+
await createTestBuild(buildDir)
|
179
|
+
|
180
|
+
// Create HTML without base tag
|
181
|
+
await Fs.write({
|
182
|
+
path: Path.join(buildDir, 'no-base.html'),
|
183
|
+
content: `<!DOCTYPE html>
|
184
|
+
<html>
|
185
|
+
<head>
|
186
|
+
<title>No Base</title>
|
187
|
+
</head>
|
188
|
+
<body>
|
189
|
+
<h1>No Base Tag</h1>
|
190
|
+
</body>
|
191
|
+
</html>`,
|
192
|
+
})
|
193
|
+
|
194
|
+
const plan: RebasePlan = {
|
195
|
+
sourcePath: buildDir,
|
196
|
+
newBasePath: '/new/',
|
197
|
+
changeMode: 'mutate',
|
198
|
+
}
|
199
|
+
|
200
|
+
await rebase(plan)
|
201
|
+
|
202
|
+
const content = await Fs.read(Path.join(buildDir, 'no-base.html'))
|
203
|
+
expect(content).toContain('<base href="/new/">')
|
204
|
+
})
|
205
|
+
|
206
|
+
test('throws error for HTML file without head tag', async () => {
|
207
|
+
const buildDir = Path.join(testDir, 'build')
|
208
|
+
await createTestBuild(buildDir)
|
209
|
+
|
210
|
+
// Create invalid HTML without head
|
211
|
+
await Fs.write({
|
212
|
+
path: Path.join(buildDir, 'invalid.html'),
|
213
|
+
content: `<!DOCTYPE html>
|
214
|
+
<html>
|
215
|
+
<body>
|
216
|
+
<h1>Invalid HTML</h1>
|
217
|
+
</body>
|
218
|
+
</html>`,
|
219
|
+
})
|
220
|
+
|
221
|
+
const plan: RebasePlan = {
|
222
|
+
sourcePath: buildDir,
|
223
|
+
newBasePath: '/new/',
|
224
|
+
changeMode: 'mutate',
|
225
|
+
}
|
226
|
+
|
227
|
+
await expect(rebase(plan)).rejects.toThrow('Could not find <head> tag in HTML file')
|
228
|
+
})
|
229
|
+
})
|
@@ -0,0 +1,140 @@
|
|
1
|
+
import { TinyGlobby } from '#dep/tiny-globby/index'
|
2
|
+
import { Fs, Path } from '@wollybeard/kit'
|
3
|
+
import { Err } from '@wollybeard/kit'
|
4
|
+
import { buildManifest, type PolenBuildManifest } from './manifest.ts'
|
5
|
+
|
6
|
+
export type RebasePlan = RebaseOverwritePlan | RebaseCopyPlan
|
7
|
+
|
8
|
+
export interface RebaseOverwritePlan {
|
9
|
+
changeMode: 'mutate'
|
10
|
+
newBasePath: string
|
11
|
+
sourcePath: string
|
12
|
+
}
|
13
|
+
|
14
|
+
export interface RebaseCopyPlan {
|
15
|
+
changeMode: 'copy'
|
16
|
+
newBasePath: string
|
17
|
+
sourcePath: string
|
18
|
+
targetPath: string
|
19
|
+
}
|
20
|
+
|
21
|
+
export const rebase = async (plan: RebasePlan): Promise<void> => {
|
22
|
+
// 1. Validate source is a Polen build
|
23
|
+
const manifestResult = await buildManifest.read(plan.sourcePath)
|
24
|
+
if (Err.is(manifestResult)) {
|
25
|
+
throw new Error(`Polen build manifest not found at: ${Path.join(plan.sourcePath, '.polen', 'build.json')}`)
|
26
|
+
}
|
27
|
+
const manifest = manifestResult
|
28
|
+
|
29
|
+
// 2. Validate newBasePath is valid URL path
|
30
|
+
if (!isValidUrlPath(plan.newBasePath)) {
|
31
|
+
throw new Error(`Invalid base path: ${plan.newBasePath}`)
|
32
|
+
}
|
33
|
+
|
34
|
+
// 3. Handle copy vs mutate
|
35
|
+
let workingPath: string
|
36
|
+
if (plan.changeMode === 'copy') {
|
37
|
+
if (await Fs.exists(plan.targetPath)) {
|
38
|
+
const isEmpty = await Fs.isEmptyDir(plan.targetPath)
|
39
|
+
if (!isEmpty) {
|
40
|
+
throw new Error(`Target path already exists and is not empty: ${plan.targetPath}`)
|
41
|
+
}
|
42
|
+
}
|
43
|
+
await Fs.copyDir({ from: plan.sourcePath, to: plan.targetPath })
|
44
|
+
workingPath = plan.targetPath
|
45
|
+
} else {
|
46
|
+
workingPath = plan.sourcePath
|
47
|
+
}
|
48
|
+
|
49
|
+
// 4. Update HTML files with new base path
|
50
|
+
await updateHtmlFiles(workingPath, manifest.basePath, plan.newBasePath)
|
51
|
+
|
52
|
+
// 5. Update manifest
|
53
|
+
await updateManifest(workingPath, { basePath: plan.newBasePath })
|
54
|
+
}
|
55
|
+
|
56
|
+
//
|
57
|
+
//
|
58
|
+
//
|
59
|
+
//
|
60
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ • Local Helpers
|
61
|
+
//
|
62
|
+
//
|
63
|
+
|
64
|
+
// TODO: this is very generic, factor out to kit-temp
|
65
|
+
const isValidUrlPath = (path: string): boolean => {
|
66
|
+
// URL path should start with / and not contain invalid characters
|
67
|
+
if (!path.startsWith('/')) return false
|
68
|
+
if (!path.endsWith('/')) return false
|
69
|
+
|
70
|
+
// Basic validation - no spaces, proper URL characters
|
71
|
+
const urlPathRegex = /^\/[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=%]*\/$/
|
72
|
+
return urlPathRegex.test(path)
|
73
|
+
}
|
74
|
+
|
75
|
+
const updateHtmlFiles = async (buildPath: string, oldBasePath: string, newBasePath: string): Promise<void> => {
|
76
|
+
// Find all HTML files recursively
|
77
|
+
const htmlFiles = await findHtmlFiles(buildPath)
|
78
|
+
|
79
|
+
for (const htmlFile of htmlFiles) {
|
80
|
+
await updateHtmlFile(htmlFile, oldBasePath, newBasePath)
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
const findHtmlFiles = async (dir: string): Promise<string[]> => {
|
85
|
+
return await TinyGlobby.glob('**/*.html', {
|
86
|
+
absolute: true,
|
87
|
+
cwd: dir,
|
88
|
+
onlyFiles: true,
|
89
|
+
})
|
90
|
+
}
|
91
|
+
|
92
|
+
const updateHtmlFile = async (filePath: string, oldBasePath: string, newBasePath: string): Promise<void> => {
|
93
|
+
const content = await Fs.read(filePath)
|
94
|
+
|
95
|
+
if (content === null) {
|
96
|
+
throw new Error(`Could not read HTML file: ${filePath}`)
|
97
|
+
}
|
98
|
+
|
99
|
+
// Simple regex-based approach to update base tag
|
100
|
+
// Look for existing base tag first
|
101
|
+
const baseTagRegex = /<base\s+href\s*=\s*["']([^"']*)["'][^>]*>/i
|
102
|
+
|
103
|
+
let updatedContent: string
|
104
|
+
|
105
|
+
if (baseTagRegex.test(content)) {
|
106
|
+
// Update existing base tag
|
107
|
+
updatedContent = content.replace(baseTagRegex, `<base href="${newBasePath}">`)
|
108
|
+
} else {
|
109
|
+
// Insert new base tag in head
|
110
|
+
const headRegex = /<head[^>]*>/i
|
111
|
+
const headMatch = content.match(headRegex)
|
112
|
+
|
113
|
+
if (headMatch) {
|
114
|
+
const insertPosition = headMatch.index! + headMatch[0].length
|
115
|
+
updatedContent = content.slice(0, insertPosition)
|
116
|
+
+ `\n <base href="${newBasePath}">`
|
117
|
+
+ content.slice(insertPosition)
|
118
|
+
} else {
|
119
|
+
throw new Error(`Could not find <head> tag in HTML file: ${filePath}`)
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
await Fs.write({ path: filePath, content: updatedContent })
|
124
|
+
}
|
125
|
+
|
126
|
+
const updateManifest = async (buildPath: string, updates: Partial<PolenBuildManifest>): Promise<void> => {
|
127
|
+
const manifestPath = Path.join(buildPath, '.polen', 'build.json')
|
128
|
+
const manifestResult = await buildManifest.read(buildPath)
|
129
|
+
if (Err.is(manifestResult)) {
|
130
|
+
throw new Error(`Polen build manifest not found at: ${manifestPath}`)
|
131
|
+
}
|
132
|
+
const currentManifest = manifestResult
|
133
|
+
|
134
|
+
const updatedManifest = { ...currentManifest, ...updates }
|
135
|
+
|
136
|
+
await Fs.write({
|
137
|
+
path: manifestPath,
|
138
|
+
content: JSON.stringify(updatedManifest, null, 2),
|
139
|
+
})
|
140
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { describe, expect, test } from 'vitest'
|
2
|
-
import { assetUrl, faviconUrl, joinPaths, pageUrl } from './asset-url.
|
2
|
+
import { assetUrl, faviconUrl, joinPaths, pageUrl } from './asset-url.ts'
|
3
3
|
|
4
4
|
describe('asset-url helpers', () => {
|
5
5
|
describe('joinPaths', () => {
|
@@ -20,9 +20,9 @@ describe('asset-url helpers', () => {
|
|
20
20
|
|
21
21
|
describe('assetUrl', () => {
|
22
22
|
test('creates asset URLs with base path', () => {
|
23
|
-
expect(assetUrl('assets/app.
|
24
|
-
expect(assetUrl('assets/app.
|
25
|
-
expect(assetUrl('/assets/app.
|
23
|
+
expect(assetUrl('assets/app.ts', '/')).toBe('/assets/app.ts')
|
24
|
+
expect(assetUrl('assets/app.ts', '/my-app/')).toBe('/my-app/assets/app.ts')
|
25
|
+
expect(assetUrl('/assets/app.ts', '/my-app/')).toBe('/my-app/assets/app.ts')
|
26
26
|
})
|
27
27
|
})
|
28
28
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
import type { Config } from '#api/config/index'
|
2
|
+
import type { PolenBuildManifest } from '#api/static/manifest'
|
2
3
|
import { Vite } from '#dep/vite/index'
|
3
4
|
import { ViteVirtual } from '#lib/vite-virtual/index'
|
4
5
|
import { Fs, Path } from '@wollybeard/kit'
|
6
|
+
import packageJson from '../../../../package.json' with { type: 'json' }
|
5
7
|
import { isKitUnusedExternalImport, isRadixModuleLevelDirective } from '../log-filters.ts'
|
6
8
|
import { polenVirtual } from '../vi.ts'
|
7
9
|
|
@@ -10,7 +12,7 @@ export const Build = (config: Config.Config): Vite.Plugin[] => {
|
|
10
12
|
|
11
13
|
// const outDir = Path.join(config.paths.project.rootDir, `dist`)
|
12
14
|
|
13
|
-
return [Manifest(config), {
|
15
|
+
return [Manifest(config), BuildManifest(config), {
|
14
16
|
name: `polen:build-client`,
|
15
17
|
apply: `build`,
|
16
18
|
applyToEnvironment: Vite.isEnvironmentClient,
|
@@ -144,3 +146,25 @@ const Manifest = (config: Config.Config): Vite.Plugin => {
|
|
144
146
|
),
|
145
147
|
}
|
146
148
|
}
|
149
|
+
|
150
|
+
const BuildManifest = (config: Config.Config): Vite.Plugin => {
|
151
|
+
return {
|
152
|
+
name: 'polen:build-manifest',
|
153
|
+
apply: 'build',
|
154
|
+
applyToEnvironment: Vite.isEnvironmentClient,
|
155
|
+
async generateBundle() {
|
156
|
+
const manifest: PolenBuildManifest = {
|
157
|
+
type: config.build.architecture === 'ssr' ? 'ssr' : 'ssg',
|
158
|
+
version: packageJson.version,
|
159
|
+
basePath: config.build.base || '/',
|
160
|
+
}
|
161
|
+
|
162
|
+
// Emit the manifest as an asset
|
163
|
+
this.emitFile({
|
164
|
+
type: 'asset',
|
165
|
+
fileName: '.polen/build.json',
|
166
|
+
source: JSON.stringify(manifest, null, 2),
|
167
|
+
})
|
168
|
+
},
|
169
|
+
}
|
170
|
+
}
|
@@ -9,7 +9,7 @@ import { ViteVirtual } from '#lib/vite-virtual/index'
|
|
9
9
|
import { debug } from '#singletons/debug'
|
10
10
|
import { superjson } from '#singletons/superjson'
|
11
11
|
import { Json, Str } from '@wollybeard/kit'
|
12
|
-
import type { ProjectData } from '../../../project-data.
|
12
|
+
import type { ProjectData } from '../../../project-data.ts'
|
13
13
|
import { SchemaAugmentation } from '../../schema-augmentation/index.ts'
|
14
14
|
import { Schema } from '../../schema/index.ts'
|
15
15
|
import { createLogger } from '../logger.ts'
|