portosaurus 2.0.3 → 2.1.0
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/bin/portosaurus.mjs +14 -327
- package/package.json +16 -11
- package/src/cli/build.mjs +43 -0
- package/src/cli/dev.mjs +31 -0
- package/src/cli/init.mjs +135 -0
- package/src/cli/serve.mjs +30 -0
- package/src/core/buildDocuConfig.mjs +664 -0
- package/src/core/{themePlugin.mjs → plugins/themePlugin.mjs} +1 -1
- package/src/template/.github/workflows/deploy.yml +52 -0
- package/src/template/.nojekyll +0 -0
- package/src/template/README.md +58 -0
- package/src/template/blog/authors.yml +1 -1
- package/src/template/blog/welcome.md +1 -1
- package/src/template/config.js +40 -23
- package/src/template/package.json +20 -0
- package/src/template/static/img/svg/icon-blog.svg +2 -0
- package/src/template/static/img/svg/icon-note.svg +2 -0
- package/src/{components → theme/components}/AboutSection/index.js +22 -13
- package/src/{components → theme/components}/AboutSection/styles.module.css +59 -48
- package/src/{components → theme/components}/ContactSection/index.js +31 -24
- package/src/{components → theme/components}/ContactSection/styles.module.css +31 -26
- package/src/{components → theme/components}/ExperienceSection/index.js +12 -7
- package/src/{components → theme/components}/ExperienceSection/styles.module.css +23 -20
- package/src/{components → theme/components}/HeroSection/index.js +9 -11
- package/src/{components → theme/components}/HeroSection/styles.module.css +44 -32
- package/src/{components → theme/components}/NoteIndex/index.js +10 -3
- package/src/{components → theme/components}/Preview/components/PreviewHeader.js +14 -8
- package/src/{components → theme/components}/Preview/components/Triggers/Pv.js +32 -7
- package/src/{components → theme/components}/Preview/components/Triggers/SrcPv.js +1 -5
- package/src/theme/components/Preview/index.js +3 -0
- package/src/{components → theme/components}/ProjectsSection/index.js +279 -224
- package/src/{components → theme/components}/ProjectsSection/styles.module.css +21 -17
- package/src/{components → theme/components}/ScrollToTop/index.js +18 -21
- package/src/{components → theme/components}/ScrollToTop/styles.module.css +10 -9
- package/src/theme/components/SocialLinks/index.js +125 -0
- package/src/{components → theme/components}/SocialLinks/styles.module.css +9 -7
- package/src/{components → theme/components}/Tooltip/index.js +4 -1
- package/src/theme/config/iconMappings.js +465 -0
- package/src/theme/config/metaTags.js +239 -0
- package/src/theme/config/prism.js +179 -0
- package/src/theme/config/sidebar.js +17 -0
- package/src/{css → theme/css}/bootstrap.css +0 -1
- package/src/theme/css/catppuccin.css +618 -0
- package/src/{css → theme/css}/custom.css +3 -9
- package/src/{css → theme/css}/tasks.css +43 -37
- package/src/theme/{MDXComponents.js → overrides/MDXComponents.js} +3 -3
- package/src/theme/{Root.js → overrides/Root.js} +2 -4
- package/src/{pages → theme/pages}/index.js +23 -39
- package/src/theme/pages/notes.js +83 -0
- package/src/{pages → theme/pages}/tasks.js +115 -56
- package/src/{core/client-utils → theme/utils}/HashNavigation.js +60 -49
- package/src/{core/client-utils → theme/utils}/updateTitle.js +21 -25
- package/src/{core/build-utils → utils/build}/cssUtils.mjs +5 -3
- package/src/{core/build-utils → utils/build}/generateFavicon.mjs +44 -12
- package/src/{core/build-utils → utils/build}/generateRobotsTxt.mjs +4 -3
- package/src/{core/build-utils → utils/build}/iconExtractor.mjs +7 -3
- package/src/utils/build/imageDownloader.mjs +159 -0
- package/src/{core/build-utils → utils/build}/imageProcessor.mjs +5 -6
- package/src/utils/helpers.mjs +153 -0
- package/src/utils/logger.mjs +53 -0
- package/src/utils/packageManager.mjs +88 -0
- package/src/components/Preview/index.js +0 -3
- package/src/components/SocialLinks/index.js +0 -130
- package/src/config/iconMappings.js +0 -329
- package/src/config/metaTags.js +0 -240
- package/src/config/prism.js +0 -179
- package/src/config/sidebar.js +0 -20
- package/src/core/build-utils/imageDownloader.mjs +0 -98
- package/src/core/createDocuConf.mjs +0 -472
- package/src/core/defaults.mjs +0 -67
- package/src/core/logger.mjs +0 -17
- package/src/core/packageManager.mjs +0 -72
- package/src/css/catppuccin.css +0 -632
- package/src/pages/notes.js +0 -87
- /package/src/template/notes/{welcome.md → welcome.mdx} +0 -0
- /package/src/{components → theme/components}/NoteIndex/styles.module.css +0 -0
- /package/src/{components → theme/components}/Preview/components/FeedbackStates.js +0 -0
- /package/src/{components → theme/components}/Preview/components/FileTabs.js +0 -0
- /package/src/{components → theme/components}/Preview/components/Triggers/index.js +0 -0
- /package/src/{components → theme/components}/Preview/components/ViewerWindow.js +0 -0
- /package/src/{components → theme/components}/Preview/hooks/useDeepLinkHash.js +0 -0
- /package/src/{components → theme/components}/Preview/hooks/useDockLayout.js +0 -0
- /package/src/{components → theme/components}/Preview/hooks/useFileFetch.js +0 -0
- /package/src/{components → theme/components}/Preview/renderers/CodeRenderer.js +0 -0
- /package/src/{components → theme/components}/Preview/renderers/ImageRenderer.js +0 -0
- /package/src/{components → theme/components}/Preview/renderers/PdfRenderer.js +0 -0
- /package/src/{components → theme/components}/Preview/renderers/WebRenderer.js +0 -0
- /package/src/{components → theme/components}/Preview/state/index.js +0 -0
- /package/src/{components → theme/components}/Preview/styles.module.css +0 -0
- /package/src/{components → theme/components}/Preview/utils/index.js +0 -0
- /package/src/{components → theme/components}/Tooltip/styles.module.css +0 -0
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { logger } from "../utils/logger.mjs";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PortoRoot = path.resolve(__dirname, "../../");
|
|
9
|
+
|
|
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
|
+
// ─── Main Config Generator ─────────────────────────────────
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Creates a Docusaurus config from a user's Portosaurus config.
|
|
254
|
+
*
|
|
255
|
+
* @param {Object} rawUserConfig - The user's config object (export of config.js).
|
|
256
|
+
* May be { usrConf: {...} } or just {...}.
|
|
257
|
+
* @param {string} UserRoot - Absolute path to the user's project root.
|
|
258
|
+
* @returns {Object} Complete Docusaurus configuration object.
|
|
259
|
+
*/
|
|
260
|
+
export function buildDocuConfig(rawUserConfig, UserRoot) {
|
|
261
|
+
// Support both { usrConf: {...} } and direct config objects
|
|
262
|
+
const UserConfigRaw = rawUserConfig.usrConf ?? rawUserConfig;
|
|
263
|
+
|
|
264
|
+
// Load master template defaults for fallback resolution
|
|
265
|
+
const require = createRequire(import.meta.url);
|
|
266
|
+
const templateDefaults = require("../template/config.js").usrConf;
|
|
267
|
+
|
|
268
|
+
// Static directories — defined early so aliases can reference them
|
|
269
|
+
const UserStaticDir = path.resolve(UserRoot, "static");
|
|
270
|
+
const PortoAssetDir = path.resolve(PortoRoot, "src/assets");
|
|
271
|
+
|
|
272
|
+
// Hydrate the configuration by deep-merging user config over the template defaults.
|
|
273
|
+
// This ensures that even if the user deletes nested fields, the structure remains safe.
|
|
274
|
+
const merged = deepMerge(templateDefaults, UserConfigRaw);
|
|
275
|
+
|
|
276
|
+
// Resolve {{...}} template variables and @alias/ path prefixes in one pass
|
|
277
|
+
const UserConfig = resolveVars(merged, merged, {
|
|
278
|
+
"@porto/": PortoAssetDir,
|
|
279
|
+
"@site/": UserStaticDir,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Compute derived values
|
|
283
|
+
const siteUrl = resolveSiteUrl(UserConfig.site_url);
|
|
284
|
+
const basePath = resolveBasePath(UserConfig.site_path);
|
|
285
|
+
const version = getVersion();
|
|
286
|
+
|
|
287
|
+
const UserProjName = UserConfig.hero_section.title;
|
|
288
|
+
|
|
289
|
+
// Resolve internal paths
|
|
290
|
+
|
|
291
|
+
// Prism themes
|
|
292
|
+
let catppuccinMocha, catppuccinLatte;
|
|
293
|
+
try {
|
|
294
|
+
const prism = require(path.resolve(PortoRoot, "src/theme/config/prism.js"));
|
|
295
|
+
catppuccinMocha = prism.catppuccinMocha;
|
|
296
|
+
catppuccinLatte = prism.catppuccinLatte;
|
|
297
|
+
} catch {
|
|
298
|
+
catppuccinMocha = {};
|
|
299
|
+
catppuccinLatte = {};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Meta tags
|
|
303
|
+
let PortoMetaTags = [];
|
|
304
|
+
try {
|
|
305
|
+
const meta = require(path.resolve(PortoRoot, "src/theme/config/metaTags.js"));
|
|
306
|
+
PortoMetaTags = meta.PortoMetaTags ?? meta.metaTags ?? [];
|
|
307
|
+
} catch {
|
|
308
|
+
// OK — no meta tags
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Paths for content — these point directly to the user's project
|
|
312
|
+
const UserNotesDir = path.resolve(UserRoot, "notes");
|
|
313
|
+
const UserBlogDir = path.resolve(UserRoot, "blog");
|
|
314
|
+
|
|
315
|
+
const StaticDirs = [UserStaticDir, PortoAssetDir].filter((d) =>
|
|
316
|
+
fs.existsSync(d),
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Validates a static asset path with fallback.
|
|
321
|
+
* Aliases (@porto/, @site/) are already resolved globally by resolveAliases().
|
|
322
|
+
* - Remote URLs (http/https) pass through as-is.
|
|
323
|
+
* - Local paths are checked against UserStaticDir, then PortoAssetDir.
|
|
324
|
+
*/
|
|
325
|
+
function resolveStaticAsset(userPath, portoFallback) {
|
|
326
|
+
// Remote URLs are always valid
|
|
327
|
+
if (userPath && /^https?:\/\//.test(userPath)) return userPath;
|
|
328
|
+
|
|
329
|
+
// Check user's static directory
|
|
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
|
+
}
|
|
358
|
+
|
|
359
|
+
// Pages
|
|
360
|
+
const PortoPagesDir = path.resolve(PortoRoot, "src/theme/pages");
|
|
361
|
+
|
|
362
|
+
// Theme overrides
|
|
363
|
+
const PortoThemeDir = path.resolve(PortoRoot, "src/theme/overrides");
|
|
364
|
+
|
|
365
|
+
// Sidebar config
|
|
366
|
+
const sidebarPath = path.resolve(PortoRoot, "src/theme/config/sidebar.js");
|
|
367
|
+
|
|
368
|
+
// CSS
|
|
369
|
+
const PortoCustomCss = path.resolve(PortoRoot, "src/theme/css/custom.css");
|
|
370
|
+
|
|
371
|
+
// ───────────────────────Build the Docusaurus config ───────────────────────
|
|
372
|
+
|
|
373
|
+
const config = {
|
|
374
|
+
projectName: UserConfig.hero_section.title,
|
|
375
|
+
title: UserConfig.hero_section.title,
|
|
376
|
+
tagline: UserConfig.hero_section.description,
|
|
377
|
+
favicon: resolveStaticAsset(UserConfig.favicon, "favicon/favicon.ico"),
|
|
378
|
+
url: siteUrl,
|
|
379
|
+
baseUrl: basePath,
|
|
380
|
+
|
|
381
|
+
organizationName: UserConfig.hero_section.title,
|
|
382
|
+
deploymentBranch: "gh-pages",
|
|
383
|
+
onBrokenAnchors: "ignore",
|
|
384
|
+
onBrokenLinks: "warn",
|
|
385
|
+
|
|
386
|
+
i18n: {
|
|
387
|
+
defaultLocale: "en",
|
|
388
|
+
locales: ["en"],
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
headTags: PortoMetaTags,
|
|
392
|
+
|
|
393
|
+
customFields: {
|
|
394
|
+
version,
|
|
395
|
+
|
|
396
|
+
heroSection: {
|
|
397
|
+
profilePic: resolveStaticAsset(
|
|
398
|
+
UserConfig.hero_section.profile_pic,
|
|
399
|
+
"img/icon.png",
|
|
400
|
+
),
|
|
401
|
+
intro: UserConfig.hero_section.intro,
|
|
402
|
+
title: UserConfig.hero_section.title,
|
|
403
|
+
subtitle: UserConfig.hero_section.subtitle,
|
|
404
|
+
profession: UserConfig.hero_section.profession,
|
|
405
|
+
description: UserConfig.hero_section.description,
|
|
406
|
+
learnMoreButtonTxt: UserConfig.hero_section.learn_more_button_txt,
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
aboutMe: {
|
|
410
|
+
enable: UserConfig.about_me.enable,
|
|
411
|
+
image: resolveStaticAsset(
|
|
412
|
+
UserConfig.about_me.image ?? UserConfig.hero_section.profile_pic,
|
|
413
|
+
"img/icon.png",
|
|
414
|
+
),
|
|
415
|
+
description: UserConfig.about_me.description,
|
|
416
|
+
skills: UserConfig.about_me.skills,
|
|
417
|
+
resumeLink: UserConfig.about_me.resume_link,
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
projects: UserConfig.project_shelf,
|
|
421
|
+
experience: UserConfig.experience,
|
|
422
|
+
|
|
423
|
+
socialLinks: {
|
|
424
|
+
enable: UserConfig.social_links.enable,
|
|
425
|
+
links: UserConfig.social_links.links,
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
robotsTxt: {
|
|
429
|
+
enable: UserConfig.robots_txt,
|
|
430
|
+
rules: [{ disallow: ["/notes/", "/tasks/"] }],
|
|
431
|
+
customLines: [],
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
tasksPage: {
|
|
435
|
+
enable: UserConfig.tasks_page.enable,
|
|
436
|
+
title: UserConfig.tasks_page.title,
|
|
437
|
+
description: UserConfig.tasks_page.description,
|
|
438
|
+
taskList: UserConfig.tasks_page.tasks,
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
corsProxyList: [
|
|
442
|
+
...[].concat(UserConfig.cors_proxy || []),
|
|
443
|
+
"https://cors-proxy.soymadip.workers.dev/?url=",
|
|
444
|
+
"https://api.allorigins.win/raw?url=",
|
|
445
|
+
].filter(Boolean),
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
presets: [
|
|
449
|
+
[
|
|
450
|
+
require.resolve("@docusaurus/preset-classic", {
|
|
451
|
+
paths: [UserRoot, PortoRoot],
|
|
452
|
+
}),
|
|
453
|
+
{
|
|
454
|
+
docs: {
|
|
455
|
+
routeBasePath: "notes",
|
|
456
|
+
path: UserNotesDir,
|
|
457
|
+
sidebarPath,
|
|
458
|
+
admonitions: {
|
|
459
|
+
keywords: [
|
|
460
|
+
"note",
|
|
461
|
+
"tip",
|
|
462
|
+
"info",
|
|
463
|
+
"warning",
|
|
464
|
+
"danger",
|
|
465
|
+
"question",
|
|
466
|
+
],
|
|
467
|
+
extendDefaults: true,
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
blog: {
|
|
471
|
+
path: UserBlogDir,
|
|
472
|
+
feedOptions: UserConfig.rss
|
|
473
|
+
? { type: ["rss", "atom"], xslt: true }
|
|
474
|
+
: undefined,
|
|
475
|
+
showReadingTime: false,
|
|
476
|
+
onInlineTags: "warn",
|
|
477
|
+
onInlineAuthors: "warn",
|
|
478
|
+
onUntruncatedBlogPosts: "warn",
|
|
479
|
+
},
|
|
480
|
+
theme: {
|
|
481
|
+
customCss: PortoCustomCss,
|
|
482
|
+
},
|
|
483
|
+
pages: {
|
|
484
|
+
path: PortoPagesDir,
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
],
|
|
489
|
+
|
|
490
|
+
// Register portosaurus's src/theme/overrides as a theme directory
|
|
491
|
+
// so Root.js and MDXComponents are natively transpiled by Docusaurus.
|
|
492
|
+
themes: [
|
|
493
|
+
[
|
|
494
|
+
path.resolve(PortoRoot, "src/core/plugins/themePlugin.mjs"),
|
|
495
|
+
{
|
|
496
|
+
themeDir: PortoThemeDir,
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
],
|
|
500
|
+
|
|
501
|
+
markdown: {
|
|
502
|
+
mermaid: true,
|
|
503
|
+
hooks: {
|
|
504
|
+
onBrokenMarkdownLinks: "warn",
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
themeConfig: {
|
|
509
|
+
image: resolveStaticAsset(UserConfig.social_card, "img/social-card.jpeg"),
|
|
510
|
+
docs: {
|
|
511
|
+
sidebar: {
|
|
512
|
+
hideable: UserConfig.collapsable_sidebar,
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
imageZoom: {
|
|
516
|
+
options: {
|
|
517
|
+
margin: 2,
|
|
518
|
+
background: "rgba(var(--ifm-background-color-rgb), 0.9)",
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
colorMode: {
|
|
522
|
+
defaultMode: UserConfig.dark_mode ? "dark" : "light",
|
|
523
|
+
disableSwitch: UserConfig.disable_theme_switch,
|
|
524
|
+
},
|
|
525
|
+
navbar: {
|
|
526
|
+
title: UserProjName,
|
|
527
|
+
hideOnScroll: UserConfig.hide_navbar_on_scroll,
|
|
528
|
+
logo: {
|
|
529
|
+
alt: "Site Logo",
|
|
530
|
+
src: resolveStaticAsset(UserConfig.favicon, "favicon/favicon.ico"),
|
|
531
|
+
},
|
|
532
|
+
items: useEnabled([
|
|
533
|
+
{
|
|
534
|
+
type: "search",
|
|
535
|
+
position: "right",
|
|
536
|
+
className: "navbar-search-bar",
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
enable: UserConfig.about_me.enable,
|
|
540
|
+
value: {
|
|
541
|
+
label: "About Me",
|
|
542
|
+
to: "/#about",
|
|
543
|
+
position: "right",
|
|
544
|
+
activeBaseRegex: "^/#about",
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
enable: UserConfig.project_shelf.enable,
|
|
549
|
+
value: {
|
|
550
|
+
label: "Projects",
|
|
551
|
+
to: "/#projects",
|
|
552
|
+
position: "right",
|
|
553
|
+
activeBaseRegex: "^/#projects",
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
enable: UserConfig.experience.enable,
|
|
558
|
+
value: {
|
|
559
|
+
label: "Experience",
|
|
560
|
+
to: "/#experience",
|
|
561
|
+
position: "right",
|
|
562
|
+
activeBaseRegex: "^/#experience",
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
enable: UserConfig.social_links.enable,
|
|
567
|
+
value: {
|
|
568
|
+
label: "Contact",
|
|
569
|
+
to: "/#contact",
|
|
570
|
+
position: "right",
|
|
571
|
+
activeBaseRegex: "^/$contact",
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
type: "dropdown",
|
|
576
|
+
label: "More",
|
|
577
|
+
position: "right",
|
|
578
|
+
className: "_navbar-more-items",
|
|
579
|
+
items: useEnabled([
|
|
580
|
+
{ label: "Notes", to: "/notes" },
|
|
581
|
+
{ label: "Blog", to: "/blog" },
|
|
582
|
+
{
|
|
583
|
+
enable: UserConfig.tasks_page.enable,
|
|
584
|
+
value: { label: "Tasks", to: "/tasks" },
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
enable: !UserConfig.disable_branding,
|
|
588
|
+
value: {
|
|
589
|
+
label: `Portosaurus v${version}`,
|
|
590
|
+
className: "_nav-protosaurus-version",
|
|
591
|
+
to: "https://github.com/soymadip/portosaurus",
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
]),
|
|
595
|
+
},
|
|
596
|
+
]),
|
|
597
|
+
},
|
|
598
|
+
tableOfContents: {
|
|
599
|
+
minHeadingLevel: 2,
|
|
600
|
+
maxHeadingLevel: 4,
|
|
601
|
+
},
|
|
602
|
+
prism: {
|
|
603
|
+
theme: catppuccinLatte,
|
|
604
|
+
darkTheme: catppuccinMocha,
|
|
605
|
+
additionalLanguages: ["java", "php", "bash"],
|
|
606
|
+
},
|
|
607
|
+
footer: {},
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
plugins: [
|
|
611
|
+
function portosaurusWebpackTranspiler() {
|
|
612
|
+
return {
|
|
613
|
+
name: "portosaurus-transpile",
|
|
614
|
+
configureWebpack(_, isServer, utils) {
|
|
615
|
+
const jsLoader = utils.getJSLoader({ isServer });
|
|
616
|
+
jsLoader.options = { ...jsLoader.options, ignore: [] };
|
|
617
|
+
|
|
618
|
+
return {
|
|
619
|
+
mergeStrategy: { "module.rules": "prepend" },
|
|
620
|
+
resolve: {
|
|
621
|
+
alias: {
|
|
622
|
+
"@porto": path.resolve(PortoRoot, "src"),
|
|
623
|
+
portosaurus: PortoRoot,
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
module: {
|
|
627
|
+
rules: [
|
|
628
|
+
{
|
|
629
|
+
test: /\\.[jt]sx?$/,
|
|
630
|
+
include: [path.resolve(PortoRoot, "src")],
|
|
631
|
+
use: [jsLoader],
|
|
632
|
+
},
|
|
633
|
+
],
|
|
634
|
+
},
|
|
635
|
+
};
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
},
|
|
639
|
+
path.resolve(PortoRoot, "src/utils/build/generateFavicon.mjs"),
|
|
640
|
+
path.resolve(PortoRoot, "src/utils/build/generateRobotsTxt.mjs"),
|
|
641
|
+
[
|
|
642
|
+
require.resolve("@easyops-cn/docusaurus-search-local", {
|
|
643
|
+
paths: [UserRoot, PortoRoot],
|
|
644
|
+
}),
|
|
645
|
+
{
|
|
646
|
+
hashed: true,
|
|
647
|
+
indexDocs: true,
|
|
648
|
+
docsDir: UserNotesDir,
|
|
649
|
+
docsRouteBasePath: "notes",
|
|
650
|
+
searchContextByPaths: ["notes", "blog"],
|
|
651
|
+
highlightSearchTermsOnTargetPage: true,
|
|
652
|
+
explicitSearchResultPath: true,
|
|
653
|
+
hideSearchBarWithNoSearchContext: true,
|
|
654
|
+
language: ["en"],
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
"plugin-image-zoom",
|
|
658
|
+
],
|
|
659
|
+
|
|
660
|
+
staticDirectories: StaticDirs,
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
return config;
|
|
664
|
+
}
|
|
@@ -11,7 +11,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
11
11
|
* from inside the portosaurus package without the user needing
|
|
12
12
|
* to have them in their own project.
|
|
13
13
|
*/
|
|
14
|
-
export default function themePlugin(
|
|
14
|
+
export default function themePlugin(_context, options) {
|
|
15
15
|
return {
|
|
16
16
|
name: "portosaurus-theme",
|
|
17
17
|
|