portosaurus 2.1.5 → 2.1.7
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 +5 -3
- package/src/cli/README.md +8 -0
- package/src/cli/build.mjs +1 -1
- package/src/cli/dev.mjs +1 -1
- package/src/cli/init.mjs +1 -1
- package/src/cli/serve.mjs +1 -1
- package/src/core/README.md +5 -0
- package/src/core/buildDocuConfig.mjs +32 -279
- package/src/plugins/README.md +8 -0
- package/src/{utils/build/generateFavicon.mjs → plugins/favicon.mjs} +50 -14
- package/src/{utils/build → plugins/lib}/cssUtils.mjs +1 -1
- package/src/{utils/build → plugins/lib}/iconExtractor.mjs +1 -1
- package/src/{utils/build → plugins/lib}/imageDownloader.mjs +1 -1
- package/src/{utils/build → plugins/lib}/imageProcessor.mjs +1 -1
- package/src/{utils/build/generateRobotsTxt.mjs → plugins/robots.mjs} +1 -1
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/blog-post-list-prop-default.json +11 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/p/blog-archive-f05.json +1 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/p/blog-authors-790.json +1 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/p/blog-bd9.json +1 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/site-blog-ss-md-18f.json +16 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/site-blog-welcome-md-a95.json +34 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-docs/default/p/notes-106.json +1 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-docs/default/site-notes-index-mdx-3b5.json +16 -0
- package/src/template/.docusaurus/docusaurus-plugin-content-docs/default/site-notes-welcome-mdx-d34.json +14 -0
- package/src/template/.docusaurus/docusaurus-plugin-debug/default/p/docusaurus-debug-content-0d5.json +1 -0
- package/src/template/.docusaurus/globalData.json +29 -0
- package/src/template/.docusaurus/portosaurus/docusaurus.config.js +7 -0
- package/src/template/.docusaurus/registry.js +40 -0
- package/src/template/.docusaurus/routes.js +122 -0
- package/src/template/.docusaurus/routesChunkNames.json +156 -0
- package/src/template/config.js +1 -0
- package/src/template/gitignore +7 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/0.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/1.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/2.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/3.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/4.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/5.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/6.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/7.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/index.pack +0 -0
- package/src/template/node_modules/.cache/webpack/client-development-en/index.pack.old +0 -0
- package/src/template/notes/index.mdx +9 -0
- package/src/theme/README.md +9 -0
- package/src/utils/README.md +8 -0
- package/src/utils/configUtils.mjs +238 -0
- /package/src/{core/plugins/themePlugin.mjs → plugins/theme.mjs} +0 -0
- /package/src/utils/{helpers.mjs → cliHelpers.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portosaurus",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"homepage": "https://github.com/soymadip/portosaurus",
|
|
6
6
|
"repository": {
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
"src/assets/",
|
|
16
16
|
"src/cli/",
|
|
17
17
|
"src/core/",
|
|
18
|
+
"src/plugins/",
|
|
19
|
+
"src/template/",
|
|
20
|
+
"src/template/.gitignore",
|
|
18
21
|
"src/theme/",
|
|
19
|
-
"src/utils/"
|
|
20
|
-
"src/template/"
|
|
22
|
+
"src/utils/"
|
|
21
23
|
],
|
|
22
24
|
"scripts": {
|
|
23
25
|
"test": "bun test",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Portosaurus CLI Commands
|
|
2
|
+
|
|
3
|
+
This directory contains the entry points for the `porto` CLI. Each command is implemented as an asynchronous function that handles project lifecycle tasks.
|
|
4
|
+
|
|
5
|
+
- **`init.mjs`**: Handles new project creation, template mirroring, and initial setup.
|
|
6
|
+
- **`dev.mjs`**: Wraps the Docusaurus development server, providing automatic config shimming and asset generation.
|
|
7
|
+
- **`build.mjs`**: Orchestrates production builds, ensuring the site is ready for deployment.
|
|
8
|
+
- **`serve.mjs`**: Provides a way to preview the production build locally.
|
package/src/cli/build.mjs
CHANGED
package/src/cli/dev.mjs
CHANGED
package/src/cli/init.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { logger } from "../utils/logger.mjs";
|
|
4
4
|
import { getPackageManager } from "../utils/packageManager.mjs";
|
|
5
|
-
import { PortoRoot, mirrorSync } from "../utils/
|
|
5
|
+
import { PortoRoot, mirrorSync } from "../utils/cliHelpers.mjs";
|
|
6
6
|
|
|
7
7
|
export async function initCommand(projectName, options) {
|
|
8
8
|
const projectDir = path.resolve(process.cwd(), projectName);
|
package/src/cli/serve.mjs
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Portosaurus Core Engine
|
|
2
|
+
|
|
3
|
+
This directory contains the central configuration engine that transforms user-provided settings into a valid Docusaurus configuration.
|
|
4
|
+
|
|
5
|
+
- **`buildDocuConfig.mjs`**: The main orchestration file. It deep-merges user configs with template defaults, resolves variable placeholders (`{{...}}`), and registers internal plugins and themes.
|
|
@@ -4,249 +4,19 @@ import { fileURLToPath } from "url";
|
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import { logger } from "../utils/logger.mjs";
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
deepMerge,
|
|
9
|
+
resolveVars,
|
|
10
|
+
resolveSiteUrl,
|
|
11
|
+
resolveBasePath,
|
|
12
|
+
getVersion,
|
|
13
|
+
useEnabled,
|
|
14
|
+
createStaticAssetResolver,
|
|
15
|
+
} from "../utils/configUtils.mjs";
|
|
16
|
+
|
|
7
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
18
|
const PortoRoot = path.resolve(__dirname, "../../");
|
|
9
19
|
|
|
10
|
-
// ─── Helpers ────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Deep merge two objects. Source values override target values.
|
|
14
|
-
* Arrays are replaced, not concatenated.
|
|
15
|
-
*/
|
|
16
|
-
function deepMerge(target, source) {
|
|
17
|
-
const result = { ...target };
|
|
18
|
-
|
|
19
|
-
for (const key of Object.keys(source)) {
|
|
20
|
-
if (
|
|
21
|
-
source[key] &&
|
|
22
|
-
typeof source[key] === "object" &&
|
|
23
|
-
!Array.isArray(source[key]) &&
|
|
24
|
-
target[key] &&
|
|
25
|
-
typeof target[key] === "object" &&
|
|
26
|
-
!Array.isArray(target[key])
|
|
27
|
-
) {
|
|
28
|
-
result[key] = deepMerge(target[key], source[key]);
|
|
29
|
-
} else if (source[key] !== undefined) {
|
|
30
|
-
result[key] = source[key];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Resolve {{...}} template references and @alias/ path prefixes
|
|
38
|
-
* inside config string values.
|
|
39
|
-
*
|
|
40
|
-
* Template refs: {{hero_section.profile_pic}} → resolves to the value
|
|
41
|
-
* Path aliases: @porto/img/icon.png → img/icon.png (prefix stripped)
|
|
42
|
-
*
|
|
43
|
-
* @param {*} obj - The value (or tree) to resolve.
|
|
44
|
-
* @param {Object} UserConfig - The full config object for {{...}} lookups.
|
|
45
|
-
* @param {Object} aliases - Map of prefix → absolute directory path.
|
|
46
|
-
* e.g. { "@porto/": "/abs/path/to/assets" }
|
|
47
|
-
* @param {Set} pathStack - Internal: tracks {{...}} refs to prevent cycles.
|
|
48
|
-
* @param {number} depth - Internal: recursion depth guard.
|
|
49
|
-
*/
|
|
50
|
-
export function resolveVars(
|
|
51
|
-
obj,
|
|
52
|
-
UserConfig,
|
|
53
|
-
aliases = {},
|
|
54
|
-
pathStack = new Set(),
|
|
55
|
-
depth = 0,
|
|
56
|
-
) {
|
|
57
|
-
if (depth > 10) return obj;
|
|
58
|
-
|
|
59
|
-
if (typeof obj === "string") {
|
|
60
|
-
// If the entire string is exactly one {{tag}}, return the raw value
|
|
61
|
-
const singleMatch = obj.match(/^\{\{([^}]+)\}\}$/);
|
|
62
|
-
|
|
63
|
-
if (singleMatch && !obj.includes("{{", 2)) {
|
|
64
|
-
const refPath = singleMatch[1];
|
|
65
|
-
const parts = refPath.split(".");
|
|
66
|
-
let val = UserConfig;
|
|
67
|
-
|
|
68
|
-
for (const p of parts) {
|
|
69
|
-
if (val == null || typeof val !== "object") break;
|
|
70
|
-
val = val[p];
|
|
71
|
-
}
|
|
72
|
-
if (val !== undefined && val !== obj) {
|
|
73
|
-
return resolveVars(val, UserConfig, aliases, pathStack, depth + 1);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Resolve {{...}} template variables
|
|
78
|
-
let result = obj.replace(
|
|
79
|
-
/(\\?)\{\{([^}]+)\}\}/g,
|
|
80
|
-
(match, escape, refPath) => {
|
|
81
|
-
// If escaped with \, return the literal {{tag}}
|
|
82
|
-
if (escape === "\\") {
|
|
83
|
-
return `{{${refPath}}}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Check for circularity
|
|
87
|
-
if (pathStack.has(refPath)) {
|
|
88
|
-
return match;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const parts = refPath.split(".");
|
|
92
|
-
let val = UserConfig;
|
|
93
|
-
|
|
94
|
-
for (const p of parts) {
|
|
95
|
-
if (val == null || typeof val !== "object") return match;
|
|
96
|
-
val = val[p];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (val === undefined) {
|
|
100
|
-
return match;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Recurse in case the resolved value also has template refs
|
|
104
|
-
const newStack = new Set(pathStack);
|
|
105
|
-
newStack.add(refPath);
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
typeof val === "string" &&
|
|
109
|
-
(val.includes("{{") || val.startsWith("@"))
|
|
110
|
-
) {
|
|
111
|
-
return resolveVars(val, UserConfig, aliases, newStack, depth + 1);
|
|
112
|
-
}
|
|
113
|
-
return val;
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// Resolve @alias/ path prefixes
|
|
118
|
-
for (const [prefix, dir] of Object.entries(aliases)) {
|
|
119
|
-
if (result.startsWith(prefix)) {
|
|
120
|
-
const relative = result.slice(prefix.length);
|
|
121
|
-
if (dir && !fs.existsSync(path.resolve(dir, relative))) {
|
|
122
|
-
logger.warn(`Asset not found: "${result}"`);
|
|
123
|
-
}
|
|
124
|
-
return relative;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (Array.isArray(obj)) {
|
|
132
|
-
return obj.map((item) =>
|
|
133
|
-
resolveVars(item, UserConfig, aliases, pathStack, depth),
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (obj && typeof obj === "object") {
|
|
138
|
-
const result = {};
|
|
139
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
140
|
-
result[key] = resolveVars(value, UserConfig, aliases, pathStack, depth);
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
return obj;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Resolves the site URL based on config value and environment.
|
|
149
|
-
*/
|
|
150
|
-
function resolveSiteUrl(configValue) {
|
|
151
|
-
if (configValue === "auto") {
|
|
152
|
-
// GitLab Detection
|
|
153
|
-
if (process.env.CI_PAGES_URL) {
|
|
154
|
-
try {
|
|
155
|
-
const url = new URL(process.env.CI_PAGES_URL);
|
|
156
|
-
return `${url.protocol}//${url.host}`;
|
|
157
|
-
} catch (e) {}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// GitHub/Gitea/Forgejo Detection
|
|
161
|
-
if (process.env.GITHUB_ACTIONS === "true") {
|
|
162
|
-
const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
|
|
163
|
-
|
|
164
|
-
// GitHub specific logic
|
|
165
|
-
if (serverUrl === "https://github.com") {
|
|
166
|
-
const repoOwner = process.env.GITHUB_REPOSITORY_OWNER;
|
|
167
|
-
return `https://${repoOwner}.github.io`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Forgejo/Gitea/Other Actions - Best effort based on instance URL
|
|
171
|
-
try {
|
|
172
|
-
const url = new URL(serverUrl);
|
|
173
|
-
return `${url.protocol}//${url.host}`;
|
|
174
|
-
} catch (e) {}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return "http://localhost";
|
|
178
|
-
}
|
|
179
|
-
return configValue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Resolves the base path based on config value and environment.
|
|
184
|
-
*/
|
|
185
|
-
function resolveBasePath(configValue) {
|
|
186
|
-
if (configValue === "auto") {
|
|
187
|
-
// GitLab Detection
|
|
188
|
-
if (process.env.CI_PAGES_URL) {
|
|
189
|
-
try {
|
|
190
|
-
const url = new URL(process.env.CI_PAGES_URL);
|
|
191
|
-
return url.pathname.endsWith("/") ? url.pathname : `${url.pathname}/`;
|
|
192
|
-
} catch (e) {}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// GitHub/Gitea/Forgejo Detection
|
|
196
|
-
if (process.env.GITHUB_ACTIONS === "true") {
|
|
197
|
-
const repo = process.env.GITHUB_REPOSITORY; // "owner/repo"
|
|
198
|
-
|
|
199
|
-
if (!repo) {
|
|
200
|
-
return "/";
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const [owner, name] = repo.split("/");
|
|
204
|
-
const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
|
|
205
|
-
|
|
206
|
-
// GitHub User/Org Pages logic
|
|
207
|
-
if (serverUrl === "https://github.com" && name === `${owner}.github.io`) {
|
|
208
|
-
return "/";
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return `/${name}/`;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return "/";
|
|
215
|
-
}
|
|
216
|
-
return configValue;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Read the portosaurus package version.
|
|
221
|
-
*/
|
|
222
|
-
function getVersion() {
|
|
223
|
-
try {
|
|
224
|
-
const pkgPath = path.resolve(PortoRoot, "package.json");
|
|
225
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
226
|
-
return pkg.version || "0.0.0";
|
|
227
|
-
} catch {
|
|
228
|
-
return "N/A";
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Filter items that have enable/value conditional structure.
|
|
234
|
-
*/
|
|
235
|
-
function useEnabled(items) {
|
|
236
|
-
if (!Array.isArray(items)) return [];
|
|
237
|
-
return items.flatMap((item) => {
|
|
238
|
-
if (
|
|
239
|
-
item &&
|
|
240
|
-
typeof item === "object" &&
|
|
241
|
-
"enable" in item &&
|
|
242
|
-
"value" in item
|
|
243
|
-
) {
|
|
244
|
-
return item.enable === true ? [item.value] : [];
|
|
245
|
-
}
|
|
246
|
-
return [item];
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
20
|
// ─── Main Config Generator ─────────────────────────────────
|
|
251
21
|
|
|
252
22
|
/**
|
|
@@ -302,7 +72,9 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
302
72
|
// Meta tags
|
|
303
73
|
let PortoMetaTags = [];
|
|
304
74
|
try {
|
|
305
|
-
const meta = require(
|
|
75
|
+
const meta = require(
|
|
76
|
+
path.resolve(PortoRoot, "src/theme/config/metaTags.js"),
|
|
77
|
+
);
|
|
306
78
|
PortoMetaTags = meta.PortoMetaTags ?? meta.metaTags ?? [];
|
|
307
79
|
} catch {
|
|
308
80
|
// OK — no meta tags
|
|
@@ -322,39 +94,11 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
322
94
|
* - Remote URLs (http/https) pass through as-is.
|
|
323
95
|
* - Local paths are checked against UserStaticDir, then PortoAssetDir.
|
|
324
96
|
*/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (userPath && fs.existsSync(path.resolve(UserStaticDir, userPath))) {
|
|
331
|
-
return userPath;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Check Porto's bundled assets
|
|
335
|
-
if (userPath && fs.existsSync(path.resolve(PortoAssetDir, userPath))) {
|
|
336
|
-
return userPath;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Fallback logic with warnings
|
|
340
|
-
if (portoFallback) {
|
|
341
|
-
// Only warn if the fallback actually exists and they provided a bad path
|
|
342
|
-
if (
|
|
343
|
-
userPath &&
|
|
344
|
-
userPath !== "favicon/favicon.ico" &&
|
|
345
|
-
fs.existsSync(path.resolve(PortoAssetDir, portoFallback))
|
|
346
|
-
) {
|
|
347
|
-
logger.warn(`Asset not found: "${userPath}" — using bundled default.`);
|
|
348
|
-
}
|
|
349
|
-
return portoFallback;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (userPath && userPath !== "favicon/favicon.ico") {
|
|
353
|
-
logger.warn(`Asset not found: "${userPath}" — no fallback available.`);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return userPath || "";
|
|
357
|
-
}
|
|
97
|
+
const resolveStaticAsset = createStaticAssetResolver(
|
|
98
|
+
UserStaticDir,
|
|
99
|
+
PortoAssetDir,
|
|
100
|
+
);
|
|
101
|
+
|
|
358
102
|
|
|
359
103
|
// Pages
|
|
360
104
|
const PortoPagesDir = path.resolve(PortoRoot, "src/theme/pages");
|
|
@@ -374,7 +118,10 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
374
118
|
projectName: UserConfig.hero_section.title,
|
|
375
119
|
title: UserConfig.hero_section.title,
|
|
376
120
|
tagline: UserConfig.hero_section.description,
|
|
377
|
-
favicon: resolveStaticAsset(
|
|
121
|
+
favicon: resolveStaticAsset(
|
|
122
|
+
UserConfig.favicon,
|
|
123
|
+
resolveStaticAsset(UserConfig.hero_section.profile_pic, "img/icon.png"),
|
|
124
|
+
),
|
|
378
125
|
url: siteUrl,
|
|
379
126
|
baseUrl: basePath,
|
|
380
127
|
|
|
@@ -491,7 +238,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
491
238
|
// so Root.js and MDXComponents are natively transpiled by Docusaurus.
|
|
492
239
|
themes: [
|
|
493
240
|
[
|
|
494
|
-
path.resolve(PortoRoot, "src/
|
|
241
|
+
path.resolve(PortoRoot, "src/plugins/theme.mjs"),
|
|
495
242
|
{
|
|
496
243
|
themeDir: PortoThemeDir,
|
|
497
244
|
},
|
|
@@ -527,7 +274,13 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
527
274
|
hideOnScroll: UserConfig.hide_navbar_on_scroll,
|
|
528
275
|
logo: {
|
|
529
276
|
alt: "Site Logo",
|
|
530
|
-
src: resolveStaticAsset(
|
|
277
|
+
src: resolveStaticAsset(
|
|
278
|
+
UserConfig.favicon,
|
|
279
|
+
resolveStaticAsset(
|
|
280
|
+
UserConfig.hero_section.profile_pic,
|
|
281
|
+
"img/icon.png",
|
|
282
|
+
),
|
|
283
|
+
),
|
|
531
284
|
},
|
|
532
285
|
items: useEnabled([
|
|
533
286
|
{
|
|
@@ -636,8 +389,8 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
|
636
389
|
},
|
|
637
390
|
};
|
|
638
391
|
},
|
|
639
|
-
path.resolve(PortoRoot, "src/
|
|
640
|
-
path.resolve(PortoRoot, "src/
|
|
392
|
+
path.resolve(PortoRoot, "src/plugins/favicon.mjs"),
|
|
393
|
+
path.resolve(PortoRoot, "src/plugins/robots.mjs"),
|
|
641
394
|
[
|
|
642
395
|
require.resolve("@easyops-cn/docusaurus-search-local", {
|
|
643
396
|
paths: [UserRoot, PortoRoot],
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Docusaurus Plugins
|
|
2
|
+
|
|
3
|
+
This directory contains internal Docusaurus plugins that extend the site's functionality during build and development.
|
|
4
|
+
|
|
5
|
+
- **`theme.mjs`**: Maps the internal `src/theme` directory so that Docusaurus can pick up Portosaurus components natively.
|
|
6
|
+
- **`favicon.mjs`**: Automates the generation of a complete favicon suite from the user's profile picture.
|
|
7
|
+
- **`robots.mjs`**: Generates a `robots.txt` file based on the configured site URL.
|
|
8
|
+
- **`lib/`**: Contains shared internal utilities used by these plugins (image processing, CSS extraction, etc.).
|
|
@@ -2,11 +2,11 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { favicons } from "favicons";
|
|
5
|
-
import { downloadImage } from "./imageDownloader.mjs";
|
|
6
|
-
import { reshapeImage } from "./imageProcessor.mjs";
|
|
7
|
-
import { getCssVar } from "./cssUtils.mjs";
|
|
8
|
-
import { extractSvg } from "./iconExtractor.mjs";
|
|
9
|
-
import { logger } from "../logger.mjs";
|
|
5
|
+
import { downloadImage } from "./lib/imageDownloader.mjs";
|
|
6
|
+
import { reshapeImage } from "./lib/imageProcessor.mjs";
|
|
7
|
+
import { getCssVar } from "./lib/cssUtils.mjs";
|
|
8
|
+
import { extractSvg } from "./lib/iconExtractor.mjs";
|
|
9
|
+
import { logger } from "../utils/logger.mjs";
|
|
10
10
|
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
|
|
@@ -142,14 +142,47 @@ export async function generateFavicons(context, options = {}) {
|
|
|
142
142
|
},
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
-
// 1.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
// 1. Resolve image source (Remote URL vs Local Path)
|
|
146
|
+
let downloadedRes;
|
|
147
|
+
const isRemote = /^https?:\/\//.test(profilePicUrl);
|
|
148
|
+
|
|
149
|
+
if (isRemote) {
|
|
150
|
+
// Download remote image with proxy support + caching
|
|
151
|
+
downloadedRes = await downloadImage(
|
|
152
|
+
profilePicUrl,
|
|
153
|
+
cacheDir,
|
|
154
|
+
"profile_pic_src.png",
|
|
155
|
+
{ proxies, cacheDir: path.join(cacheDir, "downloads") },
|
|
156
|
+
);
|
|
157
|
+
tempFiles.push(downloadedRes);
|
|
158
|
+
} else {
|
|
159
|
+
// Handle local path
|
|
160
|
+
let localPath = null;
|
|
161
|
+
// Static directories are provided in siteConfig or we check common ones
|
|
162
|
+
const staticDirs = siteConfig.staticDirectories || ["static"];
|
|
163
|
+
|
|
164
|
+
for (const sDir of staticDirs) {
|
|
165
|
+
const fullPath = path.resolve(context.siteDir, sDir, profilePicUrl);
|
|
166
|
+
if (fs.existsSync(fullPath)) {
|
|
167
|
+
localPath = fullPath;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!localPath) {
|
|
173
|
+
// Fallback: check Porto asset dir if not found in user static
|
|
174
|
+
const portoAssetDir = path.resolve(__dirname, "../../assets");
|
|
175
|
+
const portoPath = path.resolve(portoAssetDir, profilePicUrl);
|
|
176
|
+
if (fs.existsSync(portoPath)) {
|
|
177
|
+
localPath = portoPath;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!localPath) {
|
|
182
|
+
throw new Error(`Local profile picture not found: ${profilePicUrl}`);
|
|
183
|
+
}
|
|
184
|
+
downloadedRes = localPath;
|
|
185
|
+
}
|
|
153
186
|
|
|
154
187
|
// 2. Reshape if needed
|
|
155
188
|
let finalImagePath = downloadedRes;
|
|
@@ -159,7 +192,10 @@ export async function generateFavicons(context, options = {}) {
|
|
|
159
192
|
reshapedImagePath,
|
|
160
193
|
shape,
|
|
161
194
|
);
|
|
162
|
-
|
|
195
|
+
// Only cleanup if it's a generated temp file, not the original local source
|
|
196
|
+
if (finalImagePath !== downloadedRes) {
|
|
197
|
+
tempFiles.push(finalImagePath);
|
|
198
|
+
}
|
|
163
199
|
}
|
|
164
200
|
|
|
165
201
|
createDirectoryIfNotExists(outputDir);
|
|
@@ -11,7 +11,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
11
11
|
*/
|
|
12
12
|
export function getCssVar(name) {
|
|
13
13
|
try {
|
|
14
|
-
const cssPath = path.resolve(__dirname, "../../css/custom.css");
|
|
14
|
+
const cssPath = path.resolve(__dirname, "../../theme/css/custom.css");
|
|
15
15
|
const cssContent = fs.readFileSync(cssPath, "utf8");
|
|
16
16
|
|
|
17
17
|
if (name === "--ifm-color-primary") {
|
|
@@ -3,7 +3,7 @@ import path from "path";
|
|
|
3
3
|
import https from "https";
|
|
4
4
|
import http from "http";
|
|
5
5
|
import crypto from "crypto";
|
|
6
|
-
import { logger } from "
|
|
6
|
+
import { logger } from "../../utils/logger.mjs";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Generates a short hash from a URL for use as a cache key.
|
package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/p/blog-archive-f05.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"archive":{"blogPosts":[{"id":"/welcome","metadata":{"permalink":"/blog/welcome","source":"@site/blog/welcome.md","title":"Welcome to the Blog","description":"Your first blog post! Edit or delete this file to get started.","date":"2017-03-31T22:00:00.000Z","tags":[{"inline":true,"label":"welcome","permalink":"/blog/tags/welcome"}],"hasTruncateMarker":true,"authors":[{"name":"Your Name","title":"Author","url":"https://github.com/yourprofile","key":"you","page":null}],"frontMatter":{"title":"Welcome to the Blog","authors":["you"],"tags":["welcome"]},"unlisted":false},"content":"Your first blog post! Edit or delete this file to get started.\n\n<!-- truncate -->\n\nThis is a blog powered by [Portosaurus](https://github.com/soymadip/portosaurus)."}]}}
|
package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/p/blog-authors-790.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"authors":[{"name":"Your Name","title":"Author","url":"https://github.com/yourprofile","key":"you","page":null,"count":1}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"metadata":{"permalink":"/blog","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}}
|
package/src/template/.docusaurus/docusaurus-plugin-content-blog/default/site-blog-ss-md-18f.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permalink": "/blog/ss",
|
|
3
|
+
"source": "@site/blog/ss.md",
|
|
4
|
+
"title": "ss",
|
|
5
|
+
"description": "",
|
|
6
|
+
"date": "2017-03-31T22:00:00.000Z",
|
|
7
|
+
"tags": [],
|
|
8
|
+
"hasTruncateMarker": false,
|
|
9
|
+
"authors": [],
|
|
10
|
+
"frontMatter": {},
|
|
11
|
+
"unlisted": false,
|
|
12
|
+
"nextItem": {
|
|
13
|
+
"title": "Welcome to the Blog",
|
|
14
|
+
"permalink": "/blog/welcome"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permalink": "/blog/welcome",
|
|
3
|
+
"source": "@site/blog/welcome.md",
|
|
4
|
+
"title": "Welcome to the Blog",
|
|
5
|
+
"description": "Your first blog post! Edit or delete this file to get started.",
|
|
6
|
+
"date": "2017-03-31T22:00:00.000Z",
|
|
7
|
+
"tags": [
|
|
8
|
+
{
|
|
9
|
+
"inline": true,
|
|
10
|
+
"label": "welcome",
|
|
11
|
+
"permalink": "/blog/tags/welcome"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"hasTruncateMarker": true,
|
|
15
|
+
"authors": [
|
|
16
|
+
{
|
|
17
|
+
"name": "Your Name",
|
|
18
|
+
"title": "Author",
|
|
19
|
+
"url": "https://github.com/yourprofile",
|
|
20
|
+
"key": "you",
|
|
21
|
+
"page": null
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"frontMatter": {
|
|
25
|
+
"title": "Welcome to the Blog",
|
|
26
|
+
"authors": [
|
|
27
|
+
"you"
|
|
28
|
+
],
|
|
29
|
+
"tags": [
|
|
30
|
+
"welcome"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"unlisted": false
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{},"docs":{"index":{"id":"index","title":"Notes","description":""},"welcome":{"id":"welcome","title":"Welcome","description":"Welcome to your notes! Add markdown or MDX files in this notes/ directory."}}}}
|