bunki 0.4.2 → 0.5.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/dist/cli.js CHANGED
@@ -32690,42 +32690,65 @@ core_default.registerLanguage("diff", diff);
32690
32690
  core_default.registerLanguage("python", python);
32691
32691
  core_default.registerLanguage("json", json);
32692
32692
  core_default.registerLanguage("swift", swift);
32693
- var marked = new B(markedHighlight({
32694
- emptyLangClass: "hljs",
32695
- langPrefix: "hljs language-",
32696
- highlight(code, lang, info) {
32697
- const language = core_default.getLanguage(lang) ? lang : "json";
32698
- return core_default.highlight(code, { language }).value;
32699
- }
32700
- }));
32701
- marked.setOptions({
32702
- gfm: true,
32703
- breaks: true
32704
- });
32705
- marked.use({
32706
- walkTokens(token) {
32707
- if (token.type === "link") {
32708
- token.href = token.href || "";
32709
- const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
32710
- if (isExternal) {
32711
- token.isExternalLink = true;
32712
- if (token.href.includes("youtube.com/watch") || token.href.includes("youtu.be/")) {
32713
- token.isYouTubeLink = true;
32714
- }
32715
- }
32716
- }
32717
- },
32718
- hooks: {
32719
- preprocess(markdown2) {
32720
- return markdown2;
32693
+ var noFollowExceptions = new Set;
32694
+ function createMarked() {
32695
+ const marked = new B(markedHighlight({
32696
+ emptyLangClass: "hljs",
32697
+ langPrefix: "hljs language-",
32698
+ highlight(code, lang, info) {
32699
+ const language = core_default.getLanguage(lang) ? lang : "json";
32700
+ return core_default.highlight(code, { language }).value;
32701
+ }
32702
+ }));
32703
+ marked.setOptions({
32704
+ gfm: true,
32705
+ breaks: true
32706
+ });
32707
+ marked.use({
32708
+ walkTokens(token) {
32709
+ if (token.type === "link") {
32710
+ token.href = token.href || "";
32711
+ const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
32712
+ if (isExternal) {
32713
+ token.isExternalLink = true;
32714
+ if (token.href.includes("youtube.com/watch") || token.href.includes("youtu.be/")) {
32715
+ token.isYouTubeLink = true;
32716
+ }
32717
+ }
32718
+ }
32721
32719
  },
32722
- postprocess(html) {
32723
- html = html.replace(/<a href="(https?:\/\/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)[^"]*)"[^>]*>(.*?)<\/a>/g, '<div class="video-container"><iframe src="https://www.youtube.com/embed/$4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>');
32724
- html = html.replace(/<img /g, '<img loading="lazy" ');
32725
- return html.replace(/<a href="(https?:\/\/|\/\/)([^"]+)"/g, '<a href="$1$2" target="_blank" rel="noopener noreferrer"');
32720
+ hooks: {
32721
+ preprocess(markdown2) {
32722
+ return markdown2;
32723
+ },
32724
+ postprocess(html) {
32725
+ html = html.replace(/<a href="(https?:\/\/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)[^"]*)"[^>]*>(.*?)<\/a>/g, '<div class="video-container"><iframe src="https://www.youtube.com/embed/$4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>');
32726
+ html = html.replace(/<img /g, '<img loading="lazy" ');
32727
+ return html.replace(/<a href="(https?:\/\/|\/\/)([^"]+)"/g, (match, protocol, rest) => {
32728
+ const fullUrl = protocol + rest;
32729
+ let relAttr = 'rel="noopener noreferrer';
32730
+ try {
32731
+ const url = new URL(fullUrl);
32732
+ const domain = url.hostname.replace(/^www\./, "");
32733
+ if (!noFollowExceptions.has(domain)) {
32734
+ relAttr += " nofollow";
32735
+ }
32736
+ } catch {
32737
+ relAttr += " nofollow";
32738
+ }
32739
+ relAttr += '"';
32740
+ return `<a href="${fullUrl}" target="_blank" ${relAttr}`;
32741
+ });
32742
+ }
32726
32743
  }
32727
- }
32728
- });
32744
+ });
32745
+ return marked;
32746
+ }
32747
+ var marked = createMarked();
32748
+ function setNoFollowExceptions(exceptions) {
32749
+ noFollowExceptions = new Set(exceptions.map((domain) => domain.toLowerCase().replace(/^www\./, "")));
32750
+ marked = createMarked();
32751
+ }
32729
32752
  function extractExcerpt(content, maxLength = 200) {
32730
32753
  const plainText = content.replace(/^#.*$/gm, "").replace(/```[\s\S]*?```/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[*_]{1,2}([^*_]+)[*_]{1,2}/g, "$1").replace(/\n+/g, " ").trim();
32731
32754
  if (plainText.length <= maxLength) {
@@ -33055,6 +33078,9 @@ class SiteGenerator {
33055
33078
  async initialize() {
33056
33079
  console.log("Initializing site generator...");
33057
33080
  await ensureDir(this.options.outputDir);
33081
+ if (this.options.config.noFollowExceptions) {
33082
+ setNoFollowExceptions(this.options.config.noFollowExceptions);
33083
+ }
33058
33084
  let tagDescriptions = {};
33059
33085
  const tagsTomlPath = path5.join(process.cwd(), "src", "tags.toml");
33060
33086
  const tagsTomlFile = Bun.file(tagsTomlPath);
package/dist/index.js CHANGED
@@ -30335,42 +30335,65 @@ core_default.registerLanguage("diff", diff);
30335
30335
  core_default.registerLanguage("python", python);
30336
30336
  core_default.registerLanguage("json", json);
30337
30337
  core_default.registerLanguage("swift", swift);
30338
- var marked = new B(markedHighlight({
30339
- emptyLangClass: "hljs",
30340
- langPrefix: "hljs language-",
30341
- highlight(code, lang, info) {
30342
- const language = core_default.getLanguage(lang) ? lang : "json";
30343
- return core_default.highlight(code, { language }).value;
30344
- }
30345
- }));
30346
- marked.setOptions({
30347
- gfm: true,
30348
- breaks: true
30349
- });
30350
- marked.use({
30351
- walkTokens(token) {
30352
- if (token.type === "link") {
30353
- token.href = token.href || "";
30354
- const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
30355
- if (isExternal) {
30356
- token.isExternalLink = true;
30357
- if (token.href.includes("youtube.com/watch") || token.href.includes("youtu.be/")) {
30358
- token.isYouTubeLink = true;
30359
- }
30360
- }
30361
- }
30362
- },
30363
- hooks: {
30364
- preprocess(markdown2) {
30365
- return markdown2;
30338
+ var noFollowExceptions = new Set;
30339
+ function createMarked() {
30340
+ const marked = new B(markedHighlight({
30341
+ emptyLangClass: "hljs",
30342
+ langPrefix: "hljs language-",
30343
+ highlight(code, lang, info) {
30344
+ const language = core_default.getLanguage(lang) ? lang : "json";
30345
+ return core_default.highlight(code, { language }).value;
30346
+ }
30347
+ }));
30348
+ marked.setOptions({
30349
+ gfm: true,
30350
+ breaks: true
30351
+ });
30352
+ marked.use({
30353
+ walkTokens(token) {
30354
+ if (token.type === "link") {
30355
+ token.href = token.href || "";
30356
+ const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
30357
+ if (isExternal) {
30358
+ token.isExternalLink = true;
30359
+ if (token.href.includes("youtube.com/watch") || token.href.includes("youtu.be/")) {
30360
+ token.isYouTubeLink = true;
30361
+ }
30362
+ }
30363
+ }
30366
30364
  },
30367
- postprocess(html) {
30368
- html = html.replace(/<a href="(https?:\/\/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)[^"]*)"[^>]*>(.*?)<\/a>/g, '<div class="video-container"><iframe src="https://www.youtube.com/embed/$4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>');
30369
- html = html.replace(/<img /g, '<img loading="lazy" ');
30370
- return html.replace(/<a href="(https?:\/\/|\/\/)([^"]+)"/g, '<a href="$1$2" target="_blank" rel="noopener noreferrer"');
30365
+ hooks: {
30366
+ preprocess(markdown2) {
30367
+ return markdown2;
30368
+ },
30369
+ postprocess(html) {
30370
+ html = html.replace(/<a href="(https?:\/\/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)[^"]*)"[^>]*>(.*?)<\/a>/g, '<div class="video-container"><iframe src="https://www.youtube.com/embed/$4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>');
30371
+ html = html.replace(/<img /g, '<img loading="lazy" ');
30372
+ return html.replace(/<a href="(https?:\/\/|\/\/)([^"]+)"/g, (match, protocol, rest) => {
30373
+ const fullUrl = protocol + rest;
30374
+ let relAttr = 'rel="noopener noreferrer';
30375
+ try {
30376
+ const url = new URL(fullUrl);
30377
+ const domain = url.hostname.replace(/^www\./, "");
30378
+ if (!noFollowExceptions.has(domain)) {
30379
+ relAttr += " nofollow";
30380
+ }
30381
+ } catch {
30382
+ relAttr += " nofollow";
30383
+ }
30384
+ relAttr += '"';
30385
+ return `<a href="${fullUrl}" target="_blank" ${relAttr}`;
30386
+ });
30387
+ }
30371
30388
  }
30372
- }
30373
- });
30389
+ });
30390
+ return marked;
30391
+ }
30392
+ var marked = createMarked();
30393
+ function setNoFollowExceptions(exceptions) {
30394
+ noFollowExceptions = new Set(exceptions.map((domain) => domain.toLowerCase().replace(/^www\./, "")));
30395
+ marked = createMarked();
30396
+ }
30374
30397
  function extractExcerpt(content, maxLength = 200) {
30375
30398
  const plainText = content.replace(/^#.*$/gm, "").replace(/```[\s\S]*?```/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[*_]{1,2}([^*_]+)[*_]{1,2}/g, "$1").replace(/\n+/g, " ").trim();
30376
30399
  if (plainText.length <= maxLength) {
@@ -30927,6 +30950,9 @@ class SiteGenerator {
30927
30950
  async initialize() {
30928
30951
  console.log("Initializing site generator...");
30929
30952
  await ensureDir(this.options.outputDir);
30953
+ if (this.options.config.noFollowExceptions) {
30954
+ setNoFollowExceptions(this.options.config.noFollowExceptions);
30955
+ }
30930
30956
  let tagDescriptions = {};
30931
30957
  const tagsTomlPath = path5.join(process.cwd(), "src", "tags.toml");
30932
30958
  const tagsTomlFile = Bun.file(tagsTomlPath);
package/dist/types.d.ts CHANGED
@@ -56,6 +56,8 @@ export interface SiteConfig {
56
56
  css?: CSSConfig;
57
57
  /** Optional number of tags to display on homepage (sorted by count). If not set, all tags are shown */
58
58
  maxTagsOnHomepage?: number;
59
+ /** Optional list of domains to exclude from nofollow attribute. Links to these domains will have follow attribute. */
60
+ noFollowExceptions?: string[];
59
61
  /** Additional custom configuration options */
60
62
  [key: string]: any;
61
63
  }
@@ -1,4 +1,5 @@
1
1
  import { Post } from "../types";
2
+ export declare function setNoFollowExceptions(exceptions: string[]): void;
2
3
  export declare function extractExcerpt(content: string, maxLength?: number): string;
3
4
  export declare function convertMarkdownToHtml(markdownContent: string): string;
4
5
  export declare function parseMarkdownFile(filePath: string): Promise<Post | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunki",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "An opinionated static site generator built with Bun featuring PostCSS integration and modern web development workflows",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",