blogger-plugin 0.0.1 → 0.0.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/README.md +148 -1
- package/dist/vite.cjs +12 -12
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +12 -12
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,150 @@
|
|
|
1
1
|
# Blogger Plugin
|
|
2
2
|
|
|
3
|
-
A plugin
|
|
3
|
+
A plugin that allows you to use modern frontend frameworks inside a Blogger template.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- ✅ Supports **all major frontend frameworks** supported by Vite — including **React, Vue, Svelte, Solid**, and more.
|
|
8
|
+
- 🔄 Enables **local development** by proxying unhandled requests to a Blogger blog.
|
|
9
|
+
- 🧩 Works seamlessly with Vite’s dev server and build system.
|
|
10
|
+
|
|
11
|
+
## 📦 Installation
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
npm install blogger-plugin
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## ⚡ Usage with Vite
|
|
18
|
+
|
|
19
|
+
**1**. **Create a new Blogger blog** for development and preview purposes.
|
|
20
|
+
This blog will be used as a proxy target for unhandled requests during local development.
|
|
21
|
+
|
|
22
|
+
**2**. **Create a new Vite project** using your preferred frontend framework (React, Vue, Svelte, etc.):
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
npm create vite
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**3**. **Create a Blogger XML template file**
|
|
29
|
+
Inside your project's `src` directory, create a new file named `template.xml`.
|
|
30
|
+
|
|
31
|
+
**Head section**
|
|
32
|
+
|
|
33
|
+
Add the following code inside the `<head>` section of your Blogger template:
|
|
34
|
+
|
|
35
|
+
```xml
|
|
36
|
+
<b:if cond='data:blog.view contains "-DevServer" or data:blog.view contains "-PreviewServer"'>
|
|
37
|
+
<!--blogger-plugin:head:begin--><!--blogger-plugin:head:end-->
|
|
38
|
+
|
|
39
|
+
<b:else/>
|
|
40
|
+
<b:comment><!--blogger-plugin:head:begin--></b:comment><b:comment><!--blogger-plugin:head:end--></b:comment>
|
|
41
|
+
</b:if>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This snippet ensures that **development and preview HTML tags** are correctly injected into the HTML content from the proxied blog, while **production HTML** tags are injected into the XML content.
|
|
45
|
+
|
|
46
|
+
**Body section**
|
|
47
|
+
|
|
48
|
+
Inside the `<body>`, add a root container for your frontend app:
|
|
49
|
+
|
|
50
|
+
```xml
|
|
51
|
+
<div id='root'></div>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
or, depending on your framework:
|
|
55
|
+
|
|
56
|
+
```xml
|
|
57
|
+
<div id='app'></div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This element will serve as the mounting point for your frontend framework.
|
|
61
|
+
|
|
62
|
+
**4**. **Add the template to your proxy Blogger blog**
|
|
63
|
+
Open your proxy blog (the one used for local development), go to **Dashboard** → **Theme** → **Edit HTML**, and replace its contents with the template from your project's `template.xml` file.
|
|
64
|
+
|
|
65
|
+
This ensures that the Blogger Plugin can inject your app's assets during development and preview phases.
|
|
66
|
+
|
|
67
|
+
**5**. **Add the Blogger plugin** to your Vite configuration file (i.e. `vite.config.ts`, `vite.config.js`, etc.):
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import react from "@vitejs/plugin-react-swc";
|
|
71
|
+
import blogger from "blogger-plugin/vite";
|
|
72
|
+
import { defineConfig } from "vite";
|
|
73
|
+
|
|
74
|
+
// https://vite.dev/config/
|
|
75
|
+
export default defineConfig({
|
|
76
|
+
plugins: [
|
|
77
|
+
react(),
|
|
78
|
+
blogger({
|
|
79
|
+
// Unhandled requests will be proxied to this Blogger blog
|
|
80
|
+
proxyBlog: "https://example.blogspot.com",
|
|
81
|
+
|
|
82
|
+
// (optional) Custom entry file path
|
|
83
|
+
// Defaults to one of: src/{index|main}.{tsx|ts|jsx|js}
|
|
84
|
+
// entry: "src/my-entry.ts",
|
|
85
|
+
|
|
86
|
+
// (optional) Custom Blogger XML template path
|
|
87
|
+
// Defaults to one of: src/{template|theme}.xml
|
|
88
|
+
// template: "src/my-template.xml",
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**6**. **Start the development server**
|
|
95
|
+
|
|
96
|
+
```shell
|
|
97
|
+
npm run dev
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**7**. **Modify the template during development**
|
|
101
|
+
|
|
102
|
+
During development, you can **freely edit the XML** directly from the Blogger dashboard.
|
|
103
|
+
The local dev server proxies all requests to your live blog and dynamically injects the latest HTML output, so you can preview live updates instantly.
|
|
104
|
+
|
|
105
|
+
> [!TIP]
|
|
106
|
+
> Once you're satisfied with the final template, copy the **dashboard XML** back into your project's `template.xml` before running your production build.
|
|
107
|
+
|
|
108
|
+
**8**. **Build for production**
|
|
109
|
+
When you’re ready to build, run:
|
|
110
|
+
|
|
111
|
+
```shell
|
|
112
|
+
npm run build
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
After the build completes, a `template.xml` file will be generated inside your project's **output directory** (`outDir`), containing all **injected asset tags** (CSS, JS, and other resources) required by your framework.
|
|
116
|
+
|
|
117
|
+
You can then upload this generated XML to your main Blogger blog via **Theme** → **Edit HTML**.
|
|
118
|
+
|
|
119
|
+
## ☁️ Hosting Assets on a CDN
|
|
120
|
+
|
|
121
|
+
Since **Blogger doesn't allow** hosting custom static assets, you'll need to serve your built files (JS, CSS, images) from a third-party CDN, such as [jsDelivr](https://www.jsdelivr.com/), GitHub Pages, or Cloudflare Workers Static Assets.
|
|
122
|
+
|
|
123
|
+
To configure your asset URLs, specify a `base` path in your Vite config — for example:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
export default defineConfig({
|
|
127
|
+
base: process.env.VITE_BASE ?? "/",
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then, set the environment variable when building for production:
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
VITE_BASE="https://cdn.jsdelivr.net/gh/username/repo@version/" pnpm run build
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This ensures all injected asset tags inside the generated `template.xml` point to your CDN-hosted files.
|
|
138
|
+
|
|
139
|
+
## 📌 Example
|
|
140
|
+
|
|
141
|
+
A fully working example using **React**, **jsDelivr**, and **GitHub Actions** is available in this GitHub repository:
|
|
142
|
+
|
|
143
|
+
https://github.com/kumardeo/react-blogger-template
|
|
144
|
+
|
|
145
|
+
The GitHub Actions workflow automatically:
|
|
146
|
+
|
|
147
|
+
1. Runs on commits to the `release` branch.
|
|
148
|
+
2. Builds the React app and generates `dist/template.xml`.
|
|
149
|
+
3. Commits all built assets and `template.xml` to the `static` branch.
|
|
150
|
+
4. Creates a new tag for jsDelivr so assets can be served via CDN.
|
package/dist/vite.cjs
CHANGED
|
@@ -209,7 +209,7 @@ function useServerMiddleware(server, ctx, _this) {
|
|
|
209
209
|
const start = Date.now();
|
|
210
210
|
const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);
|
|
211
211
|
const viewParam = proxyUrl.searchParams.get("view");
|
|
212
|
-
proxyUrl.searchParams.set("view",
|
|
212
|
+
proxyUrl.searchParams.set("view", `${isViteDevServer(server) ? "-DevServer" : "-PreviewServer"}${(viewParam == null ? void 0 : viewParam.startsWith("-")) ? viewParam : ""}`);
|
|
213
213
|
const proxyRequest = new Request(proxyUrl, {
|
|
214
214
|
method: req.method,
|
|
215
215
|
headers: toWebHeaders(req.headers),
|
|
@@ -258,23 +258,23 @@ function useServerMiddleware(server, ctx, _this) {
|
|
|
258
258
|
});
|
|
259
259
|
const contentType = proxyResponse.headers.get("content-type");
|
|
260
260
|
if (contentType == null ? void 0 : contentType.startsWith("text/html")) {
|
|
261
|
-
let
|
|
261
|
+
let htmlTemplateContent = await proxyResponse.text();
|
|
262
262
|
if (requestHost && requestProtocol) {
|
|
263
|
-
|
|
263
|
+
htmlTemplateContent = replaceHost(htmlTemplateContent, proxyUrl.host, requestHost, requestProtocol);
|
|
264
264
|
}
|
|
265
265
|
if (isViteDevServer(server)) {
|
|
266
266
|
const htmlTags = [];
|
|
267
|
-
htmlTags.push(`<script src='/${_path2.default.relative(ctx.viteConfig.root, ctx.entry)}' type='module'></script>`);
|
|
267
|
+
htmlTags.push(`<script src='/${escapeHtml(_path2.default.relative(ctx.viteConfig.root, ctx.entry))}' type='module'></script>`);
|
|
268
268
|
const template = await server.transformIndexHtml(
|
|
269
269
|
req.url,
|
|
270
|
-
replaceBloggerPluginHeadComment(
|
|
270
|
+
replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTags.join("")),
|
|
271
271
|
req.originalUrl
|
|
272
272
|
);
|
|
273
273
|
res.end(template);
|
|
274
274
|
} else {
|
|
275
|
-
const
|
|
276
|
-
const htmlTagsStr = getBloggerPluginHeadComment(
|
|
277
|
-
const template = replaceBloggerPluginHeadComment(
|
|
275
|
+
const xmlTemplateContent = _fs2.default.readFileSync(_path2.default.resolve(ctx.viteConfig.build.outDir, "template.xml"), "utf8");
|
|
276
|
+
const htmlTagsStr = getBloggerPluginHeadComment(xmlTemplateContent, true);
|
|
277
|
+
const template = replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTagsStr != null ? htmlTagsStr : "");
|
|
278
278
|
res.end(template);
|
|
279
279
|
}
|
|
280
280
|
} else if (requestHost && requestProtocol && contentType && /^(text\/)|(application\/(.*\+)?(xml|json))/.test(contentType)) {
|
|
@@ -356,8 +356,8 @@ Tried: ${DEFAULT_TEMPLATES.map((c) => _path2.default.join("src", c)).join(", ")}
|
|
|
356
356
|
(_a = config.build) != null ? _a : config.build = {};
|
|
357
357
|
(_c = (_b = config.build).rollupOptions) != null ? _c : _b.rollupOptions = {};
|
|
358
358
|
config.build.rollupOptions.input = entry;
|
|
359
|
-
const
|
|
360
|
-
_fs2.default.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(
|
|
359
|
+
const xmlTemplateContent = _fs2.default.readFileSync(ctx.template, "utf8");
|
|
360
|
+
_fs2.default.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(xmlTemplateContent, ""), "", true), {
|
|
361
361
|
encoding: "utf8"
|
|
362
362
|
});
|
|
363
363
|
},
|
|
@@ -370,13 +370,13 @@ Tried: ${DEFAULT_TEMPLATES.map((c) => _path2.default.join("src", c)).join(", ")}
|
|
|
370
370
|
if (output.type !== "chunk" || !output.isEntry) {
|
|
371
371
|
continue;
|
|
372
372
|
}
|
|
373
|
-
const
|
|
373
|
+
const xmlTemplateContent = _fs2.default.readFileSync(ctx.template, "utf8");
|
|
374
374
|
const htmlTags = [];
|
|
375
375
|
(_a = output.viteMetadata) == null ? void 0 : _a.importedCss.forEach((value) => {
|
|
376
376
|
htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);
|
|
377
377
|
});
|
|
378
378
|
htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);
|
|
379
|
-
const template = replaceBloggerPluginHeadComment(
|
|
379
|
+
const template = replaceBloggerPluginHeadComment(xmlTemplateContent, htmlTags.join(""), true);
|
|
380
380
|
this.emitFile({
|
|
381
381
|
type: "asset",
|
|
382
382
|
fileName: "template.xml",
|
package/dist/vite.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/blogger-plugin/blogger-plugin/packages/blogger-plugin/dist/vite.cjs","../src/vite.ts","../src/schema.ts","../src/utils.ts"],"names":["_a","templateContent"],"mappings":"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","file":"/home/runner/work/blogger-plugin/blogger-plugin/packages/blogger-plugin/dist/vite.cjs","sourcesContent":[null,"import fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { MinimalPluginContextWithoutEnvironment, Plugin, PreviewServer, ResolvedConfig, ViteDevServer } from 'vite';\nimport { type BloggerPluginOptions, BloggerPluginOptionsSchema } from './schema';\nimport { errorHtml, escapeHtml, getBloggerPluginHeadComment, replaceBloggerPluginHeadComment, replaceHost, toWebHeaders } from './utils';\n\nconst DEFAULT_ENTRIES = ['index.tsx', 'index.ts', 'index.jsx', 'index.js', 'main.tsx', 'main.ts', 'main.jsx', 'main.js'];\nconst DEFAULT_TEMPLATES = ['template.xml', 'theme.xml'];\n\ninterface PluginContext {\n viteConfig: ResolvedConfig;\n entry: string;\n template: string;\n options: BloggerPluginOptions;\n}\n\nfunction createPluginContext(userOptions: BloggerPluginOptions): PluginContext {\n return {\n viteConfig: undefined as unknown as ResolvedConfig,\n entry: undefined as unknown as string,\n template: undefined as unknown as string,\n options: BloggerPluginOptionsSchema.parse(userOptions),\n };\n}\n\nfunction isViteDevServer(server: ViteDevServer | PreviewServer): server is ViteDevServer {\n return 'hot' in server && 'transformRequest' in server && 'transformIndexHtml' in server;\n}\n\nfunction useServerMiddleware(server: ViteDevServer | PreviewServer, ctx: PluginContext, _this: MinimalPluginContextWithoutEnvironment) {\n return () => {\n server.httpServer?.once('listening', () => {\n setTimeout(() => {\n _this.info(`Unhandled requests will be proxied to ${ctx.options.proxyBlog}`);\n }, 0);\n });\n\n server.middlewares.use(async (req, res, next) => {\n if (!req.url || !req.originalUrl) {\n next();\n return;\n }\n\n const start = Date.now();\n\n const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);\n\n const viewParam = proxyUrl.searchParams.get('view');\n proxyUrl.searchParams.set('view', `-${isViteDevServer(server) ? 'Dev' : 'Preview'}Server${viewParam?.startsWith('-') ? viewParam : ''}`);\n\n const proxyRequest = new Request(proxyUrl, {\n method: req.method,\n headers: toWebHeaders(req.headers),\n body: ['GET', 'HEAD'].includes(req.method ?? '') ? undefined : Readable.toWeb(req),\n redirect: 'manual',\n });\n\n const proxyResponse = await fetch(proxyRequest).catch((error) => {\n if (error instanceof Error) {\n _this.warn({\n message: `${error.name}: ${error.message}`,\n cause: error.cause,\n stack: error.stack,\n });\n } else {\n _this.warn('Fetch failed');\n }\n return null;\n });\n\n if (proxyResponse) {\n const requestProtocol = `${(req.headers['x-forwarded-proto'] as string) || (req.socket && 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http')}:`;\n const requestHost = (req.headers['x-forwarded-host'] as string) || req.headers.host;\n\n res.statusCode = proxyResponse.status;\n res.statusMessage = proxyResponse.statusText;\n\n proxyResponse.headers.forEach((value, key) => {\n if (key === 'location') {\n const redirectUrl = new URL(value, requestHost ? `${requestProtocol}//${requestHost}${req.originalUrl}` : proxyUrl.href);\n if ((requestHost && redirectUrl.host === requestHost) || redirectUrl.host === proxyUrl.host) {\n if (requestHost && requestProtocol) {\n redirectUrl.host = requestHost;\n redirectUrl.protocol = requestProtocol;\n }\n const viewParam = redirectUrl.searchParams.get('view')?.replaceAll('-DevServer', '').replaceAll('-PreviewServer', '');\n if (viewParam) {\n redirectUrl.searchParams.set('view', viewParam);\n } else {\n redirectUrl.searchParams.delete('view');\n }\n res.setHeader(key, redirectUrl.pathname + redirectUrl.search + redirectUrl.hash);\n } else {\n res.setHeader(key, redirectUrl.href);\n }\n } else if (['content-type', 'x-robots-tag', 'date', 'location'].includes(key)) {\n res.setHeader(key, value);\n }\n });\n\n const contentType = proxyResponse.headers.get('content-type');\n\n if (contentType?.startsWith('text/html')) {\n let templateContent = await proxyResponse.text();\n\n if (requestHost && requestProtocol) {\n templateContent = replaceHost(templateContent, proxyUrl.host, requestHost, requestProtocol);\n }\n\n if (isViteDevServer(server)) {\n const htmlTags: string[] = [];\n\n htmlTags.push(`<script src='/${path.relative(ctx.viteConfig.root, ctx.entry)}' type='module'></script>`);\n\n const template = await server.transformIndexHtml(\n req.url,\n replaceBloggerPluginHeadComment(templateContent, htmlTags.join('')),\n req.originalUrl,\n );\n\n res.end(template);\n } else {\n const templateContent = fs.readFileSync(path.resolve(ctx.viteConfig.build.outDir, 'template.xml'), 'utf8');\n\n const htmlTagsStr = getBloggerPluginHeadComment(templateContent, true);\n\n const template = replaceBloggerPluginHeadComment(templateContent, htmlTagsStr ?? '');\n\n res.end(template);\n }\n } else if (requestHost && requestProtocol && contentType && /^(text\\/)|(application\\/(.*\\+)?(xml|json))/.test(contentType)) {\n const content = await proxyResponse.text();\n\n res.end(replaceHost(content, proxyUrl.host, requestHost, requestProtocol));\n } else {\n res.end(new Uint8Array(await proxyResponse.arrayBuffer()));\n }\n } else {\n res.statusCode = 500;\n res.statusMessage = 'Internal Server Error';\n\n res.setHeader('Content-Type', 'text/html');\n\n res.end(errorHtml(proxyUrl.href));\n }\n\n const duration = Date.now() - start;\n\n _this.info(`${req.method} ${req.originalUrl} -> ${res.statusCode} ${res.statusMessage} (${duration}ms)`);\n });\n };\n}\n\nexport default function blogger(userOptions: BloggerPluginOptions): Plugin {\n const ctx = createPluginContext(userOptions);\n\n return {\n name: 'vite-plugin-blogger',\n config(config) {\n const root = config.root || process.cwd();\n\n let entry: string | undefined;\n let template: string | undefined;\n\n if (ctx.options.entry) {\n const providedPath = path.resolve(root, ctx.options.entry);\n if (fs.existsSync(providedPath)) {\n entry = providedPath;\n } else {\n this.error(`Provided entry file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_ENTRIES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n entry = fullPath;\n break;\n }\n }\n\n if (!entry) {\n this.error(\n 'No entry file found in \"src\".\\n' +\n `Tried: ${DEFAULT_ENTRIES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom entry like:\\n' +\n ' blogger({ entry: \"src/my-entry.ts\" })',\n );\n }\n }\n\n if (ctx.options.template) {\n const providedPath = path.resolve(root, ctx.options.template);\n if (fs.existsSync(providedPath)) {\n template = providedPath;\n } else {\n this.error(`Provided template file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_TEMPLATES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n template = fullPath;\n break;\n }\n }\n\n if (!template) {\n this.error(\n 'No template file found in \"src\".\\n' +\n `Tried: ${DEFAULT_TEMPLATES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom template like:\\n' +\n ' blogger({ template: \"src/my-template.xml\" })',\n );\n }\n }\n\n // populate plugin context\n ctx.entry = entry as string;\n ctx.template = template as string;\n\n // override vite config\n config.build ??= {};\n config.build.rollupOptions ??= {};\n config.build.rollupOptions.input = entry;\n\n // remove contents between comments from template\n const templateContent = fs.readFileSync(ctx.template, 'utf8');\n\n fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(templateContent, ''), '', true), {\n encoding: 'utf8',\n });\n },\n configResolved(config) {\n ctx.viteConfig = config;\n },\n generateBundle(_options, bundle) {\n for (const output of Object.values(bundle)) {\n if (output.type !== 'chunk' || !output.isEntry) {\n continue;\n }\n\n const templateContent = fs.readFileSync(ctx.template, 'utf8');\n\n const htmlTags: string[] = [];\n output.viteMetadata?.importedCss.forEach((value) => {\n htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);\n });\n htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);\n\n const template = replaceBloggerPluginHeadComment(templateContent, htmlTags.join(''), true);\n\n this.emitFile({\n type: 'asset',\n fileName: 'template.xml',\n source: template,\n });\n\n break;\n }\n },\n configureServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n configurePreviewServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n };\n}\n\nexport type { BloggerPluginOptions } from './schema';\n","import { z } from 'zod';\n\nexport const BloggerPluginOptionsSchema = z\n .object({\n entry: z.string().optional(),\n template: z.string().optional(),\n proxyBlog: z.url(),\n })\n .strict();\n\nexport type BloggerPluginOptions = z.infer<typeof BloggerPluginOptionsSchema>;\n","import type { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';\n\nexport function escapeHtml(str: string) {\n if (str === '') return '';\n return str.replace(/[&<>\"'`]/g, (ch) => {\n switch (ch) {\n case '&':\n return '&';\n case '<':\n return '<';\n case '>':\n return '>';\n case '\"':\n return '"';\n case \"'\":\n return ''';\n case '`':\n return '`';\n default:\n return ch;\n }\n });\n}\n\nexport function escapeRegex(str: string) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function toWebHeaders(httpHeaders: IncomingHttpHeaders | OutgoingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(httpHeaders)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, String(value ?? ''));\n }\n }\n return headers;\n}\n\nexport function replaceBloggerPluginHeadComment(input: string, replacement: string, bcomment = false) {\n if (bcomment) {\n return input.replace(\n /<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>[\\s\\S]*?<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/,\n `<b:comment><!--blogger-plugin:head:begin--></b:comment>${replacement}<b:comment><!--blogger-plugin:head:end--></b:comment>`,\n );\n }\n return input.replace(\n /<!--blogger-plugin:head:begin-->[\\s\\S]*?<!--blogger-plugin:head:end-->/,\n `<!--blogger-plugin:head:begin-->${replacement}<!--blogger-plugin:head:end-->`,\n );\n}\n\nexport function getBloggerPluginHeadComment(input: string, bcomment = false) {\n if (bcomment) {\n return (\n input.match(/<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>([\\s\\S]*?)<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/)?.[1] ??\n null\n );\n }\n return input.match(/<!--blogger-plugin:head:begin-->([\\s\\S]*?)<!--blogger-plugin:head:end-->/)?.[1] ?? null;\n}\n\nexport function replaceHost(input: string, oldHost: string, newHost: string, newProtocol?: string) {\n return input.replace(\n new RegExp(`(https?:)?(\\\\/\\\\/|\\\\\\\\/\\\\\\\\/)${escapeRegex(oldHost)}`, 'g'),\n (_, protocol, slash) => `${protocol ? (newProtocol ?? protocol) : ''}${slash ?? ''}${newHost}`,\n );\n}\n\nexport function errorHtml(reqUrl: string) {\n return `<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset='UTF-8'/>\n <meta content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, user-scalable=yes' name='viewport'/>\n <title>500 Internal Server Error</title>\n <link rel='icon' href='data:,' />\n <style>\n *, ::before, ::after {\n box-sizing: border-box;\n }\n body {\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 20px;\n background-color: #f5f5f5;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", Segoe UI Symbol, \"Noto Color Emoji\";\n }\n .card {\n padding: 24px;\n background-color: #ffffff;\n border: 1px solid #e5e5e5;\n max-width: 448px;\n border-radius: 14px;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n .card-content {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .card-title {\n font-weight: 600;\n }\n .card-description {\n font-size: 14px;\n opacity: 0.85;\n }\n .card-footer {\n display: flex;\n align-items: center;\n }\n .button {\n display: inline-flex;\n white-space: nowrap;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 16px;\n font-weight: 500;\n background-color: #171717;\n outline: none;\n border: none;\n color: #ffffff;\n border-radius: 8px;\n min-height: 36px;\n }\n .button:hover {\n opacity: 0.9;\n }\n .button svg {\n wiheadersdth: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n .card-footer .button {\n flex-grow: 1;\n }\n </style>\n</head>\n\n<body>\n <div class='card'>\n <div class='card-content'>\n <div class='card-title'>500 Internal Server Error</div>\n <div class='card-description'>Failed to fetch: ${escapeHtml(reqUrl)}</div>\n </div>\n <div class='card-footer'>\n <button class='button' type='button'>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-ccw\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"></path><path d=\"M3 3v5h5\"></path><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"></path><path d=\"M16 16h5v5\"></path></svg>\n Reload\n </button>\n </div>\n </div>\n <script>\n const button = document.getElementsByTagName('button')[0];\n button.addEventListener('click', () => {\n window.location.reload();\n });\n </script>\n</body>\n\n</html>`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/blogger-plugin/blogger-plugin/packages/blogger-plugin/dist/vite.cjs","../src/vite.ts","../src/schema.ts","../src/utils.ts"],"names":["_a"],"mappings":"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","file":"/home/runner/work/blogger-plugin/blogger-plugin/packages/blogger-plugin/dist/vite.cjs","sourcesContent":[null,"import fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { MinimalPluginContextWithoutEnvironment, Plugin, PreviewServer, ResolvedConfig, ViteDevServer } from 'vite';\nimport { type BloggerPluginOptions, BloggerPluginOptionsSchema } from './schema';\nimport { errorHtml, escapeHtml, getBloggerPluginHeadComment, replaceBloggerPluginHeadComment, replaceHost, toWebHeaders } from './utils';\n\nconst DEFAULT_ENTRIES = ['index.tsx', 'index.ts', 'index.jsx', 'index.js', 'main.tsx', 'main.ts', 'main.jsx', 'main.js'];\nconst DEFAULT_TEMPLATES = ['template.xml', 'theme.xml'];\n\ninterface PluginContext {\n viteConfig: ResolvedConfig;\n entry: string;\n template: string;\n options: BloggerPluginOptions;\n}\n\nfunction createPluginContext(userOptions: BloggerPluginOptions): PluginContext {\n return {\n viteConfig: undefined as unknown as ResolvedConfig,\n entry: undefined as unknown as string,\n template: undefined as unknown as string,\n options: BloggerPluginOptionsSchema.parse(userOptions),\n };\n}\n\nfunction isViteDevServer(server: ViteDevServer | PreviewServer): server is ViteDevServer {\n return 'hot' in server && 'transformRequest' in server && 'transformIndexHtml' in server;\n}\n\nfunction useServerMiddleware(server: ViteDevServer | PreviewServer, ctx: PluginContext, _this: MinimalPluginContextWithoutEnvironment) {\n return () => {\n server.httpServer?.once('listening', () => {\n setTimeout(() => {\n _this.info(`Unhandled requests will be proxied to ${ctx.options.proxyBlog}`);\n }, 0);\n });\n\n server.middlewares.use(async (req, res, next) => {\n if (!req.url || !req.originalUrl) {\n next();\n return;\n }\n\n const start = Date.now();\n\n const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);\n\n const viewParam = proxyUrl.searchParams.get('view');\n proxyUrl.searchParams.set('view', `${isViteDevServer(server) ? '-DevServer' : '-PreviewServer'}${viewParam?.startsWith('-') ? viewParam : ''}`);\n\n const proxyRequest = new Request(proxyUrl, {\n method: req.method,\n headers: toWebHeaders(req.headers),\n body: ['GET', 'HEAD'].includes(req.method ?? '') ? undefined : Readable.toWeb(req),\n redirect: 'manual',\n });\n\n const proxyResponse = await fetch(proxyRequest).catch((error) => {\n if (error instanceof Error) {\n _this.warn({\n message: `${error.name}: ${error.message}`,\n cause: error.cause,\n stack: error.stack,\n });\n } else {\n _this.warn('Fetch failed');\n }\n return null;\n });\n\n if (proxyResponse) {\n const requestProtocol = `${(req.headers['x-forwarded-proto'] as string) || (req.socket && 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http')}:`;\n const requestHost = (req.headers['x-forwarded-host'] as string) || req.headers.host;\n\n res.statusCode = proxyResponse.status;\n res.statusMessage = proxyResponse.statusText;\n\n proxyResponse.headers.forEach((value, key) => {\n if (key === 'location') {\n const redirectUrl = new URL(value, requestHost ? `${requestProtocol}//${requestHost}${req.originalUrl}` : proxyUrl.href);\n if ((requestHost && redirectUrl.host === requestHost) || redirectUrl.host === proxyUrl.host) {\n if (requestHost && requestProtocol) {\n redirectUrl.host = requestHost;\n redirectUrl.protocol = requestProtocol;\n }\n const viewParam = redirectUrl.searchParams.get('view')?.replaceAll('-DevServer', '').replaceAll('-PreviewServer', '');\n if (viewParam) {\n redirectUrl.searchParams.set('view', viewParam);\n } else {\n redirectUrl.searchParams.delete('view');\n }\n res.setHeader(key, redirectUrl.pathname + redirectUrl.search + redirectUrl.hash);\n } else {\n res.setHeader(key, redirectUrl.href);\n }\n } else if (['content-type', 'x-robots-tag', 'date', 'location'].includes(key)) {\n res.setHeader(key, value);\n }\n });\n\n const contentType = proxyResponse.headers.get('content-type');\n\n if (contentType?.startsWith('text/html')) {\n let htmlTemplateContent = await proxyResponse.text();\n\n if (requestHost && requestProtocol) {\n htmlTemplateContent = replaceHost(htmlTemplateContent, proxyUrl.host, requestHost, requestProtocol);\n }\n\n if (isViteDevServer(server)) {\n const htmlTags: string[] = [];\n\n htmlTags.push(`<script src='/${escapeHtml(path.relative(ctx.viteConfig.root, ctx.entry))}' type='module'></script>`);\n\n const template = await server.transformIndexHtml(\n req.url,\n replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTags.join('')),\n req.originalUrl,\n );\n\n res.end(template);\n } else {\n const xmlTemplateContent = fs.readFileSync(path.resolve(ctx.viteConfig.build.outDir, 'template.xml'), 'utf8');\n\n const htmlTagsStr = getBloggerPluginHeadComment(xmlTemplateContent, true);\n\n const template = replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTagsStr ?? '');\n\n res.end(template);\n }\n } else if (requestHost && requestProtocol && contentType && /^(text\\/)|(application\\/(.*\\+)?(xml|json))/.test(contentType)) {\n const content = await proxyResponse.text();\n\n res.end(replaceHost(content, proxyUrl.host, requestHost, requestProtocol));\n } else {\n res.end(new Uint8Array(await proxyResponse.arrayBuffer()));\n }\n } else {\n res.statusCode = 500;\n res.statusMessage = 'Internal Server Error';\n\n res.setHeader('Content-Type', 'text/html');\n\n res.end(errorHtml(proxyUrl.href));\n }\n\n const duration = Date.now() - start;\n\n _this.info(`${req.method} ${req.originalUrl} -> ${res.statusCode} ${res.statusMessage} (${duration}ms)`);\n });\n };\n}\n\nexport default function blogger(userOptions: BloggerPluginOptions): Plugin {\n const ctx = createPluginContext(userOptions);\n\n return {\n name: 'vite-plugin-blogger',\n config(config) {\n const root = config.root || process.cwd();\n\n let entry: string | undefined;\n let template: string | undefined;\n\n if (ctx.options.entry) {\n const providedPath = path.resolve(root, ctx.options.entry);\n if (fs.existsSync(providedPath)) {\n entry = providedPath;\n } else {\n this.error(`Provided entry file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_ENTRIES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n entry = fullPath;\n break;\n }\n }\n\n if (!entry) {\n this.error(\n 'No entry file found in \"src\".\\n' +\n `Tried: ${DEFAULT_ENTRIES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom entry like:\\n' +\n ' blogger({ entry: \"src/my-entry.ts\" })',\n );\n }\n }\n\n if (ctx.options.template) {\n const providedPath = path.resolve(root, ctx.options.template);\n if (fs.existsSync(providedPath)) {\n template = providedPath;\n } else {\n this.error(`Provided template file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_TEMPLATES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n template = fullPath;\n break;\n }\n }\n\n if (!template) {\n this.error(\n 'No template file found in \"src\".\\n' +\n `Tried: ${DEFAULT_TEMPLATES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom template like:\\n' +\n ' blogger({ template: \"src/my-template.xml\" })',\n );\n }\n }\n\n // populate plugin context\n ctx.entry = entry as string;\n ctx.template = template as string;\n\n // override vite config\n config.build ??= {};\n config.build.rollupOptions ??= {};\n config.build.rollupOptions.input = entry;\n\n // remove contents between comments from template\n const xmlTemplateContent = fs.readFileSync(ctx.template, 'utf8');\n fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(xmlTemplateContent, ''), '', true), {\n encoding: 'utf8',\n });\n },\n configResolved(config) {\n ctx.viteConfig = config;\n },\n generateBundle(_options, bundle) {\n for (const output of Object.values(bundle)) {\n if (output.type !== 'chunk' || !output.isEntry) {\n continue;\n }\n\n const xmlTemplateContent = fs.readFileSync(ctx.template, 'utf8');\n\n const htmlTags: string[] = [];\n output.viteMetadata?.importedCss.forEach((value) => {\n htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);\n });\n htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);\n\n const template = replaceBloggerPluginHeadComment(xmlTemplateContent, htmlTags.join(''), true);\n\n this.emitFile({\n type: 'asset',\n fileName: 'template.xml',\n source: template,\n });\n\n break;\n }\n },\n configureServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n configurePreviewServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n };\n}\n\nexport type { BloggerPluginOptions } from './schema';\n","import { z } from 'zod';\n\nexport const BloggerPluginOptionsSchema = z\n .object({\n entry: z.string().optional(),\n template: z.string().optional(),\n proxyBlog: z.url(),\n })\n .strict();\n\nexport type BloggerPluginOptions = z.infer<typeof BloggerPluginOptionsSchema>;\n","import type { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';\n\nexport function escapeHtml(str: string) {\n if (str === '') return '';\n return str.replace(/[&<>\"'`]/g, (ch) => {\n switch (ch) {\n case '&':\n return '&';\n case '<':\n return '<';\n case '>':\n return '>';\n case '\"':\n return '"';\n case \"'\":\n return ''';\n case '`':\n return '`';\n default:\n return ch;\n }\n });\n}\n\nexport function escapeRegex(str: string) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function toWebHeaders(httpHeaders: IncomingHttpHeaders | OutgoingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(httpHeaders)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, String(value ?? ''));\n }\n }\n return headers;\n}\n\nexport function replaceBloggerPluginHeadComment(input: string, replacement: string, bcomment = false) {\n if (bcomment) {\n return input.replace(\n /<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>[\\s\\S]*?<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/,\n `<b:comment><!--blogger-plugin:head:begin--></b:comment>${replacement}<b:comment><!--blogger-plugin:head:end--></b:comment>`,\n );\n }\n return input.replace(\n /<!--blogger-plugin:head:begin-->[\\s\\S]*?<!--blogger-plugin:head:end-->/,\n `<!--blogger-plugin:head:begin-->${replacement}<!--blogger-plugin:head:end-->`,\n );\n}\n\nexport function getBloggerPluginHeadComment(input: string, bcomment = false) {\n if (bcomment) {\n return (\n input.match(/<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>([\\s\\S]*?)<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/)?.[1] ??\n null\n );\n }\n return input.match(/<!--blogger-plugin:head:begin-->([\\s\\S]*?)<!--blogger-plugin:head:end-->/)?.[1] ?? null;\n}\n\nexport function replaceHost(input: string, oldHost: string, newHost: string, newProtocol?: string) {\n return input.replace(\n new RegExp(`(https?:)?(\\\\/\\\\/|\\\\\\\\/\\\\\\\\/)${escapeRegex(oldHost)}`, 'g'),\n (_, protocol, slash) => `${protocol ? (newProtocol ?? protocol) : ''}${slash ?? ''}${newHost}`,\n );\n}\n\nexport function errorHtml(reqUrl: string) {\n return `<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset='UTF-8'/>\n <meta content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, user-scalable=yes' name='viewport'/>\n <title>500 Internal Server Error</title>\n <link rel='icon' href='data:,' />\n <style>\n *, ::before, ::after {\n box-sizing: border-box;\n }\n body {\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 20px;\n background-color: #f5f5f5;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", Segoe UI Symbol, \"Noto Color Emoji\";\n }\n .card {\n padding: 24px;\n background-color: #ffffff;\n border: 1px solid #e5e5e5;\n max-width: 448px;\n border-radius: 14px;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n .card-content {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .card-title {\n font-weight: 600;\n }\n .card-description {\n font-size: 14px;\n opacity: 0.85;\n }\n .card-footer {\n display: flex;\n align-items: center;\n }\n .button {\n display: inline-flex;\n white-space: nowrap;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 16px;\n font-weight: 500;\n background-color: #171717;\n outline: none;\n border: none;\n color: #ffffff;\n border-radius: 8px;\n min-height: 36px;\n }\n .button:hover {\n opacity: 0.9;\n }\n .button svg {\n wiheadersdth: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n .card-footer .button {\n flex-grow: 1;\n }\n </style>\n</head>\n\n<body>\n <div class='card'>\n <div class='card-content'>\n <div class='card-title'>500 Internal Server Error</div>\n <div class='card-description'>Failed to fetch: ${escapeHtml(reqUrl)}</div>\n </div>\n <div class='card-footer'>\n <button class='button' type='button'>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-ccw\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"></path><path d=\"M3 3v5h5\"></path><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"></path><path d=\"M16 16h5v5\"></path></svg>\n Reload\n </button>\n </div>\n </div>\n <script>\n const button = document.getElementsByTagName('button')[0];\n button.addEventListener('click', () => {\n window.location.reload();\n });\n </script>\n</body>\n\n</html>`;\n}\n"]}
|
package/dist/vite.js
CHANGED
|
@@ -209,7 +209,7 @@ function useServerMiddleware(server, ctx, _this) {
|
|
|
209
209
|
const start = Date.now();
|
|
210
210
|
const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);
|
|
211
211
|
const viewParam = proxyUrl.searchParams.get("view");
|
|
212
|
-
proxyUrl.searchParams.set("view",
|
|
212
|
+
proxyUrl.searchParams.set("view", `${isViteDevServer(server) ? "-DevServer" : "-PreviewServer"}${(viewParam == null ? void 0 : viewParam.startsWith("-")) ? viewParam : ""}`);
|
|
213
213
|
const proxyRequest = new Request(proxyUrl, {
|
|
214
214
|
method: req.method,
|
|
215
215
|
headers: toWebHeaders(req.headers),
|
|
@@ -258,23 +258,23 @@ function useServerMiddleware(server, ctx, _this) {
|
|
|
258
258
|
});
|
|
259
259
|
const contentType = proxyResponse.headers.get("content-type");
|
|
260
260
|
if (contentType == null ? void 0 : contentType.startsWith("text/html")) {
|
|
261
|
-
let
|
|
261
|
+
let htmlTemplateContent = await proxyResponse.text();
|
|
262
262
|
if (requestHost && requestProtocol) {
|
|
263
|
-
|
|
263
|
+
htmlTemplateContent = replaceHost(htmlTemplateContent, proxyUrl.host, requestHost, requestProtocol);
|
|
264
264
|
}
|
|
265
265
|
if (isViteDevServer(server)) {
|
|
266
266
|
const htmlTags = [];
|
|
267
|
-
htmlTags.push(`<script src='/${path.relative(ctx.viteConfig.root, ctx.entry)}' type='module'></script>`);
|
|
267
|
+
htmlTags.push(`<script src='/${escapeHtml(path.relative(ctx.viteConfig.root, ctx.entry))}' type='module'></script>`);
|
|
268
268
|
const template = await server.transformIndexHtml(
|
|
269
269
|
req.url,
|
|
270
|
-
replaceBloggerPluginHeadComment(
|
|
270
|
+
replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTags.join("")),
|
|
271
271
|
req.originalUrl
|
|
272
272
|
);
|
|
273
273
|
res.end(template);
|
|
274
274
|
} else {
|
|
275
|
-
const
|
|
276
|
-
const htmlTagsStr = getBloggerPluginHeadComment(
|
|
277
|
-
const template = replaceBloggerPluginHeadComment(
|
|
275
|
+
const xmlTemplateContent = fs.readFileSync(path.resolve(ctx.viteConfig.build.outDir, "template.xml"), "utf8");
|
|
276
|
+
const htmlTagsStr = getBloggerPluginHeadComment(xmlTemplateContent, true);
|
|
277
|
+
const template = replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTagsStr != null ? htmlTagsStr : "");
|
|
278
278
|
res.end(template);
|
|
279
279
|
}
|
|
280
280
|
} else if (requestHost && requestProtocol && contentType && /^(text\/)|(application\/(.*\+)?(xml|json))/.test(contentType)) {
|
|
@@ -356,8 +356,8 @@ Tried: ${DEFAULT_TEMPLATES.map((c) => path.join("src", c)).join(", ")}
|
|
|
356
356
|
(_a = config.build) != null ? _a : config.build = {};
|
|
357
357
|
(_c = (_b = config.build).rollupOptions) != null ? _c : _b.rollupOptions = {};
|
|
358
358
|
config.build.rollupOptions.input = entry;
|
|
359
|
-
const
|
|
360
|
-
fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(
|
|
359
|
+
const xmlTemplateContent = fs.readFileSync(ctx.template, "utf8");
|
|
360
|
+
fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(xmlTemplateContent, ""), "", true), {
|
|
361
361
|
encoding: "utf8"
|
|
362
362
|
});
|
|
363
363
|
},
|
|
@@ -370,13 +370,13 @@ Tried: ${DEFAULT_TEMPLATES.map((c) => path.join("src", c)).join(", ")}
|
|
|
370
370
|
if (output.type !== "chunk" || !output.isEntry) {
|
|
371
371
|
continue;
|
|
372
372
|
}
|
|
373
|
-
const
|
|
373
|
+
const xmlTemplateContent = fs.readFileSync(ctx.template, "utf8");
|
|
374
374
|
const htmlTags = [];
|
|
375
375
|
(_a = output.viteMetadata) == null ? void 0 : _a.importedCss.forEach((value) => {
|
|
376
376
|
htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);
|
|
377
377
|
});
|
|
378
378
|
htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);
|
|
379
|
-
const template = replaceBloggerPluginHeadComment(
|
|
379
|
+
const template = replaceBloggerPluginHeadComment(xmlTemplateContent, htmlTags.join(""), true);
|
|
380
380
|
this.emitFile({
|
|
381
381
|
type: "asset",
|
|
382
382
|
fileName: "template.xml",
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite.ts","../src/schema.ts","../src/utils.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { MinimalPluginContextWithoutEnvironment, Plugin, PreviewServer, ResolvedConfig, ViteDevServer } from 'vite';\nimport { type BloggerPluginOptions, BloggerPluginOptionsSchema } from './schema';\nimport { errorHtml, escapeHtml, getBloggerPluginHeadComment, replaceBloggerPluginHeadComment, replaceHost, toWebHeaders } from './utils';\n\nconst DEFAULT_ENTRIES = ['index.tsx', 'index.ts', 'index.jsx', 'index.js', 'main.tsx', 'main.ts', 'main.jsx', 'main.js'];\nconst DEFAULT_TEMPLATES = ['template.xml', 'theme.xml'];\n\ninterface PluginContext {\n viteConfig: ResolvedConfig;\n entry: string;\n template: string;\n options: BloggerPluginOptions;\n}\n\nfunction createPluginContext(userOptions: BloggerPluginOptions): PluginContext {\n return {\n viteConfig: undefined as unknown as ResolvedConfig,\n entry: undefined as unknown as string,\n template: undefined as unknown as string,\n options: BloggerPluginOptionsSchema.parse(userOptions),\n };\n}\n\nfunction isViteDevServer(server: ViteDevServer | PreviewServer): server is ViteDevServer {\n return 'hot' in server && 'transformRequest' in server && 'transformIndexHtml' in server;\n}\n\nfunction useServerMiddleware(server: ViteDevServer | PreviewServer, ctx: PluginContext, _this: MinimalPluginContextWithoutEnvironment) {\n return () => {\n server.httpServer?.once('listening', () => {\n setTimeout(() => {\n _this.info(`Unhandled requests will be proxied to ${ctx.options.proxyBlog}`);\n }, 0);\n });\n\n server.middlewares.use(async (req, res, next) => {\n if (!req.url || !req.originalUrl) {\n next();\n return;\n }\n\n const start = Date.now();\n\n const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);\n\n const viewParam = proxyUrl.searchParams.get('view');\n proxyUrl.searchParams.set('view', `-${isViteDevServer(server) ? 'Dev' : 'Preview'}Server${viewParam?.startsWith('-') ? viewParam : ''}`);\n\n const proxyRequest = new Request(proxyUrl, {\n method: req.method,\n headers: toWebHeaders(req.headers),\n body: ['GET', 'HEAD'].includes(req.method ?? '') ? undefined : Readable.toWeb(req),\n redirect: 'manual',\n });\n\n const proxyResponse = await fetch(proxyRequest).catch((error) => {\n if (error instanceof Error) {\n _this.warn({\n message: `${error.name}: ${error.message}`,\n cause: error.cause,\n stack: error.stack,\n });\n } else {\n _this.warn('Fetch failed');\n }\n return null;\n });\n\n if (proxyResponse) {\n const requestProtocol = `${(req.headers['x-forwarded-proto'] as string) || (req.socket && 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http')}:`;\n const requestHost = (req.headers['x-forwarded-host'] as string) || req.headers.host;\n\n res.statusCode = proxyResponse.status;\n res.statusMessage = proxyResponse.statusText;\n\n proxyResponse.headers.forEach((value, key) => {\n if (key === 'location') {\n const redirectUrl = new URL(value, requestHost ? `${requestProtocol}//${requestHost}${req.originalUrl}` : proxyUrl.href);\n if ((requestHost && redirectUrl.host === requestHost) || redirectUrl.host === proxyUrl.host) {\n if (requestHost && requestProtocol) {\n redirectUrl.host = requestHost;\n redirectUrl.protocol = requestProtocol;\n }\n const viewParam = redirectUrl.searchParams.get('view')?.replaceAll('-DevServer', '').replaceAll('-PreviewServer', '');\n if (viewParam) {\n redirectUrl.searchParams.set('view', viewParam);\n } else {\n redirectUrl.searchParams.delete('view');\n }\n res.setHeader(key, redirectUrl.pathname + redirectUrl.search + redirectUrl.hash);\n } else {\n res.setHeader(key, redirectUrl.href);\n }\n } else if (['content-type', 'x-robots-tag', 'date', 'location'].includes(key)) {\n res.setHeader(key, value);\n }\n });\n\n const contentType = proxyResponse.headers.get('content-type');\n\n if (contentType?.startsWith('text/html')) {\n let templateContent = await proxyResponse.text();\n\n if (requestHost && requestProtocol) {\n templateContent = replaceHost(templateContent, proxyUrl.host, requestHost, requestProtocol);\n }\n\n if (isViteDevServer(server)) {\n const htmlTags: string[] = [];\n\n htmlTags.push(`<script src='/${path.relative(ctx.viteConfig.root, ctx.entry)}' type='module'></script>`);\n\n const template = await server.transformIndexHtml(\n req.url,\n replaceBloggerPluginHeadComment(templateContent, htmlTags.join('')),\n req.originalUrl,\n );\n\n res.end(template);\n } else {\n const templateContent = fs.readFileSync(path.resolve(ctx.viteConfig.build.outDir, 'template.xml'), 'utf8');\n\n const htmlTagsStr = getBloggerPluginHeadComment(templateContent, true);\n\n const template = replaceBloggerPluginHeadComment(templateContent, htmlTagsStr ?? '');\n\n res.end(template);\n }\n } else if (requestHost && requestProtocol && contentType && /^(text\\/)|(application\\/(.*\\+)?(xml|json))/.test(contentType)) {\n const content = await proxyResponse.text();\n\n res.end(replaceHost(content, proxyUrl.host, requestHost, requestProtocol));\n } else {\n res.end(new Uint8Array(await proxyResponse.arrayBuffer()));\n }\n } else {\n res.statusCode = 500;\n res.statusMessage = 'Internal Server Error';\n\n res.setHeader('Content-Type', 'text/html');\n\n res.end(errorHtml(proxyUrl.href));\n }\n\n const duration = Date.now() - start;\n\n _this.info(`${req.method} ${req.originalUrl} -> ${res.statusCode} ${res.statusMessage} (${duration}ms)`);\n });\n };\n}\n\nexport default function blogger(userOptions: BloggerPluginOptions): Plugin {\n const ctx = createPluginContext(userOptions);\n\n return {\n name: 'vite-plugin-blogger',\n config(config) {\n const root = config.root || process.cwd();\n\n let entry: string | undefined;\n let template: string | undefined;\n\n if (ctx.options.entry) {\n const providedPath = path.resolve(root, ctx.options.entry);\n if (fs.existsSync(providedPath)) {\n entry = providedPath;\n } else {\n this.error(`Provided entry file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_ENTRIES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n entry = fullPath;\n break;\n }\n }\n\n if (!entry) {\n this.error(\n 'No entry file found in \"src\".\\n' +\n `Tried: ${DEFAULT_ENTRIES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom entry like:\\n' +\n ' blogger({ entry: \"src/my-entry.ts\" })',\n );\n }\n }\n\n if (ctx.options.template) {\n const providedPath = path.resolve(root, ctx.options.template);\n if (fs.existsSync(providedPath)) {\n template = providedPath;\n } else {\n this.error(`Provided template file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_TEMPLATES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n template = fullPath;\n break;\n }\n }\n\n if (!template) {\n this.error(\n 'No template file found in \"src\".\\n' +\n `Tried: ${DEFAULT_TEMPLATES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom template like:\\n' +\n ' blogger({ template: \"src/my-template.xml\" })',\n );\n }\n }\n\n // populate plugin context\n ctx.entry = entry as string;\n ctx.template = template as string;\n\n // override vite config\n config.build ??= {};\n config.build.rollupOptions ??= {};\n config.build.rollupOptions.input = entry;\n\n // remove contents between comments from template\n const templateContent = fs.readFileSync(ctx.template, 'utf8');\n\n fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(templateContent, ''), '', true), {\n encoding: 'utf8',\n });\n },\n configResolved(config) {\n ctx.viteConfig = config;\n },\n generateBundle(_options, bundle) {\n for (const output of Object.values(bundle)) {\n if (output.type !== 'chunk' || !output.isEntry) {\n continue;\n }\n\n const templateContent = fs.readFileSync(ctx.template, 'utf8');\n\n const htmlTags: string[] = [];\n output.viteMetadata?.importedCss.forEach((value) => {\n htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);\n });\n htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);\n\n const template = replaceBloggerPluginHeadComment(templateContent, htmlTags.join(''), true);\n\n this.emitFile({\n type: 'asset',\n fileName: 'template.xml',\n source: template,\n });\n\n break;\n }\n },\n configureServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n configurePreviewServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n };\n}\n\nexport type { BloggerPluginOptions } from './schema';\n","import { z } from 'zod';\n\nexport const BloggerPluginOptionsSchema = z\n .object({\n entry: z.string().optional(),\n template: z.string().optional(),\n proxyBlog: z.url(),\n })\n .strict();\n\nexport type BloggerPluginOptions = z.infer<typeof BloggerPluginOptionsSchema>;\n","import type { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';\n\nexport function escapeHtml(str: string) {\n if (str === '') return '';\n return str.replace(/[&<>\"'`]/g, (ch) => {\n switch (ch) {\n case '&':\n return '&';\n case '<':\n return '<';\n case '>':\n return '>';\n case '\"':\n return '"';\n case \"'\":\n return ''';\n case '`':\n return '`';\n default:\n return ch;\n }\n });\n}\n\nexport function escapeRegex(str: string) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function toWebHeaders(httpHeaders: IncomingHttpHeaders | OutgoingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(httpHeaders)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, String(value ?? ''));\n }\n }\n return headers;\n}\n\nexport function replaceBloggerPluginHeadComment(input: string, replacement: string, bcomment = false) {\n if (bcomment) {\n return input.replace(\n /<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>[\\s\\S]*?<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/,\n `<b:comment><!--blogger-plugin:head:begin--></b:comment>${replacement}<b:comment><!--blogger-plugin:head:end--></b:comment>`,\n );\n }\n return input.replace(\n /<!--blogger-plugin:head:begin-->[\\s\\S]*?<!--blogger-plugin:head:end-->/,\n `<!--blogger-plugin:head:begin-->${replacement}<!--blogger-plugin:head:end-->`,\n );\n}\n\nexport function getBloggerPluginHeadComment(input: string, bcomment = false) {\n if (bcomment) {\n return (\n input.match(/<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>([\\s\\S]*?)<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/)?.[1] ??\n null\n );\n }\n return input.match(/<!--blogger-plugin:head:begin-->([\\s\\S]*?)<!--blogger-plugin:head:end-->/)?.[1] ?? null;\n}\n\nexport function replaceHost(input: string, oldHost: string, newHost: string, newProtocol?: string) {\n return input.replace(\n new RegExp(`(https?:)?(\\\\/\\\\/|\\\\\\\\/\\\\\\\\/)${escapeRegex(oldHost)}`, 'g'),\n (_, protocol, slash) => `${protocol ? (newProtocol ?? protocol) : ''}${slash ?? ''}${newHost}`,\n );\n}\n\nexport function errorHtml(reqUrl: string) {\n return `<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset='UTF-8'/>\n <meta content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, user-scalable=yes' name='viewport'/>\n <title>500 Internal Server Error</title>\n <link rel='icon' href='data:,' />\n <style>\n *, ::before, ::after {\n box-sizing: border-box;\n }\n body {\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 20px;\n background-color: #f5f5f5;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", Segoe UI Symbol, \"Noto Color Emoji\";\n }\n .card {\n padding: 24px;\n background-color: #ffffff;\n border: 1px solid #e5e5e5;\n max-width: 448px;\n border-radius: 14px;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n .card-content {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .card-title {\n font-weight: 600;\n }\n .card-description {\n font-size: 14px;\n opacity: 0.85;\n }\n .card-footer {\n display: flex;\n align-items: center;\n }\n .button {\n display: inline-flex;\n white-space: nowrap;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 16px;\n font-weight: 500;\n background-color: #171717;\n outline: none;\n border: none;\n color: #ffffff;\n border-radius: 8px;\n min-height: 36px;\n }\n .button:hover {\n opacity: 0.9;\n }\n .button svg {\n wiheadersdth: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n .card-footer .button {\n flex-grow: 1;\n }\n </style>\n</head>\n\n<body>\n <div class='card'>\n <div class='card-content'>\n <div class='card-title'>500 Internal Server Error</div>\n <div class='card-description'>Failed to fetch: ${escapeHtml(reqUrl)}</div>\n </div>\n <div class='card-footer'>\n <button class='button' type='button'>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-ccw\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"></path><path d=\"M3 3v5h5\"></path><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"></path><path d=\"M16 16h5v5\"></path></svg>\n Reload\n </button>\n </div>\n </div>\n <script>\n const button = document.getElementsByTagName('button')[0];\n button.addEventListener('click', () => {\n window.location.reload();\n });\n </script>\n</body>\n\n</html>`;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,gBAAgB;;;ACFzB,SAAS,SAAS;AAEX,IAAM,6BAA6B,EACvC,OAAO;AAAA,EACN,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,WAAW,EAAE,IAAI;AACnB,CAAC,EACA,OAAO;;;ACNH,SAAS,WAAW,KAAa;AACtC,MAAI,QAAQ,GAAI,QAAO;AACvB,SAAO,IAAI,QAAQ,aAAa,CAAC,OAAO;AACtC,YAAQ,IAAI;AAAA,MACV,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC;AACH;AAEO,SAAS,YAAY,KAAa;AACvC,SAAO,IAAI,QAAQ,uBAAuB,MAAM;AAClD;AAEO,SAAS,aAAa,aAAiE;AAC5F,QAAM,UAAU,IAAI,QAAQ;AAC5B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACvD,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,QAAQ,OAAO;AACxB,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AAAA,IACF,OAAO;AACL,cAAQ,IAAI,MAAM,OAAO,wBAAS,EAAE,CAAC;AAAA,IACvC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gCAAgC,OAAe,aAAqB,WAAW,OAAO;AACpG,MAAI,UAAU;AACZ,WAAO,MAAM;AAAA,MACX;AAAA,MACA,0DAA0D,WAAW;AAAA,IACvE;AAAA,EACF;AACA,SAAO,MAAM;AAAA,IACX;AAAA,IACA,mCAAmC,WAAW;AAAA,EAChD;AACF;AAEO,SAAS,4BAA4B,OAAe,WAAW,OAAO;AAvD7E;AAwDE,MAAI,UAAU;AACZ,YACE,iBAAM,MAAM,0HAA0H,MAAtI,mBAA0I,OAA1I,YACA;AAAA,EAEJ;AACA,UAAO,iBAAM,MAAM,0EAA0E,MAAtF,mBAA0F,OAA1F,YAAgG;AACzG;AAEO,SAAS,YAAY,OAAe,SAAiB,SAAiB,aAAsB;AACjG,SAAO,MAAM;AAAA,IACX,IAAI,OAAO,gCAAgC,YAAY,OAAO,CAAC,IAAI,GAAG;AAAA,IACtE,CAAC,GAAG,UAAU,UAAU,GAAG,WAAY,oCAAe,WAAY,EAAE,GAAG,wBAAS,EAAE,GAAG,OAAO;AAAA,EAC9F;AACF;AAEO,SAAS,UAAU,QAAgB;AACxC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uDAmF8C,WAAW,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBzE;;;AFvKA,IAAM,kBAAkB,CAAC,aAAa,YAAY,aAAa,YAAY,YAAY,WAAW,YAAY,SAAS;AACvH,IAAM,oBAAoB,CAAC,gBAAgB,WAAW;AAStD,SAAS,oBAAoB,aAAkD;AAC7E,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS,2BAA2B,MAAM,WAAW;AAAA,EACvD;AACF;AAEA,SAAS,gBAAgB,QAAgE;AACvF,SAAO,SAAS,UAAU,sBAAsB,UAAU,wBAAwB;AACpF;AAEA,SAAS,oBAAoB,QAAuC,KAAoB,OAA+C;AACrI,SAAO,MAAM;AA/Bf;AAgCI,iBAAO,eAAP,mBAAmB,KAAK,aAAa,MAAM;AACzC,iBAAW,MAAM;AACf,cAAM,KAAK,yCAAyC,IAAI,QAAQ,SAAS,EAAE;AAAA,MAC7E,GAAG,CAAC;AAAA,IACN;AAEA,WAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAtCrD,UAAAA;AAuCM,UAAI,CAAC,IAAI,OAAO,CAAC,IAAI,aAAa;AAChC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,QAAQ,KAAK,IAAI;AAEvB,YAAM,WAAW,IAAI,IAAI,IAAI,aAAa,IAAI,QAAQ,SAAS;AAE/D,YAAM,YAAY,SAAS,aAAa,IAAI,MAAM;AAClD,eAAS,aAAa,IAAI,QAAQ,IAAI,gBAAgB,MAAM,IAAI,QAAQ,SAAS,UAAS,uCAAW,WAAW,QAAO,YAAY,EAAE,EAAE;AAEvI,YAAM,eAAe,IAAI,QAAQ,UAAU;AAAA,QACzC,QAAQ,IAAI;AAAA,QACZ,SAAS,aAAa,IAAI,OAAO;AAAA,QACjC,MAAM,CAAC,OAAO,MAAM,EAAE,UAASA,MAAA,IAAI,WAAJ,OAAAA,MAAc,EAAE,IAAI,SAAY,SAAS,MAAM,GAAG;AAAA,QACjF,UAAU;AAAA,MACZ,CAAC;AAED,YAAM,gBAAgB,MAAM,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU;AAC/D,YAAI,iBAAiB,OAAO;AAC1B,gBAAM,KAAK;AAAA,YACT,SAAS,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO;AAAA,YACxC,OAAO,MAAM;AAAA,YACb,OAAO,MAAM;AAAA,UACf,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,KAAK,cAAc;AAAA,QAC3B;AACA,eAAO;AAAA,MACT,CAAC;AAED,UAAI,eAAe;AACjB,cAAM,kBAAkB,GAAI,IAAI,QAAQ,mBAAmB,MAAiB,IAAI,UAAU,eAAe,IAAI,UAAU,IAAI,OAAO,YAAY,UAAU,OAAO;AAC/J,cAAM,cAAe,IAAI,QAAQ,kBAAkB,KAAgB,IAAI,QAAQ;AAE/E,YAAI,aAAa,cAAc;AAC/B,YAAI,gBAAgB,cAAc;AAElC,sBAAc,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AA9EtD,cAAAA;AA+EU,cAAI,QAAQ,YAAY;AACtB,kBAAM,cAAc,IAAI,IAAI,OAAO,cAAc,GAAG,eAAe,KAAK,WAAW,GAAG,IAAI,WAAW,KAAK,SAAS,IAAI;AACvH,gBAAK,eAAe,YAAY,SAAS,eAAgB,YAAY,SAAS,SAAS,MAAM;AAC3F,kBAAI,eAAe,iBAAiB;AAClC,4BAAY,OAAO;AACnB,4BAAY,WAAW;AAAA,cACzB;AACA,oBAAMC,cAAYD,MAAA,YAAY,aAAa,IAAI,MAAM,MAAnC,gBAAAA,IAAsC,WAAW,cAAc,IAAI,WAAW,kBAAkB;AAClH,kBAAIC,YAAW;AACb,4BAAY,aAAa,IAAI,QAAQA,UAAS;AAAA,cAChD,OAAO;AACL,4BAAY,aAAa,OAAO,MAAM;AAAA,cACxC;AACA,kBAAI,UAAU,KAAK,YAAY,WAAW,YAAY,SAAS,YAAY,IAAI;AAAA,YACjF,OAAO;AACL,kBAAI,UAAU,KAAK,YAAY,IAAI;AAAA,YACrC;AAAA,UACF,WAAW,CAAC,gBAAgB,gBAAgB,QAAQ,UAAU,EAAE,SAAS,GAAG,GAAG;AAC7E,gBAAI,UAAU,KAAK,KAAK;AAAA,UAC1B;AAAA,QACF,CAAC;AAED,cAAM,cAAc,cAAc,QAAQ,IAAI,cAAc;AAE5D,YAAI,2CAAa,WAAW,cAAc;AACxC,cAAI,kBAAkB,MAAM,cAAc,KAAK;AAE/C,cAAI,eAAe,iBAAiB;AAClC,8BAAkB,YAAY,iBAAiB,SAAS,MAAM,aAAa,eAAe;AAAA,UAC5F;AAEA,cAAI,gBAAgB,MAAM,GAAG;AAC3B,kBAAM,WAAqB,CAAC;AAE5B,qBAAS,KAAK,iBAAiB,KAAK,SAAS,IAAI,WAAW,MAAM,IAAI,KAAK,CAAC,2BAA2B;AAEvG,kBAAM,WAAW,MAAM,OAAO;AAAA,cAC5B,IAAI;AAAA,cACJ,gCAAgC,iBAAiB,SAAS,KAAK,EAAE,CAAC;AAAA,cAClE,IAAI;AAAA,YACN;AAEA,gBAAI,IAAI,QAAQ;AAAA,UAClB,OAAO;AACL,kBAAMC,mBAAkB,GAAG,aAAa,KAAK,QAAQ,IAAI,WAAW,MAAM,QAAQ,cAAc,GAAG,MAAM;AAEzG,kBAAM,cAAc,4BAA4BA,kBAAiB,IAAI;AAErE,kBAAM,WAAW,gCAAgCA,kBAAiB,oCAAe,EAAE;AAEnF,gBAAI,IAAI,QAAQ;AAAA,UAClB;AAAA,QACF,WAAW,eAAe,mBAAmB,eAAe,6CAA6C,KAAK,WAAW,GAAG;AAC1H,gBAAM,UAAU,MAAM,cAAc,KAAK;AAEzC,cAAI,IAAI,YAAY,SAAS,SAAS,MAAM,aAAa,eAAe,CAAC;AAAA,QAC3E,OAAO;AACL,cAAI,IAAI,IAAI,WAAW,MAAM,cAAc,YAAY,CAAC,CAAC;AAAA,QAC3D;AAAA,MACF,OAAO;AACL,YAAI,aAAa;AACjB,YAAI,gBAAgB;AAEpB,YAAI,UAAU,gBAAgB,WAAW;AAEzC,YAAI,IAAI,UAAU,SAAS,IAAI,CAAC;AAAA,MAClC;AAEA,YAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,YAAM,KAAK,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW,OAAO,IAAI,UAAU,IAAI,IAAI,aAAa,KAAK,QAAQ,KAAK;AAAA,IACzG,CAAC;AAAA,EACH;AACF;AAEe,SAAR,QAAyB,aAA2C;AACzE,QAAM,MAAM,oBAAoB,WAAW;AAE3C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,QAAQ;AA/JnB;AAgKM,YAAM,OAAO,OAAO,QAAQ,QAAQ,IAAI;AAExC,UAAI;AACJ,UAAI;AAEJ,UAAI,IAAI,QAAQ,OAAO;AACrB,cAAM,eAAe,KAAK,QAAQ,MAAM,IAAI,QAAQ,KAAK;AACzD,YAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,kBAAQ;AAAA,QACV,OAAO;AACL,eAAK,MAAM,uCAAuC,YAAY,EAAE;AAAA,QAClE;AAAA,MACF,OAAO;AACL,mBAAW,QAAQ,iBAAiB;AAClC,gBAAM,WAAW,KAAK,QAAQ,MAAM,OAAO,IAAI;AAC/C,cAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,oBAAQ;AACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,CAAC,OAAO;AACV,eAAK;AAAA,YACH;AAAA,SACY,gBAAgB,IAAI,CAAC,MAAM,KAAK,KAAK,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,UAGxE;AAAA,QACF;AAAA,MACF;AAEA,UAAI,IAAI,QAAQ,UAAU;AACxB,cAAM,eAAe,KAAK,QAAQ,MAAM,IAAI,QAAQ,QAAQ;AAC5D,YAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,qBAAW;AAAA,QACb,OAAO;AACL,eAAK,MAAM,0CAA0C,YAAY,EAAE;AAAA,QACrE;AAAA,MACF,OAAO;AACL,mBAAW,QAAQ,mBAAmB;AACpC,gBAAM,WAAW,KAAK,QAAQ,MAAM,OAAO,IAAI;AAC/C,cAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,uBAAW;AACX;AAAA,UACF;AAAA,QACF;AAEA,YAAI,CAAC,UAAU;AACb,eAAK;AAAA,YACH;AAAA,SACY,kBAAkB,IAAI,CAAC,MAAM,KAAK,KAAK,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,UAG1E;AAAA,QACF;AAAA,MACF;AAGA,UAAI,QAAQ;AACZ,UAAI,WAAW;AAGf,mBAAO,UAAP,mBAAO,QAAU,CAAC;AAClB,yBAAO,OAAM,kBAAb,eAAa,gBAAkB,CAAC;AAChC,aAAO,MAAM,cAAc,QAAQ;AAGnC,YAAM,kBAAkB,GAAG,aAAa,IAAI,UAAU,MAAM;AAE5D,SAAG,cAAc,IAAI,UAAU,gCAAgC,gCAAgC,iBAAiB,EAAE,GAAG,IAAI,IAAI,GAAG;AAAA,QAC9H,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IACA,eAAe,QAAQ;AACrB,UAAI,aAAa;AAAA,IACnB;AAAA,IACA,eAAe,UAAU,QAAQ;AA5OrC;AA6OM,iBAAW,UAAU,OAAO,OAAO,MAAM,GAAG;AAC1C,YAAI,OAAO,SAAS,WAAW,CAAC,OAAO,SAAS;AAC9C;AAAA,QACF;AAEA,cAAM,kBAAkB,GAAG,aAAa,IAAI,UAAU,MAAM;AAE5D,cAAM,WAAqB,CAAC;AAC5B,qBAAO,iBAAP,mBAAqB,YAAY,QAAQ,CAAC,UAAU;AAClD,mBAAS,KAAK,uCAAuC,WAAW,IAAI,WAAW,OAAO,KAAK,CAAC,sBAAsB;AAAA,QACpH;AACA,iBAAS,KAAK,wCAAwC,WAAW,IAAI,WAAW,OAAO,OAAO,QAAQ,CAAC,2BAA2B;AAElI,cAAM,WAAW,gCAAgC,iBAAiB,SAAS,KAAK,EAAE,GAAG,IAAI;AAEzF,aAAK,SAAS;AAAA,UACZ,MAAM;AAAA,UACN,UAAU;AAAA,UACV,QAAQ;AAAA,QACV,CAAC;AAED;AAAA,MACF;AAAA,IACF;AAAA,IACA,gBAAgB,QAAQ;AACtB,aAAO,oBAAoB,QAAQ,KAAK,IAAI;AAAA,IAC9C;AAAA,IACA,uBAAuB,QAAQ;AAC7B,aAAO,oBAAoB,QAAQ,KAAK,IAAI;AAAA,IAC9C;AAAA,EACF;AACF;","names":["_a","viewParam","templateContent"]}
|
|
1
|
+
{"version":3,"sources":["../src/vite.ts","../src/schema.ts","../src/utils.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { MinimalPluginContextWithoutEnvironment, Plugin, PreviewServer, ResolvedConfig, ViteDevServer } from 'vite';\nimport { type BloggerPluginOptions, BloggerPluginOptionsSchema } from './schema';\nimport { errorHtml, escapeHtml, getBloggerPluginHeadComment, replaceBloggerPluginHeadComment, replaceHost, toWebHeaders } from './utils';\n\nconst DEFAULT_ENTRIES = ['index.tsx', 'index.ts', 'index.jsx', 'index.js', 'main.tsx', 'main.ts', 'main.jsx', 'main.js'];\nconst DEFAULT_TEMPLATES = ['template.xml', 'theme.xml'];\n\ninterface PluginContext {\n viteConfig: ResolvedConfig;\n entry: string;\n template: string;\n options: BloggerPluginOptions;\n}\n\nfunction createPluginContext(userOptions: BloggerPluginOptions): PluginContext {\n return {\n viteConfig: undefined as unknown as ResolvedConfig,\n entry: undefined as unknown as string,\n template: undefined as unknown as string,\n options: BloggerPluginOptionsSchema.parse(userOptions),\n };\n}\n\nfunction isViteDevServer(server: ViteDevServer | PreviewServer): server is ViteDevServer {\n return 'hot' in server && 'transformRequest' in server && 'transformIndexHtml' in server;\n}\n\nfunction useServerMiddleware(server: ViteDevServer | PreviewServer, ctx: PluginContext, _this: MinimalPluginContextWithoutEnvironment) {\n return () => {\n server.httpServer?.once('listening', () => {\n setTimeout(() => {\n _this.info(`Unhandled requests will be proxied to ${ctx.options.proxyBlog}`);\n }, 0);\n });\n\n server.middlewares.use(async (req, res, next) => {\n if (!req.url || !req.originalUrl) {\n next();\n return;\n }\n\n const start = Date.now();\n\n const proxyUrl = new URL(req.originalUrl, ctx.options.proxyBlog);\n\n const viewParam = proxyUrl.searchParams.get('view');\n proxyUrl.searchParams.set('view', `${isViteDevServer(server) ? '-DevServer' : '-PreviewServer'}${viewParam?.startsWith('-') ? viewParam : ''}`);\n\n const proxyRequest = new Request(proxyUrl, {\n method: req.method,\n headers: toWebHeaders(req.headers),\n body: ['GET', 'HEAD'].includes(req.method ?? '') ? undefined : Readable.toWeb(req),\n redirect: 'manual',\n });\n\n const proxyResponse = await fetch(proxyRequest).catch((error) => {\n if (error instanceof Error) {\n _this.warn({\n message: `${error.name}: ${error.message}`,\n cause: error.cause,\n stack: error.stack,\n });\n } else {\n _this.warn('Fetch failed');\n }\n return null;\n });\n\n if (proxyResponse) {\n const requestProtocol = `${(req.headers['x-forwarded-proto'] as string) || (req.socket && 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http')}:`;\n const requestHost = (req.headers['x-forwarded-host'] as string) || req.headers.host;\n\n res.statusCode = proxyResponse.status;\n res.statusMessage = proxyResponse.statusText;\n\n proxyResponse.headers.forEach((value, key) => {\n if (key === 'location') {\n const redirectUrl = new URL(value, requestHost ? `${requestProtocol}//${requestHost}${req.originalUrl}` : proxyUrl.href);\n if ((requestHost && redirectUrl.host === requestHost) || redirectUrl.host === proxyUrl.host) {\n if (requestHost && requestProtocol) {\n redirectUrl.host = requestHost;\n redirectUrl.protocol = requestProtocol;\n }\n const viewParam = redirectUrl.searchParams.get('view')?.replaceAll('-DevServer', '').replaceAll('-PreviewServer', '');\n if (viewParam) {\n redirectUrl.searchParams.set('view', viewParam);\n } else {\n redirectUrl.searchParams.delete('view');\n }\n res.setHeader(key, redirectUrl.pathname + redirectUrl.search + redirectUrl.hash);\n } else {\n res.setHeader(key, redirectUrl.href);\n }\n } else if (['content-type', 'x-robots-tag', 'date', 'location'].includes(key)) {\n res.setHeader(key, value);\n }\n });\n\n const contentType = proxyResponse.headers.get('content-type');\n\n if (contentType?.startsWith('text/html')) {\n let htmlTemplateContent = await proxyResponse.text();\n\n if (requestHost && requestProtocol) {\n htmlTemplateContent = replaceHost(htmlTemplateContent, proxyUrl.host, requestHost, requestProtocol);\n }\n\n if (isViteDevServer(server)) {\n const htmlTags: string[] = [];\n\n htmlTags.push(`<script src='/${escapeHtml(path.relative(ctx.viteConfig.root, ctx.entry))}' type='module'></script>`);\n\n const template = await server.transformIndexHtml(\n req.url,\n replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTags.join('')),\n req.originalUrl,\n );\n\n res.end(template);\n } else {\n const xmlTemplateContent = fs.readFileSync(path.resolve(ctx.viteConfig.build.outDir, 'template.xml'), 'utf8');\n\n const htmlTagsStr = getBloggerPluginHeadComment(xmlTemplateContent, true);\n\n const template = replaceBloggerPluginHeadComment(htmlTemplateContent, htmlTagsStr ?? '');\n\n res.end(template);\n }\n } else if (requestHost && requestProtocol && contentType && /^(text\\/)|(application\\/(.*\\+)?(xml|json))/.test(contentType)) {\n const content = await proxyResponse.text();\n\n res.end(replaceHost(content, proxyUrl.host, requestHost, requestProtocol));\n } else {\n res.end(new Uint8Array(await proxyResponse.arrayBuffer()));\n }\n } else {\n res.statusCode = 500;\n res.statusMessage = 'Internal Server Error';\n\n res.setHeader('Content-Type', 'text/html');\n\n res.end(errorHtml(proxyUrl.href));\n }\n\n const duration = Date.now() - start;\n\n _this.info(`${req.method} ${req.originalUrl} -> ${res.statusCode} ${res.statusMessage} (${duration}ms)`);\n });\n };\n}\n\nexport default function blogger(userOptions: BloggerPluginOptions): Plugin {\n const ctx = createPluginContext(userOptions);\n\n return {\n name: 'vite-plugin-blogger',\n config(config) {\n const root = config.root || process.cwd();\n\n let entry: string | undefined;\n let template: string | undefined;\n\n if (ctx.options.entry) {\n const providedPath = path.resolve(root, ctx.options.entry);\n if (fs.existsSync(providedPath)) {\n entry = providedPath;\n } else {\n this.error(`Provided entry file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_ENTRIES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n entry = fullPath;\n break;\n }\n }\n\n if (!entry) {\n this.error(\n 'No entry file found in \"src\".\\n' +\n `Tried: ${DEFAULT_ENTRIES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom entry like:\\n' +\n ' blogger({ entry: \"src/my-entry.ts\" })',\n );\n }\n }\n\n if (ctx.options.template) {\n const providedPath = path.resolve(root, ctx.options.template);\n if (fs.existsSync(providedPath)) {\n template = providedPath;\n } else {\n this.error(`Provided template file does not exist: ${providedPath}`);\n }\n } else {\n for (const file of DEFAULT_TEMPLATES) {\n const fullPath = path.resolve(root, 'src', file);\n if (fs.existsSync(fullPath)) {\n template = fullPath;\n break;\n }\n }\n\n if (!template) {\n this.error(\n 'No template file found in \"src\".\\n' +\n `Tried: ${DEFAULT_TEMPLATES.map((c) => path.join('src', c)).join(', ')}\\n` +\n '👉 Tip: You can pass a custom template like:\\n' +\n ' blogger({ template: \"src/my-template.xml\" })',\n );\n }\n }\n\n // populate plugin context\n ctx.entry = entry as string;\n ctx.template = template as string;\n\n // override vite config\n config.build ??= {};\n config.build.rollupOptions ??= {};\n config.build.rollupOptions.input = entry;\n\n // remove contents between comments from template\n const xmlTemplateContent = fs.readFileSync(ctx.template, 'utf8');\n fs.writeFileSync(ctx.template, replaceBloggerPluginHeadComment(replaceBloggerPluginHeadComment(xmlTemplateContent, ''), '', true), {\n encoding: 'utf8',\n });\n },\n configResolved(config) {\n ctx.viteConfig = config;\n },\n generateBundle(_options, bundle) {\n for (const output of Object.values(bundle)) {\n if (output.type !== 'chunk' || !output.isEntry) {\n continue;\n }\n\n const xmlTemplateContent = fs.readFileSync(ctx.template, 'utf8');\n\n const htmlTags: string[] = [];\n output.viteMetadata?.importedCss.forEach((value) => {\n htmlTags.push(`<link crossorigin='anonymous' href='${escapeHtml(ctx.viteConfig.base + value)}' rel='stylesheet'/>`);\n });\n htmlTags.push(`<script crossorigin='anonymous' src='${escapeHtml(ctx.viteConfig.base + output.fileName)}' type='module'></script>`);\n\n const template = replaceBloggerPluginHeadComment(xmlTemplateContent, htmlTags.join(''), true);\n\n this.emitFile({\n type: 'asset',\n fileName: 'template.xml',\n source: template,\n });\n\n break;\n }\n },\n configureServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n configurePreviewServer(server) {\n return useServerMiddleware(server, ctx, this);\n },\n };\n}\n\nexport type { BloggerPluginOptions } from './schema';\n","import { z } from 'zod';\n\nexport const BloggerPluginOptionsSchema = z\n .object({\n entry: z.string().optional(),\n template: z.string().optional(),\n proxyBlog: z.url(),\n })\n .strict();\n\nexport type BloggerPluginOptions = z.infer<typeof BloggerPluginOptionsSchema>;\n","import type { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';\n\nexport function escapeHtml(str: string) {\n if (str === '') return '';\n return str.replace(/[&<>\"'`]/g, (ch) => {\n switch (ch) {\n case '&':\n return '&';\n case '<':\n return '<';\n case '>':\n return '>';\n case '\"':\n return '"';\n case \"'\":\n return ''';\n case '`':\n return '`';\n default:\n return ch;\n }\n });\n}\n\nexport function escapeRegex(str: string) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function toWebHeaders(httpHeaders: IncomingHttpHeaders | OutgoingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(httpHeaders)) {\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, String(value ?? ''));\n }\n }\n return headers;\n}\n\nexport function replaceBloggerPluginHeadComment(input: string, replacement: string, bcomment = false) {\n if (bcomment) {\n return input.replace(\n /<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>[\\s\\S]*?<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/,\n `<b:comment><!--blogger-plugin:head:begin--></b:comment>${replacement}<b:comment><!--blogger-plugin:head:end--></b:comment>`,\n );\n }\n return input.replace(\n /<!--blogger-plugin:head:begin-->[\\s\\S]*?<!--blogger-plugin:head:end-->/,\n `<!--blogger-plugin:head:begin-->${replacement}<!--blogger-plugin:head:end-->`,\n );\n}\n\nexport function getBloggerPluginHeadComment(input: string, bcomment = false) {\n if (bcomment) {\n return (\n input.match(/<b:comment><!--blogger-plugin:head:begin--><\\/b:comment>([\\s\\S]*?)<b:comment><!--blogger-plugin:head:end--><\\/b:comment>/)?.[1] ??\n null\n );\n }\n return input.match(/<!--blogger-plugin:head:begin-->([\\s\\S]*?)<!--blogger-plugin:head:end-->/)?.[1] ?? null;\n}\n\nexport function replaceHost(input: string, oldHost: string, newHost: string, newProtocol?: string) {\n return input.replace(\n new RegExp(`(https?:)?(\\\\/\\\\/|\\\\\\\\/\\\\\\\\/)${escapeRegex(oldHost)}`, 'g'),\n (_, protocol, slash) => `${protocol ? (newProtocol ?? protocol) : ''}${slash ?? ''}${newHost}`,\n );\n}\n\nexport function errorHtml(reqUrl: string) {\n return `<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset='UTF-8'/>\n <meta content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, user-scalable=yes' name='viewport'/>\n <title>500 Internal Server Error</title>\n <link rel='icon' href='data:,' />\n <style>\n *, ::before, ::after {\n box-sizing: border-box;\n }\n body {\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 20px;\n background-color: #f5f5f5;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", Segoe UI Symbol, \"Noto Color Emoji\";\n }\n .card {\n padding: 24px;\n background-color: #ffffff;\n border: 1px solid #e5e5e5;\n max-width: 448px;\n border-radius: 14px;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n .card-content {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .card-title {\n font-weight: 600;\n }\n .card-description {\n font-size: 14px;\n opacity: 0.85;\n }\n .card-footer {\n display: flex;\n align-items: center;\n }\n .button {\n display: inline-flex;\n white-space: nowrap;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 16px;\n font-weight: 500;\n background-color: #171717;\n outline: none;\n border: none;\n color: #ffffff;\n border-radius: 8px;\n min-height: 36px;\n }\n .button:hover {\n opacity: 0.9;\n }\n .button svg {\n wiheadersdth: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n .card-footer .button {\n flex-grow: 1;\n }\n </style>\n</head>\n\n<body>\n <div class='card'>\n <div class='card-content'>\n <div class='card-title'>500 Internal Server Error</div>\n <div class='card-description'>Failed to fetch: ${escapeHtml(reqUrl)}</div>\n </div>\n <div class='card-footer'>\n <button class='button' type='button'>\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-ccw\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"></path><path d=\"M3 3v5h5\"></path><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"></path><path d=\"M16 16h5v5\"></path></svg>\n Reload\n </button>\n </div>\n </div>\n <script>\n const button = document.getElementsByTagName('button')[0];\n button.addEventListener('click', () => {\n window.location.reload();\n });\n </script>\n</body>\n\n</html>`;\n}\n"],"mappings":"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","names":["_a","viewParam"]}
|