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.
Files changed (91) hide show
  1. package/bin/portosaurus.mjs +14 -327
  2. package/package.json +16 -11
  3. package/src/cli/build.mjs +43 -0
  4. package/src/cli/dev.mjs +31 -0
  5. package/src/cli/init.mjs +135 -0
  6. package/src/cli/serve.mjs +30 -0
  7. package/src/core/buildDocuConfig.mjs +664 -0
  8. package/src/core/{themePlugin.mjs → plugins/themePlugin.mjs} +1 -1
  9. package/src/template/.github/workflows/deploy.yml +52 -0
  10. package/src/template/.nojekyll +0 -0
  11. package/src/template/README.md +58 -0
  12. package/src/template/blog/authors.yml +1 -1
  13. package/src/template/blog/welcome.md +1 -1
  14. package/src/template/config.js +40 -23
  15. package/src/template/package.json +20 -0
  16. package/src/template/static/img/svg/icon-blog.svg +2 -0
  17. package/src/template/static/img/svg/icon-note.svg +2 -0
  18. package/src/{components → theme/components}/AboutSection/index.js +22 -13
  19. package/src/{components → theme/components}/AboutSection/styles.module.css +59 -48
  20. package/src/{components → theme/components}/ContactSection/index.js +31 -24
  21. package/src/{components → theme/components}/ContactSection/styles.module.css +31 -26
  22. package/src/{components → theme/components}/ExperienceSection/index.js +12 -7
  23. package/src/{components → theme/components}/ExperienceSection/styles.module.css +23 -20
  24. package/src/{components → theme/components}/HeroSection/index.js +9 -11
  25. package/src/{components → theme/components}/HeroSection/styles.module.css +44 -32
  26. package/src/{components → theme/components}/NoteIndex/index.js +10 -3
  27. package/src/{components → theme/components}/Preview/components/PreviewHeader.js +14 -8
  28. package/src/{components → theme/components}/Preview/components/Triggers/Pv.js +32 -7
  29. package/src/{components → theme/components}/Preview/components/Triggers/SrcPv.js +1 -5
  30. package/src/theme/components/Preview/index.js +3 -0
  31. package/src/{components → theme/components}/ProjectsSection/index.js +279 -224
  32. package/src/{components → theme/components}/ProjectsSection/styles.module.css +21 -17
  33. package/src/{components → theme/components}/ScrollToTop/index.js +18 -21
  34. package/src/{components → theme/components}/ScrollToTop/styles.module.css +10 -9
  35. package/src/theme/components/SocialLinks/index.js +125 -0
  36. package/src/{components → theme/components}/SocialLinks/styles.module.css +9 -7
  37. package/src/{components → theme/components}/Tooltip/index.js +4 -1
  38. package/src/theme/config/iconMappings.js +465 -0
  39. package/src/theme/config/metaTags.js +239 -0
  40. package/src/theme/config/prism.js +179 -0
  41. package/src/theme/config/sidebar.js +17 -0
  42. package/src/{css → theme/css}/bootstrap.css +0 -1
  43. package/src/theme/css/catppuccin.css +618 -0
  44. package/src/{css → theme/css}/custom.css +3 -9
  45. package/src/{css → theme/css}/tasks.css +43 -37
  46. package/src/theme/{MDXComponents.js → overrides/MDXComponents.js} +3 -3
  47. package/src/theme/{Root.js → overrides/Root.js} +2 -4
  48. package/src/{pages → theme/pages}/index.js +23 -39
  49. package/src/theme/pages/notes.js +83 -0
  50. package/src/{pages → theme/pages}/tasks.js +115 -56
  51. package/src/{core/client-utils → theme/utils}/HashNavigation.js +60 -49
  52. package/src/{core/client-utils → theme/utils}/updateTitle.js +21 -25
  53. package/src/{core/build-utils → utils/build}/cssUtils.mjs +5 -3
  54. package/src/{core/build-utils → utils/build}/generateFavicon.mjs +44 -12
  55. package/src/{core/build-utils → utils/build}/generateRobotsTxt.mjs +4 -3
  56. package/src/{core/build-utils → utils/build}/iconExtractor.mjs +7 -3
  57. package/src/utils/build/imageDownloader.mjs +159 -0
  58. package/src/{core/build-utils → utils/build}/imageProcessor.mjs +5 -6
  59. package/src/utils/helpers.mjs +153 -0
  60. package/src/utils/logger.mjs +53 -0
  61. package/src/utils/packageManager.mjs +88 -0
  62. package/src/components/Preview/index.js +0 -3
  63. package/src/components/SocialLinks/index.js +0 -130
  64. package/src/config/iconMappings.js +0 -329
  65. package/src/config/metaTags.js +0 -240
  66. package/src/config/prism.js +0 -179
  67. package/src/config/sidebar.js +0 -20
  68. package/src/core/build-utils/imageDownloader.mjs +0 -98
  69. package/src/core/createDocuConf.mjs +0 -472
  70. package/src/core/defaults.mjs +0 -67
  71. package/src/core/logger.mjs +0 -17
  72. package/src/core/packageManager.mjs +0 -72
  73. package/src/css/catppuccin.css +0 -632
  74. package/src/pages/notes.js +0 -87
  75. /package/src/template/notes/{welcome.md → welcome.mdx} +0 -0
  76. /package/src/{components → theme/components}/NoteIndex/styles.module.css +0 -0
  77. /package/src/{components → theme/components}/Preview/components/FeedbackStates.js +0 -0
  78. /package/src/{components → theme/components}/Preview/components/FileTabs.js +0 -0
  79. /package/src/{components → theme/components}/Preview/components/Triggers/index.js +0 -0
  80. /package/src/{components → theme/components}/Preview/components/ViewerWindow.js +0 -0
  81. /package/src/{components → theme/components}/Preview/hooks/useDeepLinkHash.js +0 -0
  82. /package/src/{components → theme/components}/Preview/hooks/useDockLayout.js +0 -0
  83. /package/src/{components → theme/components}/Preview/hooks/useFileFetch.js +0 -0
  84. /package/src/{components → theme/components}/Preview/renderers/CodeRenderer.js +0 -0
  85. /package/src/{components → theme/components}/Preview/renderers/ImageRenderer.js +0 -0
  86. /package/src/{components → theme/components}/Preview/renderers/PdfRenderer.js +0 -0
  87. /package/src/{components → theme/components}/Preview/renderers/WebRenderer.js +0 -0
  88. /package/src/{components → theme/components}/Preview/state/index.js +0 -0
  89. /package/src/{components → theme/components}/Preview/styles.module.css +0 -0
  90. /package/src/{components → theme/components}/Preview/utils/index.js +0 -0
  91. /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(context, options) {
14
+ export default function themePlugin(_context, options) {
15
15
  return {
16
16
  name: "portosaurus-theme",
17
17