@bakery-framework/plugin-vue 2.0.0-alpha.1 → 2.0.0-alpha.12
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 +3 -3
- package/src/chunks.ts +6 -1
- package/src/client.ts +5 -3
- package/src/handler.ts +68 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakery-framework/plugin-vue",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.12",
|
|
4
4
|
"description": "Bakery vue plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bakery",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"vue": "^3.5.38"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bakery-framework/core": "^
|
|
46
|
+
"@bakery-framework/core": "^2.0.0-alpha.12"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
|
-
"bun": ">=1.
|
|
49
|
+
"bun": ">=1.4.0"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/chunks.ts
CHANGED
|
@@ -66,7 +66,12 @@ export async function serveVueChunk(
|
|
|
66
66
|
entrypoints: [sourcePath],
|
|
67
67
|
target: 'browser',
|
|
68
68
|
format: 'esm',
|
|
69
|
-
|
|
69
|
+
// `Boolean(...)`, not the flag directly: the mode flags are `'1'`/`''`
|
|
70
|
+
// strings (`core/init.ts`), and `Bun.build` **rejects** a non-boolean
|
|
71
|
+
// `minify` rather than coercing it. Core's `compiler.ts` already wrapped
|
|
72
|
+
// its three; this one did not, and the build would have thrown on every
|
|
73
|
+
// production SFC chunk.
|
|
74
|
+
minify: Boolean(import.meta.env.PROD),
|
|
70
75
|
define: BUNDLE_DEFINES,
|
|
71
76
|
})
|
|
72
77
|
|
package/src/client.ts
CHANGED
|
@@ -31,9 +31,11 @@ type StampedRoute = {
|
|
|
31
31
|
/** The catch-all's param name (`slug` in `[...slug!]`). */
|
|
32
32
|
param: string | null
|
|
33
33
|
/**
|
|
34
|
-
* First path segments the catch-all's *
|
|
35
|
-
* `faculty/[id].vue` sits beside the catch-all. Those URLs belong to
|
|
36
|
-
* specific routes, so they get real navigations, not soft ones.
|
|
34
|
+
* First path segments the catch-all's sibling *routes* claim — `faculty`
|
|
35
|
+
* when `faculty/[id].vue` sits beside the catch-all. Those URLs belong to
|
|
36
|
+
* more specific routes, so they get real navigations, not soft ones. Route
|
|
37
|
+
* names only, and only on catch-all pages: the stamp is readable by every
|
|
38
|
+
* visitor, so it must never be a directory listing.
|
|
37
39
|
*/
|
|
38
40
|
claimed?: string[]
|
|
39
41
|
/** True when a `[param]` sibling claims every single-segment path. */
|
package/src/handler.ts
CHANGED
|
@@ -113,7 +113,36 @@ function findLayoutRoute(filePath: string, meta: VueMeta): string | null {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
116
|
+
* Does `dir` hold a file this handler routes, at any depth? A sibling
|
|
117
|
+
* directory claims its first segment only when it does — the claim exists for
|
|
118
|
+
* `faculty/[id].vue`-shaped subtrees, and a directory of assets or helpers
|
|
119
|
+
* routes nowhere more specific than the catch-all, so stamping its name would
|
|
120
|
+
* disclose it for no navigational gain. Files at each level are checked
|
|
121
|
+
* before any subdirectory is entered, so the common shallow layout answers
|
|
122
|
+
* without recursing. `Dirent.isDirectory()` is false for symlinks, which is
|
|
123
|
+
* what keeps the walk from cycling.
|
|
124
|
+
*/
|
|
125
|
+
function containsRouteFile(dir: string, exts: string[]): boolean {
|
|
126
|
+
let entries: import('node:fs').Dirent[]
|
|
127
|
+
try {
|
|
128
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
129
|
+
} catch {
|
|
130
|
+
// Unreadable: treated as holding no routes — same reasoning as the catch
|
|
131
|
+
// in claimedBeside below.
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const dirs: string[] = []
|
|
136
|
+
for (const entry of entries) {
|
|
137
|
+
if (entry.name.startsWith('.')) continue
|
|
138
|
+
if (entry.isDirectory()) dirs.push(`${dir}/${entry.name}`)
|
|
139
|
+
else if (exts.some(ext => entry.name.endsWith(ext))) return true
|
|
140
|
+
}
|
|
141
|
+
return dirs.some(sub => containsRouteFile(sub, exts))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* What the catch-all's sibling *routes* claim, for the `defineLayout()` stamp.
|
|
117
146
|
*
|
|
118
147
|
* A catch-all owns only *what nothing else claims*: with
|
|
119
148
|
* `admin/[...slug].vue` beside `admin/faculty/[id].vue`, the URL
|
|
@@ -128,6 +157,22 @@ function findLayoutRoute(filePath: string, meta: VueMeta): string | null {
|
|
|
128
157
|
* path, which is what `claimedSingle` carries. `layout.vue` claims nothing (it
|
|
129
158
|
* is not routable), and the catch-all file itself is the page being served.
|
|
130
159
|
*
|
|
160
|
+
* Only entries the handler's own extension table routes are claims. The stamp
|
|
161
|
+
* is serialized into the HTML of every served page, so each name in it is
|
|
162
|
+
* published to any visitor — and this function used to list *every* sibling
|
|
163
|
+
* stem, which put non-route file names (`sample.bin`, `script.ts`,
|
|
164
|
+
* `index.tsx`) from the source directory into production responses: a
|
|
165
|
+
* directory listing of `src/`, observed in a smoke test of the published
|
|
166
|
+
* alpha. A file sibling therefore counts only with a routed extension, and a
|
|
167
|
+
* directory sibling only when a route file exists somewhere under it.
|
|
168
|
+
*
|
|
169
|
+
* The boundary that filter accepts: a *non-route* file under the base is
|
|
170
|
+
* served by core's real-file-beats-catch-all rule (`findDynamicRoute`), and
|
|
171
|
+
* the stamp no longer names it, so a plain anchor to one soft-navigates into
|
|
172
|
+
* the catch-all's view. An anchor carrying `target` or `download` is never
|
|
173
|
+
* intercepted — that is the spelling for linking a raw file out of a
|
|
174
|
+
* catch-all's subtree, and what `docs/plugins/vue.md` prescribes.
|
|
175
|
+
*
|
|
131
176
|
* Computed per page request, so files added or removed in dev are seen on the
|
|
132
177
|
* next load without cache ceremony.
|
|
133
178
|
*/
|
|
@@ -140,6 +185,9 @@ export function claimedBeside(catchAllFile: string): {
|
|
|
140
185
|
|
|
141
186
|
const dir = fs.resolve(catchAllFile).replace(/\/[^/]*$/, '')
|
|
142
187
|
const self = fs.resolve(catchAllFile).slice(dir.length + 1)
|
|
188
|
+
// The handler's own table (`['vue']`), read at call time so the two cannot
|
|
189
|
+
// drift apart.
|
|
190
|
+
const exts = VueHandler.config.ext.map(ext => `.${ext}`)
|
|
143
191
|
|
|
144
192
|
let entries: import('node:fs').Dirent[]
|
|
145
193
|
try {
|
|
@@ -157,6 +205,12 @@ export function claimedBeside(catchAllFile: string): {
|
|
|
157
205
|
if (name.startsWith('.')) continue
|
|
158
206
|
|
|
159
207
|
if (RX_CATCHALL.test(name) || RX_OPT_CATCHALL.test(name)) continue
|
|
208
|
+
|
|
209
|
+
const isRoute = entry.isDirectory()
|
|
210
|
+
? containsRouteFile(`${dir}/${name}`, exts)
|
|
211
|
+
: exts.some(ext => name.endsWith(ext))
|
|
212
|
+
if (!isRoute) continue
|
|
213
|
+
|
|
160
214
|
if (RX_DYNAMIC.test(name)) {
|
|
161
215
|
claimedSingle = true
|
|
162
216
|
continue
|
|
@@ -460,7 +514,13 @@ export class VueHandler extends DynamicHandler {
|
|
|
460
514
|
routePath: string,
|
|
461
515
|
serverParams: any,
|
|
462
516
|
parsed: ParsedCacheEntry,
|
|
463
|
-
route?: {
|
|
517
|
+
route?: {
|
|
518
|
+
catchAll: boolean
|
|
519
|
+
base: string
|
|
520
|
+
param: string | null
|
|
521
|
+
claimed?: string[]
|
|
522
|
+
claimedSingle?: boolean
|
|
523
|
+
},
|
|
464
524
|
) {
|
|
465
525
|
const { hasCss, serverScript } = parsed
|
|
466
526
|
const hasServerData =
|
|
@@ -724,6 +784,7 @@ async function sharedHandler(
|
|
|
724
784
|
// `base` is the URL prefix the page owns: the file's directory. For
|
|
725
785
|
// `wiki/[...page!].vue` that is `/wiki`; for a root-level catch-all it is
|
|
726
786
|
// the empty string, which `defineLayout` treats as "everything".
|
|
787
|
+
const catchAll = Boolean(info.catchAll)
|
|
727
788
|
return VueHandler.handleHtml(
|
|
728
789
|
id,
|
|
729
790
|
finalParams,
|
|
@@ -731,10 +792,13 @@ async function sharedHandler(
|
|
|
731
792
|
serverParams,
|
|
732
793
|
parsed,
|
|
733
794
|
{
|
|
734
|
-
catchAll
|
|
795
|
+
catchAll,
|
|
735
796
|
base: routePath.slice(0, routePath.lastIndexOf('/')),
|
|
736
797
|
param: info.params.length ? info.params[info.params.length - 1] : null,
|
|
737
|
-
|
|
798
|
+
// Catch-all pages only: `defineLayout()` refuses every other page, so
|
|
799
|
+
// on those the claims would be sibling names published in the HTML with
|
|
800
|
+
// no reader. Skipping the stamp also skips the directory scan.
|
|
801
|
+
...(catchAll ? claimedBeside(diskFile.name ?? '') : null),
|
|
738
802
|
},
|
|
739
803
|
)
|
|
740
804
|
}
|