@bakery-framework/plugin-vue 2.1.0 → 2.1.2
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 +2 -2
- package/src/handler.ts +29 -0
- package/src/utils.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakery-framework/plugin-vue",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Bakery vue plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bakery",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"vue": "^3.5.38"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bakery-framework/core": "^2.1.
|
|
46
|
+
"@bakery-framework/core": "^2.1.2"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"bun": ">=1.4.0"
|
package/src/handler.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
JsonResponseData,
|
|
16
16
|
response,
|
|
17
17
|
toHash,
|
|
18
|
+
Try,
|
|
18
19
|
} from '@bakery-framework/core/utils'
|
|
19
20
|
import { ETag, injectIfHtml } from '@bakery-framework/core/utils/http'
|
|
20
21
|
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
parseVueMeta,
|
|
45
46
|
RX_EXPORT_BRACE,
|
|
46
47
|
RX_EXPORT_HANGING,
|
|
48
|
+
absolutizeRelativeImports,
|
|
47
49
|
rewriteVueImports,
|
|
48
50
|
VUE_SERVER_DATA_TOKEN,
|
|
49
51
|
} from './utils'
|
|
@@ -52,6 +54,16 @@ function normalizePath(path: string) {
|
|
|
52
54
|
return path.endsWith('.js') ? path.slice(0, -3) : path
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Whether a request names the route's file itself, rather than reaching it
|
|
59
|
+
* through a route parameter. Decoded, because an import of a bracketed file
|
|
60
|
+
* arrives percent-encoded (`absolutizeRelativeImports`).
|
|
61
|
+
*/
|
|
62
|
+
function requestNamesFile(path: string, routePath: string): boolean {
|
|
63
|
+
const requested = Try.return(() => decodeURIComponent(path), path)
|
|
64
|
+
return requested === routePath.replace(/\\/g, '/')
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
const RX_SERVER_DATA_TOKEN = new RegExp(`\\b${VUE_SERVER_DATA_TOKEN}\\b`, 'g')
|
|
56
68
|
|
|
57
69
|
/**
|
|
@@ -434,6 +446,10 @@ export class VueHandler extends DynamicHandler {
|
|
|
434
446
|
if (code.includes('.vue"') || code.includes(".vue'")) {
|
|
435
447
|
code = rewriteVueImports(code)
|
|
436
448
|
}
|
|
449
|
+
// After the `.vue` rewrite, which only appends a query and leaves the
|
|
450
|
+
// specifier relative. See `absolutizeRelativeImports` for why relative
|
|
451
|
+
// imports cannot survive into the browser.
|
|
452
|
+
code = absolutizeRelativeImports(code, routePath)
|
|
437
453
|
|
|
438
454
|
if (serverDataReplacement !== undefined) {
|
|
439
455
|
code = code.replace(RX_SERVER_DATA_TOKEN, () => serverDataReplacement)
|
|
@@ -726,6 +742,19 @@ async function sharedHandler(
|
|
|
726
742
|
accept.includes('text/javascript') ||
|
|
727
743
|
req.headers.get('sec-fetch-dest') === 'script'
|
|
728
744
|
|
|
745
|
+
// An import of some other file that reached this page only through a route
|
|
746
|
+
// parameter: `/admin/faculty/pages/dashboard.vue?__vue_script=module`
|
|
747
|
+
// matched by `admin/[...slug].vue`. It was answered with this page's own
|
|
748
|
+
// script, a 200 the browser took for the module it asked for, and that is
|
|
749
|
+
// what kept a whole class of wrong import URLs out of sight. Refused now.
|
|
750
|
+
// The page's own root script and stylesheet still answer at its URL: that
|
|
751
|
+
// is where 2.0.4 links them (`__vue_script=root`, `__vue_css`).
|
|
752
|
+
const isImport =
|
|
753
|
+
vueScriptParam === 'module' || (vueScriptParam === null && isScript)
|
|
754
|
+
if (isImport && !requestNamesFile(path, routePath)) {
|
|
755
|
+
return response.error('Not Found', 404)
|
|
756
|
+
}
|
|
757
|
+
|
|
729
758
|
const parsed = await VueHandler.parseVueFile(
|
|
730
759
|
id,
|
|
731
760
|
diskFile,
|
package/src/utils.ts
CHANGED
|
@@ -635,6 +635,35 @@ export async function getServerResponse(options: ServerResponseOptions) {
|
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
637
|
|
|
638
|
+
/** A relative specifier in a static import, an export-from or a dynamic import. */
|
|
639
|
+
const RX_RELATIVE_SPECIFIER =
|
|
640
|
+
/((?:\bfrom|\bimport)\s*\(?\s*)(['"])(\.{1,2}\/[^'"]*)\2/g
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Relative specifiers made absolute from the file's own place under the
|
|
644
|
+
* serve root, so a script resolves the same however deep the URL it was
|
|
645
|
+
* served from.
|
|
646
|
+
*
|
|
647
|
+
* A browser resolves a relative import against the importing script's URL.
|
|
648
|
+
* Since 2.0.4 a dynamic page's root script is served at the page's URL, which
|
|
649
|
+
* shares the source file's directory only one segment deep: at
|
|
650
|
+
* `/admin/faculty/add` the root script of `admin/[...slug].vue` resolved
|
|
651
|
+
* `../shared/usePath` to `/admin/shared/usePath`. Absolute, every file has one
|
|
652
|
+
* URL, which is also what keeps a module from loading twice under two names.
|
|
653
|
+
*
|
|
654
|
+
* Brackets are percent-encoded: a requested name is read as a glob, where a
|
|
655
|
+
* bare `[id]` is a character class and never matches the file of that name.
|
|
656
|
+
*/
|
|
657
|
+
export function absolutizeRelativeImports(code: string, routePath: string): string {
|
|
658
|
+
const base = `http://bakery.invalid${routePath.replace(/\\/g, '/')}`
|
|
659
|
+
return code.replace(RX_RELATIVE_SPECIFIER, (match, lead: string, quote: string, spec: string) => {
|
|
660
|
+
const url = Try.return(() => new URL(spec, base), null)
|
|
661
|
+
if (!url) return match
|
|
662
|
+
const path = url.pathname.replaceAll('[', '%5B').replaceAll(']', '%5D')
|
|
663
|
+
return `${lead}${quote}${path}${url.search}${url.hash}${quote}`
|
|
664
|
+
})
|
|
665
|
+
}
|
|
666
|
+
|
|
638
667
|
export function rewriteVueImports(code: string): string {
|
|
639
668
|
return code.replace(
|
|
640
669
|
RX_IMPORT_VUE_FILE,
|