vuepress-plugin-feed2
Advanced tools
Comparing version 2.0.0-beta.7 to 2.0.0-beta.8
@@ -341,10 +341,4 @@ import { Page, Plugin, PluginConfig } from '@vuepress/core'; | ||
interface FeedOptions { | ||
interface BaseFeedOptions { | ||
/** | ||
* Deploy hostname | ||
* | ||
* 部署的域名 | ||
*/ | ||
hostname: string; | ||
/** | ||
* Whether to output Atom syntax files. | ||
@@ -446,2 +440,12 @@ * | ||
} | ||
interface FeedOptions extends BaseFeedOptions { | ||
/** | ||
* Deploy hostname | ||
* | ||
* 部署的域名 | ||
*/ | ||
hostname: string; | ||
/** Locales for feed */ | ||
locales?: Record<string, BaseFeedOptions>; | ||
} | ||
@@ -451,2 +455,2 @@ declare const feedPlugin: Plugin<FeedOptions>; | ||
export { FeedAuthor, FeedCategory, FeedChannelOption, FeedContributor, FeedEnclosure, FeedFrontmatterOption, FeedGetter, FeedInitOptions, FeedItemOption, FeedLinks, FeedOptions, FeedPluginFrontmatter, feedPlugin as default, feed, feedPlugin }; | ||
export { BaseFeedOptions, FeedAuthor, FeedCategory, FeedChannelOption, FeedContributor, FeedEnclosure, FeedFrontmatterOption, FeedGetter, FeedInitOptions, FeedItemOption, FeedLinks, FeedOptions, FeedPluginFrontmatter, feedPlugin as default, feed, feedPlugin }; |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("@mr-hope/vuepress-shared"),e=require("@vuepress/utils"),i=require("path"),r=require("xml-js");function a(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach((function(i){if("default"!==i){var r=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,r.get?r:{enumerable:!0,get:function(){return t[i]}})}})),e.default=t,Object.freeze(e)}var n=a(r);const o=new t.Logger("vuepress-plugin-feed2"),s=(t,e)=>t&&t instanceof Date?e&&e instanceof Date?e.getTime()-t.getTime():-1:1,u=t=>t.replace(/ class=".*?"/gu,"").replace(/ v-pre/gu,"").replace(/<a href="#.*?">.*?<\/a>/gu,"").replace(/(<!--.*?--!?>)|(<!--[\S\s]+?--!?>)|(<!--[\S\s]*?$)/gu,"").replace(/<OutboundLink ?\/>/gu,"").replace(/<RouterLink to="(.*?)">(.*?)<\/RouterLink>/gu,'<a href="$1">$2</a>').replace(/<(?:a|div|span)[^>]*?\/>/gu,"").replace(/<(Badge|FlowChart|Presentation).*?(?:>.*?<\/\1>|\/>)/gu,"<i>Content not supported</i>").replace(/<math[\s\S]*?\/math>/gu,"<i>Content not supported</i>"),p=(t,e="",i="")=>`${t}${e.replace(/^\/?/u,"/").replace(/\/?$/u,"/")}${i.replace(/^\//u,"")}`,c=(t="")=>`image/${"jpg"===t?"jpeg":"svg"===t?"svg+xml":"jpeg"===t||"png"===t||"bmp"===t||"gif"===t||"webp"===t?t:""}`,h=t=>({atomOutputFilename:t.atomOutputFilename||"atom.xml",jsonOutputFilename:t.jsonOutputFilename||"feed.json",rssOutputFilename:t.rssOutputFilename||"rss.xml"}),l=(t,e)=>{const{base:i}=t.options,{hostname:r}=e,{atomOutputFilename:a,jsonOutputFilename:n,rssOutputFilename:o}=h(e);return{atom:p(r,i,a),json:p(r,i,n),rss:p(r,i,o)}},d=e=>{const{name:i,email:r,url:a}=e;return{name:i,...r?{email:r}:{},...a?{uri:t.encodeXML(a)}:{}}},g=t=>{const{name:e,scheme:i}=t;return{_attributes:{term:e,scheme:i}}},m=t=>({name:t.name,...t.url?{url:t.url}:{},...t.avatar?{avatar:t.avatar}:{}}),f=t=>{const{name:e,domain:i}=t;return{_text:e,...i?{_attributes:{domain:i}}:{}}},y=e=>{const i=e.guid||t.encodeXML(e.link);return{...t.isUrl(i)?{}:{_attributes:{isPermaLink:!1}},_text:i}};class b{constructor(e){this.options=e,this.items=[],this.categories=new Set,this._contributorKeys=new Set,this.contributors=[],this.addItem=t=>{this.items.push(t)},this.addCategory=t=>{this.categories.add(t)},this.addContributor=t=>{const e=t.email||t.name;e&&!this._contributorKeys.has(e)&&(this._contributorKeys.add(e),this.contributors.push(t))},this.atom=()=>(e=>{const{channel:i,links:a}=e.options,n={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}},feed:{_attributes:{xmlns:"http://www.w3.org/2005/Atom",...i.language?{"xml:lang":i.language}:{}},id:i.link,title:i.title,...i.description?{subtitle:i.description}:{},...i.author?{author:d(i.author)}:{},updated:i.lastUpdated?i.lastUpdated.toISOString():(new Date).toISOString(),generator:"vuepress-plugin-feed2",link:[{_attributes:{rel:"self",href:t.encodeXML(a.atom)}}]}};return i.link&&n.feed.link.push({_attributes:{rel:"alternate",href:t.encodeXML(i.link)}}),i.hub&&n.feed.link.push({_attributes:{rel:"hub",href:t.encodeXML(i.hub)}}),i.image&&(n.feed.logo=i.image),i.icon&&(n.feed.icon=i.icon),i.copyright&&(n.feed.rights=i.copyright),n.feed.category=Array.from(e.categories).map((t=>({_attributes:{term:t}}))),n.feed.contributor=Array.from(e.contributors).filter((t=>t.name)).map((t=>d(t))),n.feed.entry=e.items.map((e=>{const i={title:{_attributes:{type:"html"},_text:t.encodeXML(e.title)},id:t.encodeXML(e.guid||e.link),link:{_attributes:{href:t.encodeXML(e.link)}},updated:e.lastUpdated.toISOString()};return e.description&&(i.summary={_attributes:{type:"html"},_cdata:t.encodeCDATA(e.description)}),e.content&&(i.content={_attributes:{type:"html"},_cdata:t.encodeCDATA(e.content)}),Array.isArray(e.author)?i.author=e.author.filter((t=>t.name)).map((t=>d(t))):e.author&&e.author.name&&(i.author=[d(e.author)]),Array.isArray(e.category)?i.category=e.category.map((t=>g(t))):e.category&&(i.category=[g(e.category)]),Array.isArray(e.contributor)&&(i.contributor=e.contributor.map((t=>d(t)))),e.pubDate&&(i.published=e.pubDate.toISOString()),e.copyright&&(i.rights=e.copyright),i})),r.js2xml(n,{compact:!0,ignoreComment:!0,spaces:2})})(this),this.rss=()=>(e=>{const{links:i,channel:r}=e.options;let a=!1;const o={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}},rss:{_attributes:{version:"2.0","xmlns:atom":"http://www.w3.org/2005/Atom"},channel:{"atom:link":{_attributes:{href:i.rss,rel:"self",type:"application/rss+xml"}},title:{_text:r.title},link:{_text:t.encodeXML(r.link)},description:{_text:r.description},language:{_text:t.encodeXML(r.language)},pubDate:{_text:r.pubDate?r.pubDate.toUTCString():(new Date).toUTCString()},lastBuildDate:{_text:r.lastUpdated?r.lastUpdated.toUTCString():(new Date).toUTCString()},generator:{_text:"vuepress-plugin-feed2"},docs:{_text:"https://validator.w3.org/feed/docs/rss2.html"}}}};return r.copyright&&(o.rss.channel.copyright={_text:r.copyright}),r.ttl&&(o.rss.channel.ttl={_text:r.ttl.toString()}),r.image&&(o.rss.channel.image={title:{_text:r.title},url:{_text:r.image},link:{_text:t.encodeXML(r.link)}}),o.rss.channel.category=Array.from(e.categories).map((t=>({_text:t}))),o.rss.channel.item=e.items.map((e=>{const r={title:{_text:t.encodeXML(e.title)},link:{_text:t.encodeXML(e.link)},guid:y(e),source:{_attributes:{url:i.rss},_text:e.title}};if(e.description&&(r.description={_text:t.encodeXML(e.description)}),Array.isArray(e.author)){const t=e.author.find((t=>t.email&&t.name));t&&(r.author={_text:`${t.email} (${t.name})`})}else if("object"==typeof e.author){const{name:t,email:i}=e.author;i&&t&&(r.author={_text:`${i} (${t})`})}var n;return Array.isArray(e.category)?r.category=e.category.filter((t=>t.name)).map((t=>f(t))):"object"==typeof e.category&&e.category.name&&(r.category=[f(e.category)]),e.comments&&(r.comments={_text:t.encodeXML(e.link)}),e.pubDate&&(r.pubDate={_text:e.pubDate.toUTCString()}),e.content&&(a=!0,r["content:encoded"]={_cdata:t.encodeCDATA(e.content)}),e.enclosure&&(r.enclosure={_attributes:{url:(n=e.enclosure).url,...n.length?{length:n.length}:{},...n.type?{type:n.type}:{}}}),r})),a&&(o.rss._attributes["xmlns:content"]="http://purl.org/rss/1.0/modules/content/",o.rss._attributes["xmlns:dc"]="http://purl.org/dc/elements/1.1/"),n.js2xml(o,{compact:!0,ignoreComment:!0,spaces:2})})(this),this.json=()=>(t=>{var e;const{channel:i,links:r}=t.options,a={version:"https://jsonfeed.org/version/1.1",title:i.title,home_page_url:i.link,feed_url:r.json};return i.description&&(a.description=i.description),i.image&&(a.icon=i.image),i.icon&&(a.favicon=i.icon),(null===(e=i.author)||void 0===e?void 0:e.name)&&(a.author={name:i.author.name,...i.author.url?{url:i.author.url}:{},...i.author.avatar?{avatar:i.author.avatar}:{}}),a.items=t.items.map((t=>{const e={title:t.title,url:t.link,id:t.guid||t.link,...t.description?{summary:t.description}:{},content_html:t.content};return t.image&&(e.image=t.image),t.pubDate&&(e.date_published=t.pubDate.toISOString()),t.lastUpdated&&(e.date_modified=t.lastUpdated.toISOString()),Array.isArray(t.author)?e.authors=t.author.filter((t=>t.name)).map((t=>m(t))):"object"==typeof t.author&&(e.authors=[m(t.author)]),Array.isArray(t.category)?e.tags=t.category.filter((t=>t.name)).map((t=>t.name)):t.category&&(e.tags=[t.category.name]),e})),JSON.stringify(a,null,2)})(this)}}class _{constructor(t,e,i,r){this.app=t,this.options=e,this.page=i,this.feed=r,this.base=this.app.options.base,this.frontmatter=i.frontmatter,this.getter=e.getter||{},this.pageFeedOptions=this.frontmatter.feed||{}}get title(){return"function"==typeof this.getter.title?this.getter.title(this.page):this.pageFeedOptions.title||this.page.title}get link(){return"function"==typeof this.getter.link?this.getter.link(this.page):p(this.options.hostname,this.base,this.page.path)}get description(){return"function"==typeof this.getter.description?this.getter.description(this.page):this.pageFeedOptions.description?this.pageFeedOptions.description:this.frontmatter.description?this.frontmatter.description:this.page.excerpt?u(this.app.markdown.render(this.page.excerpt)):void 0}get author(){var e,i;return"function"==typeof this.getter.author?this.getter.author(this.page):Array.isArray(this.pageFeedOptions.author)?this.pageFeedOptions.author:"object"==typeof this.pageFeedOptions.author?[this.pageFeedOptions.author]:!1===this.frontmatter.author?[]:this.frontmatter.author?t.getAuthor(this.frontmatter.author):(null===(e=this.options.channel)||void 0===e?void 0:e.author)?t.getAuthor(null===(i=this.options.channel)||void 0===i?void 0:i.author):[]}get category(){if("function"==typeof this.getter.category)return this.getter.category(this.page);if(Array.isArray(this.pageFeedOptions.category))return this.pageFeedOptions.category;if("object"==typeof this.pageFeedOptions.category)return[this.pageFeedOptions.category];const{categories:e,category:i=e}=this.frontmatter;return t.getCategory(i).map((t=>({name:t})))}get enclosure(){return"function"==typeof this.getter.enclosure?this.getter.enclosure(this.page):this.image?{url:this.image,type:c(this.image.split(".").pop())}:void 0}get guid(){return this.pageFeedOptions.guid||this.link}get pubDate(){if("function"==typeof this.getter.publishDate)return this.getter.publishDate(this.page);const{time:t,date:e=t}=this.page.frontmatter,{createdTime:i}=this.page.git||{};return e&&e instanceof Date?e:i?new Date(i):void 0}get lastUpdated(){if("function"==typeof this.getter.lastUpdateDate)return this.getter.lastUpdateDate(this.page);const{updatedTime:t}=this.page.git||{};return t?new Date(t):new Date}get content(){return"function"==typeof this.getter.content?this.getter.content(this.page):this.pageFeedOptions.content?this.pageFeedOptions.content:u(this.page.contentRendered)}get image(){if("function"==typeof this.getter.image)return this.getter.image(this.page);const{banner:e,cover:i}=this.frontmatter;if(e){if(t.isAbsoluteUrl(e))return p(this.options.hostname,this.app.options.base,e);if(t.isUrl(e))return e}if(i){if(t.isAbsoluteUrl(i))return p(this.options.hostname,this.app.options.base,i);if(t.isUrl(i))return i}const r=/!\[.*?\]\((.*?)\)/iu.exec(this.page.content);if(r){if(t.isAbsoluteUrl(r[1]))return p(this.options.hostname,this.app.options.base,r[1]);if(t.isUrl(r[1]))return r[1]}}get contributor(){return"function"==typeof this.getter.contributor?this.getter.contributor(this.page):Array.isArray(this.pageFeedOptions.contributor)?this.pageFeedOptions.contributor:"object"==typeof this.pageFeedOptions.contributor?[this.pageFeedOptions.contributor]:this.author}get copyright(){if("function"==typeof this.getter.copyright)return this.getter.copyright(this.page);if(this.frontmatter.copyright)return this.frontmatter.copyright;const t=this.author[0];return t&&t.name?`Copyright by ${t.name}`:void 0}getFeedItem(){const{author:t,category:e,content:i,contributor:r,copyright:a,description:n,enclosure:o,guid:s,image:u,lastUpdated:p,link:c,pubDate:h,title:l}=this;return l||n?(e&&e.forEach((t=>this.feed.addCategory(t.name))),r&&r.forEach((t=>this.feed.addContributor(t))),{title:l,link:c,description:n,author:t,category:e,enclosure:o,guid:s,pubDate:h,lastUpdated:p,content:i,image:u,contributor:r,copyright:a}):null}}class v{constructor(t,e,i,r){this.app=t,this.options=e,this.pages=i,this.feed=new b(r)}addPages(){let t=0;for(const e of this.pages){const i=new _(this.app,this.options,e,this.feed).getFeedItem();i&&(this.feed.addItem(i),t+=1)}o.succeed(`added ${e.chalk.cyan(`${t} page(s)`)} as feed item(s)`)}async generateFeed(){const{dest:t}=this.app.dir,{atomOutputFilename:r,jsonOutputFilename:a,rssOutputFilename:n}=h(this.options);this.addPages(),this.options.atom&&(o.load("Generating Atom Feed"),await e.fs.ensureDir(i.dirname(t(r))),await e.fs.outputFile(t(r),this.feed.atom()),o.update(`Atom feed file generated and saved to ${e.chalk.cyan(r)}`),o.succeed()),this.options.json&&(o.load("Generating JSON Feed"),await e.fs.ensureDir(i.dirname(t(a))),await e.fs.outputFile(t(a),this.feed.json()),o.update(`JSON feed file generated and saved to ${e.chalk.cyan(a)}`),o.succeed()),this.options.rss&&(o.load("Generating RSS Feed"),await e.fs.ensureDir(i.dirname(t(n))),await e.fs.outputFile(t(n),this.feed.rss()),o.update(`RSS feed file generated and saved to ${e.chalk.cyan(n)}`),o.succeed())}}const O=(e,i)=>{if(!e.hostname)return o.error("Option 'hostname' is required!"),{name:"vuepress-plugin-feed2"};e.hostname=e.hostname.replace(/\/?$/u,"");const r=e,a=((e,i)=>{var r,a;const{hostname:n,icon:o,image:s}=i,{base:u}=e.options,{title:c,description:h}=e.siteData,l=null===(a=null===(r=i.channel)||void 0===r?void 0:r.author)||void 0===a?void 0:a.name,d={title:c,link:p(n,u),description:h,language:t.getRootLang(e),copyright:l?`Copyright by ${l}`:"",pubDate:new Date,lastUpdated:new Date,...o?{icon:o}:{},...s?{image:s}:{},...l?{author:{name:l}}:{}};return t.deepAssign(d,i.channel||{})})(i,r);return r.atom||r.json||r.rss?{name:"vuepress-plugin-feed2",onPrepared:t=>((t,e)=>{const{base:i}=t.options,{siteData:r}=t,{atomOutputFilename:a,jsonOutputFilename:n,rssOutputFilename:o}=h(e),s=(t,a,n)=>["link",{rel:"alternate",type:n,href:p(e.hostname,i,a),title:`${r.title||""} ${t} Feed`}];r.head||(r.head=[]),e.atom&&r.head.push(s("Atom",a,"application/atom+xml")),e.json&&r.head.push(s("JSON",n,"application/json")),e.rss&&r.head.push(s("RSS",o,"application/rss+xml"))})(t,r),onGenerated:async t=>{const{filter:e=(({frontmatter:t,filePathRelative:e})=>!(t.home||!e||!1===t.article||!1===t.feed)),sorter:i=((t,e)=>{var i,r,a,n;return s((null===(i=t.git)||void 0===i?void 0:i.createdTime)?new Date(null===(r=t.git)||void 0===r?void 0:r.createdTime):t.frontmatter.date,(null===(a=e.git)||void 0===a?void 0:a.createdTime)?new Date(null===(n=e.git)||void 0===n?void 0:n.createdTime):e.frontmatter.date)})}=r,n=t.pages.filter(e).sort(i).slice(0,r.count||100);await new v(t,r,n,{channel:a,links:l(t,r)}).generateFeed()}}:(o.info("No requested output, the plugin won't start!"),{name:"vuepress-plugin-feed2"})};exports.default=O,exports.feed=t=>["feed2",t],exports.feedPlugin=O; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("@mr-hope/vuepress-shared"),e=require("@vuepress/utils"),i=require("path"),r=require("xml-js");function a(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach((function(i){if("default"!==i){var r=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,r.get?r:{enumerable:!0,get:function(){return t[i]}})}})),e.default=t,Object.freeze(e)}var o=a(r);const n=new t.Logger("vuepress-plugin-feed2"),s=(t,e)=>t&&t instanceof Date?e&&e instanceof Date?e.getTime()-t.getTime():-1:1,u=t=>t.replace(/ class=".*?"/gu,"").replace(/ v-pre/gu,"").replace(/<a href="#.*?">.*?<\/a>/gu,"").replace(/(<!--.*?--!?>)|(<!--[\S\s]+?--!?>)|(<!--[\S\s]*?$)/gu,"").replace(/<OutboundLink ?\/>/gu,"").replace(/<RouterLink to="(.*?)">(.*?)<\/RouterLink>/gu,'<a href="$1">$2</a>').replace(/<(?:a|div|span)[^>]*?\/>/gu,"").replace(/<(Badge|FlowChart|Presentation).*?(?:>.*?<\/\1>|\/>)/gu,"<i>Content not supported</i>").replace(/<math[\s\S]*?\/math>/gu,"<i>Content not supported</i>"),l=(t,e="",i="")=>`${t}${e.replace(/^\/?/u,"/").replace(/\/?$/u,"/")}${i.replace(/^\//u,"")}`,p=(t="")=>`image/${"jpg"===t?"jpeg":"svg"===t?"svg+xml":"jpeg"===t||"png"===t||"bmp"===t||"gif"===t||"webp"===t?t:""}`,c=(e,i,r="")=>{var a,o,n,s,u,p,c;const{hostname:h,icon:d,image:g}=i,{base:m}=e.options,f=null===(o=null===(a=i.channel)||void 0===a?void 0:a.author)||void 0===o?void 0:o.name,y={title:(null===(n=e.siteData.locales[r])||void 0===n?void 0:n.title)||e.siteData.title||(null===(s=e.siteData.locales["/"])||void 0===s?void 0:s.title)||"",link:l(h,m,r),description:(null===(u=e.siteData.locales[r])||void 0===u?void 0:u.description)||e.siteData.description||(null===(p=e.siteData.locales["/"])||void 0===p?void 0:p.description)||"",language:(null===(c=e.siteData.locales[r])||void 0===c?void 0:c.lang)||e.siteData.lang,copyright:f?`Copyright by ${f}`:"",pubDate:new Date,lastUpdated:new Date,...d?{icon:d}:{},...g?{image:g}:{},...f?{author:{name:f}}:{}};return t.deepAssign(y,i.channel||{})},h=(t,e="/")=>({atomOutputFilename:`${e.replace(/^\//,"")}${t.atomOutputFilename||"atom.xml"}`,jsonOutputFilename:`${e.replace(/^\//,"")}${t.jsonOutputFilename||"feed.json"}`,rssOutputFilename:`${e.replace(/^\//,"")}${t.rssOutputFilename||"rss.xml"}`}),d=(t,e)=>{const{base:i}=t.options,{hostname:r}=e,{atomOutputFilename:a,jsonOutputFilename:o,rssOutputFilename:n}=h(e);return{atom:l(r,i,a),json:l(r,i,o),rss:l(r,i,n)}},g=e=>{const{name:i,email:r,url:a}=e;return{name:i,...r?{email:r}:{},...a?{uri:t.encodeXML(a)}:{}}},m=t=>{const{name:e,scheme:i}=t;return{_attributes:{term:e,scheme:i}}},f=t=>({name:t.name,...t.url?{url:t.url}:{},...t.avatar?{avatar:t.avatar}:{}}),y=t=>{const{name:e,domain:i}=t;return{_text:e,...i?{_attributes:{domain:i}}:{}}},b=e=>{const i=e.guid||t.encodeXML(e.link);return{...t.isUrl(i)?{}:{_attributes:{isPermaLink:!1}},_text:i}};class v{constructor(e){this.options=e,this.items=[],this.categories=new Set,this._contributorKeys=new Set,this.contributors=[],this.addItem=t=>{this.items.push(t)},this.addCategory=t=>{this.categories.add(t)},this.addContributor=t=>{const e=t.email||t.name;e&&!this._contributorKeys.has(e)&&(this._contributorKeys.add(e),this.contributors.push(t))},this.atom=()=>(e=>{const{channel:i,links:a}=e.options,o={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}},feed:{_attributes:{xmlns:"http://www.w3.org/2005/Atom",...i.language?{"xml:lang":i.language}:{}},id:i.link,title:i.title,...i.description?{subtitle:i.description}:{},...i.author?{author:g(i.author)}:{},updated:i.lastUpdated?i.lastUpdated.toISOString():(new Date).toISOString(),generator:"vuepress-plugin-feed2",link:[{_attributes:{rel:"self",href:t.encodeXML(a.atom)}}]}};return i.link&&o.feed.link.push({_attributes:{rel:"alternate",href:t.encodeXML(i.link)}}),i.hub&&o.feed.link.push({_attributes:{rel:"hub",href:t.encodeXML(i.hub)}}),i.image&&(o.feed.logo=i.image),i.icon&&(o.feed.icon=i.icon),i.copyright&&(o.feed.rights=i.copyright),o.feed.category=Array.from(e.categories).map((t=>({_attributes:{term:t}}))),o.feed.contributor=Array.from(e.contributors).filter((t=>t.name)).map((t=>g(t))),o.feed.entry=e.items.map((e=>{const i={title:{_attributes:{type:"html"},_text:t.encodeXML(e.title)},id:t.encodeXML(e.guid||e.link),link:{_attributes:{href:t.encodeXML(e.link)}},updated:e.lastUpdated.toISOString()};return e.description&&(i.summary=e.description.startsWith("html:")?{_attributes:{type:"html"},_cdata:t.encodeCDATA(e.description.substring(5))}:{_attributes:{type:"html"},_text:e.description}),e.content&&(i.content={_attributes:{type:"html"},_cdata:t.encodeCDATA(e.content)}),Array.isArray(e.author)?i.author=e.author.filter((t=>t.name)).map((t=>g(t))):e.author&&e.author.name&&(i.author=[g(e.author)]),Array.isArray(e.category)?i.category=e.category.map((t=>m(t))):e.category&&(i.category=[m(e.category)]),Array.isArray(e.contributor)&&(i.contributor=e.contributor.map((t=>g(t)))),e.pubDate&&(i.published=e.pubDate.toISOString()),e.copyright&&(i.rights=e.copyright),i})),r.js2xml(o,{compact:!0,ignoreComment:!0,spaces:2})})(this),this.rss=()=>(e=>{const{links:i,channel:r}=e.options;let a=!1;const n={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}},rss:{_attributes:{version:"2.0","xmlns:atom":"http://www.w3.org/2005/Atom"},channel:{"atom:link":{_attributes:{href:i.rss,rel:"self",type:"application/rss+xml"}},title:{_text:r.title},link:{_text:t.encodeXML(r.link)},description:{_text:r.description},language:{_text:t.encodeXML(r.language)},pubDate:{_text:r.pubDate?r.pubDate.toUTCString():(new Date).toUTCString()},lastBuildDate:{_text:r.lastUpdated?r.lastUpdated.toUTCString():(new Date).toUTCString()},generator:{_text:"vuepress-plugin-feed2"},docs:{_text:"https://validator.w3.org/feed/docs/rss2.html"}}}};return r.copyright&&(n.rss.channel.copyright={_text:r.copyright}),r.ttl&&(n.rss.channel.ttl={_text:r.ttl.toString()}),r.image&&(n.rss.channel.image={title:{_text:r.title},url:{_text:r.image},link:{_text:t.encodeXML(r.link)}}),n.rss.channel.category=Array.from(e.categories).map((t=>({_text:t}))),n.rss.channel.item=e.items.map((e=>{const r={title:{_text:t.encodeXML(e.title)},link:{_text:t.encodeXML(e.link)},guid:b(e),source:{_attributes:{url:i.rss},_text:e.title}};if(e.description&&(r.description={_text:e.description.startsWith("html:")?t.stripTags(e.description.substring(5)):e.description}),Array.isArray(e.author)){const t=e.author.find((t=>t.email&&t.name));t&&(r.author={_text:`${t.email} (${t.name})`})}else if("object"==typeof e.author){const{name:t,email:i}=e.author;i&&t&&(r.author={_text:`${i} (${t})`})}var o;return Array.isArray(e.category)?r.category=e.category.filter((t=>t.name)).map((t=>y(t))):"object"==typeof e.category&&e.category.name&&(r.category=[y(e.category)]),e.comments&&(r.comments={_text:t.encodeXML(e.link)}),e.pubDate&&(r.pubDate={_text:e.pubDate.toUTCString()}),e.content&&(a=!0,r["content:encoded"]={_cdata:t.encodeCDATA(e.content)}),e.enclosure&&(r.enclosure={_attributes:{url:(o=e.enclosure).url,...o.length?{length:o.length}:{},...o.type?{type:o.type}:{}}}),r})),a&&(n.rss._attributes["xmlns:content"]="http://purl.org/rss/1.0/modules/content/",n.rss._attributes["xmlns:dc"]="http://purl.org/dc/elements/1.1/"),o.js2xml(n,{compact:!0,ignoreComment:!0,spaces:2})})(this),this.json=()=>(e=>{var i;const{channel:r,links:a}=e.options,o={version:"https://jsonfeed.org/version/1.1",title:r.title,home_page_url:r.link,feed_url:a.json};return r.description&&(o.description=r.description),r.image&&(o.icon=r.image),r.icon&&(o.favicon=r.icon),(null===(i=r.author)||void 0===i?void 0:i.name)&&(o.author={name:r.author.name,...r.author.url?{url:r.author.url}:{},...r.author.avatar?{avatar:r.author.avatar}:{}}),o.items=e.items.map((e=>{const i={title:e.title,url:e.link,id:e.guid||e.link,...e.description?{summary:e.description.startsWith("html:")?t.stripTags(e.description.substring(5)):e.description}:{},content_html:e.content};return e.image&&(i.image=e.image),e.pubDate&&(i.date_published=e.pubDate.toISOString()),e.lastUpdated&&(i.date_modified=e.lastUpdated.toISOString()),Array.isArray(e.author)?i.authors=e.author.filter((t=>t.name)).map((t=>f(t))):"object"==typeof e.author&&(i.authors=[f(e.author)]),Array.isArray(e.category)?i.tags=e.category.filter((t=>t.name)).map((t=>t.name)):e.category&&(i.tags=[e.category.name]),i})),JSON.stringify(o,null,2)})(this)}}class _{constructor(t,e,i,r){this.app=t,this.options=e,this.page=i,this.feed=r,this.base=this.app.options.base,this.frontmatter=i.frontmatter,this.getter=e.getter||{},this.pageFeedOptions=this.frontmatter.feed||{}}get title(){return"function"==typeof this.getter.title?this.getter.title(this.page):this.pageFeedOptions.title||this.page.title}get link(){return"function"==typeof this.getter.link?this.getter.link(this.page):l(this.options.hostname,this.base,this.page.path)}get description(){return"function"==typeof this.getter.description?this.getter.description(this.page):this.pageFeedOptions.description?this.pageFeedOptions.description:this.frontmatter.description?this.frontmatter.description:this.page.excerpt?`html:${u(this.app.markdown.render(this.page.excerpt))}`:void 0}get author(){var e,i;return"function"==typeof this.getter.author?this.getter.author(this.page):Array.isArray(this.pageFeedOptions.author)?this.pageFeedOptions.author:"object"==typeof this.pageFeedOptions.author?[this.pageFeedOptions.author]:!1===this.frontmatter.author?[]:this.frontmatter.author?t.getAuthor(this.frontmatter.author):(null===(e=this.options.channel)||void 0===e?void 0:e.author)?t.getAuthor(null===(i=this.options.channel)||void 0===i?void 0:i.author):[]}get category(){if("function"==typeof this.getter.category)return this.getter.category(this.page);if(Array.isArray(this.pageFeedOptions.category))return this.pageFeedOptions.category;if("object"==typeof this.pageFeedOptions.category)return[this.pageFeedOptions.category];const{categories:e,category:i=e}=this.frontmatter;return t.getCategory(i).map((t=>({name:t})))}get enclosure(){return"function"==typeof this.getter.enclosure?this.getter.enclosure(this.page):this.image?{url:this.image,type:p(this.image.split(".").pop())}:void 0}get guid(){return this.pageFeedOptions.guid||this.link}get pubDate(){if("function"==typeof this.getter.publishDate)return this.getter.publishDate(this.page);const{time:t,date:e=t}=this.page.frontmatter,{createdTime:i}=this.page.git||{};return e&&e instanceof Date?e:i?new Date(i):void 0}get lastUpdated(){if("function"==typeof this.getter.lastUpdateDate)return this.getter.lastUpdateDate(this.page);const{updatedTime:t}=this.page.git||{};return t?new Date(t):new Date}get content(){return"function"==typeof this.getter.content?this.getter.content(this.page):this.pageFeedOptions.content?this.pageFeedOptions.content:u(this.page.contentRendered)}get image(){if("function"==typeof this.getter.image)return this.getter.image(this.page);const{banner:e,cover:i}=this.frontmatter;if(e){if(t.isAbsoluteUrl(e))return l(this.options.hostname,this.app.options.base,e);if(t.isUrl(e))return e}if(i){if(t.isAbsoluteUrl(i))return l(this.options.hostname,this.app.options.base,i);if(t.isUrl(i))return i}const r=/!\[.*?\]\(.*?\)/iu.exec(this.page.content);if(r){if(t.isAbsoluteUrl(r[1]))return l(this.options.hostname,this.app.options.base,r[1]);if(t.isUrl(r[1]))return r[1]}}get contributor(){return"function"==typeof this.getter.contributor?this.getter.contributor(this.page):Array.isArray(this.pageFeedOptions.contributor)?this.pageFeedOptions.contributor:"object"==typeof this.pageFeedOptions.contributor?[this.pageFeedOptions.contributor]:this.author}get copyright(){if("function"==typeof this.getter.copyright)return this.getter.copyright(this.page);if(this.frontmatter.copyright)return this.frontmatter.copyright;const t=this.author[0];return t&&t.name?`Copyright by ${t.name}`:void 0}getFeedItem(){const{author:t,category:e,content:i,contributor:r,copyright:a,description:o,enclosure:n,guid:s,image:u,lastUpdated:l,link:p,pubDate:c,title:h}=this;return h||o?(e&&e.forEach((t=>this.feed.addCategory(t.name))),r&&r.forEach((t=>this.feed.addContributor(t))),{title:h,link:p,description:o,author:t,category:e,enclosure:n,guid:s,pubDate:c,lastUpdated:l,content:i,image:u,contributor:r,copyright:a}):null}}class O{constructor(t,e){this.app=t,this.options=e,this.feedMap=Object.fromEntries(Object.entries(e).map((([e,i])=>[e,new v({channel:c(t,i,e),links:d(t,i)})])))}addPages(t){const i=this.feedMap[t],r=this.options[t],{filter:a=(({frontmatter:t,filePathRelative:e})=>!(t.home||!e||!1===t.article||!1===t.feed)),sorter:o=((t,e)=>{var i,r,a,o;return s((null===(i=t.git)||void 0===i?void 0:i.createdTime)?new Date(null===(r=t.git)||void 0===r?void 0:r.createdTime):t.frontmatter.date,(null===(a=e.git)||void 0===a?void 0:a.createdTime)?new Date(null===(o=e.git)||void 0===o?void 0:o.createdTime):e.frontmatter.date)})}=r,u=this.app.pages.filter((e=>e.pathLocale===t)).filter(a).sort(o).slice(0,r.count||100);let l=0;for(const t of u){const e=new _(this.app,r,t,i).getFeedItem();e&&(i.addItem(e),l+=1)}n.succeed(`added ${e.chalk.cyan(`${l} page(s)`)} as feed item(s) in route ${e.chalk.cyan(t)}`)}async generateFeed(){const{dest:t}=this.app.dir;await Promise.all(Object.entries(this.options).map((async([r,a])=>{if(a.atom||a.json||a.rss){const o=this.feedMap[r],{atomOutputFilename:s,jsonOutputFilename:u,rssOutputFilename:l}=h(a,r);this.addPages(r),a.atom&&(await e.fs.ensureDir(i.dirname(t(s))),await e.fs.outputFile(t(s),o.atom()),n.succeed(`Atom feed file generated and saved to ${e.chalk.cyan(s)}`)),a.json&&(await e.fs.ensureDir(i.dirname(t(u))),await e.fs.outputFile(t(u),o.json()),n.succeed(`JSON feed file generated and saved to ${e.chalk.cyan(u)}`)),a.rss&&(await e.fs.ensureDir(i.dirname(t(l))),await e.fs.outputFile(t(l),o.rss()),n.succeed(`RSS feed file generated and saved to ${e.chalk.cyan(l)}`))}})))}}const D=(t,e)=>{if(!(t=>!!t.hostname&&(t.hostname=t.hostname.replace(/\/?$/u,""),!0))(t))return n.error("Option 'hostname' is required!"),{name:"vuepress-plugin-feed2"};if(!(t=>t.locales&&Object.entries(t.locales).some((([,{atom:t,json:e,rss:i}])=>t||e||i))||Boolean(t.atom||t.json||t.rss))(t))return n.info("No requested output, the plugin won't start!"),{name:"vuepress-plugin-feed2"};const i=((t,e)=>Object.fromEntries(Object.keys({"/":{},...t.siteData.locales}).map((t=>{var i;return[t,{filter:({frontmatter:t,filePathRelative:e})=>!(t.home||!e||!1===t.article||!1===t.feed),sorter:(t,e)=>{var i,r,a,o;return s((null===(i=t.git)||void 0===i?void 0:i.createdTime)?new Date(null===(r=t.git)||void 0===r?void 0:r.createdTime):t.frontmatter.date,(null===(a=e.git)||void 0===a?void 0:a.createdTime)?new Date(null===(o=e.git)||void 0===o?void 0:o.createdTime):e.frontmatter.date)},...e,...null===(i=e.locales)||void 0===i?void 0:i[t],hostname:e.hostname}]}))))(e,t);return{name:"vuepress-plugin-feed2",onPrepared:t=>((t,e)=>{const{base:i}=t.options,{siteData:r}=t,a=Object.keys(e);if(1===a.length){const{atomOutputFilename:t,jsonOutputFilename:a,rssOutputFilename:o}=h(e["/"]),n=(t,a,o)=>["link",{rel:"alternate",type:o,href:l(e["/"].hostname,i,a),title:`${r.title||r.locales["/"].title||""} ${t} Feed`}];r.head||(r.head=[]),e.atom&&r.head.push(n("Atom",t,"application/atom+xml")),e.json&&r.head.push(n("JSON",a,"application/json")),e.rss&&r.head.push(n("RSS",o,"application/rss+xml"))}else t.pages.forEach((t=>{const{pathLocale:o}=t,n=e[o];if(a.includes(o)){const{atomOutputFilename:e,jsonOutputFilename:a,rssOutputFilename:s}=h(n,o),u=(t,e,a)=>["link",{rel:"alternate",type:a,href:l(n.hostname,i,e),title:`${r.locales[o].title||r.title||r.locales["/"].title||""} ${t} Feed`}];t.frontmatter.head||(t.frontmatter.head=[]),n.atom&&t.frontmatter.head.push(u("Atom",e,"application/atom+xml")),n.json&&t.frontmatter.head.push(u("JSON",a,"application/json")),n.rss&&t.frontmatter.head.push(u("RSS",s,"application/rss+xml"))}}))})(t,i),onGenerated:async t=>{await new O(t,i).generateFeed()}}};exports.default=D,exports.feed=t=>["feed2",t],exports.feedPlugin=D; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "vuepress-plugin-feed2", | ||
"version": "2.0.0-beta.7", | ||
"version": "2.0.0-beta.8", | ||
"description": "Feed plugin for vuepress-theme-hope", | ||
@@ -44,3 +44,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@mr-hope/vuepress-shared": "2.0.0-beta.7", | ||
"@mr-hope/vuepress-shared": "2.0.0-beta.8", | ||
"@vuepress/utils": "2.0.0-beta.35", | ||
@@ -55,3 +55,3 @@ "xml-js": "^1.6.11" | ||
}, | ||
"gitHead": "f4cad47182c8a3b9dabb6ae99dcf907c9faec24d" | ||
"gitHead": "646fe86ed1406a1a3f093961289e325c6b1b87f9" | ||
} |
import { encodeCDATA, encodeXML } from "@mr-hope/vuepress-shared"; | ||
import { js2xml } from "xml-js"; | ||
import { generator } from "../utils"; | ||
import { FEED_GENERATOR } from "../utils"; | ||
@@ -67,3 +67,3 @@ import type { Feed } from "../feed"; | ||
: new Date().toISOString(), | ||
generator: generator, | ||
generator: FEED_GENERATOR, | ||
link: [{ _attributes: { rel: "self", href: encodeXML(links.atom) } }], | ||
@@ -110,7 +110,13 @@ }, | ||
// entry: recommended elements | ||
if (item.description) | ||
entry.summary = { | ||
_attributes: { type: "html" }, | ||
_cdata: encodeCDATA(item.description), | ||
}; | ||
if (item.description) { | ||
entry.summary = item.description.startsWith("html:") | ||
? { | ||
_attributes: { type: "html" }, | ||
_cdata: encodeCDATA(item.description.substring(5)), | ||
} | ||
: { | ||
_attributes: { type: "html" }, | ||
_text: item.description, | ||
}; | ||
} | ||
@@ -117,0 +123,0 @@ if (item.content) |
@@ -84,3 +84,3 @@ export interface AtomText { | ||
*/ | ||
summary?: AtomCDATA; | ||
summary?: AtomText | AtomCDATA; | ||
/** | ||
@@ -87,0 +87,0 @@ * Specifies a category that the entry belongs to. |
import { chalk, fs } from "@vuepress/utils"; | ||
import { dirname } from "path"; | ||
import { getFeedChannelOption, getFilename, getFeedLinks } from "./options"; | ||
import { Feed } from "./feed"; | ||
import { FeedPage } from "./page"; | ||
import { getFilename, logger } from "./utils"; | ||
import { compareDate, logger } from "./utils"; | ||
import type { App, Page } from "@vuepress/core"; | ||
import type { FeedOptions, FeedInitOptions } from "../shared"; | ||
import type { GitData } from "@vuepress/plugin-git"; | ||
import type { ResolvedFeedOptionsMap } from "./options"; | ||
export class FeedGenerator { | ||
/** feed 生成器 */ | ||
feed: Feed; | ||
feedMap: Record<string, Feed>; | ||
constructor( | ||
private app: App, | ||
private options: FeedOptions, | ||
private pages: Page[], | ||
feedOption: FeedInitOptions | ||
) { | ||
this.feed = new Feed(feedOption); | ||
constructor(private app: App, private options: ResolvedFeedOptionsMap) { | ||
this.feedMap = Object.fromEntries( | ||
Object.entries(options).map(([localePath, localeOptions]) => { | ||
return [ | ||
localePath, | ||
new Feed({ | ||
channel: getFeedChannelOption(app, localeOptions, localePath), | ||
links: getFeedLinks(app, localeOptions), | ||
}), | ||
]; | ||
}) | ||
); | ||
} | ||
addPages(): void { | ||
addPages(localePath: string): void { | ||
const feed = this.feedMap[localePath]; | ||
const localeOption = this.options[localePath]; | ||
const { | ||
filter = ({ frontmatter, filePathRelative }: Page): boolean => | ||
!( | ||
frontmatter.home || | ||
!filePathRelative || | ||
frontmatter.article === false || | ||
frontmatter.feed === false | ||
), | ||
sorter = ( | ||
pageA: Page<Record<string, never>, { git?: GitData }>, | ||
pageB: Page<Record<string, never>, { git?: GitData }> | ||
): number => { | ||
return compareDate( | ||
pageA.git?.createdTime | ||
? new Date(pageA.git?.createdTime) | ||
: pageA.frontmatter.date, | ||
pageB.git?.createdTime | ||
? new Date(pageB.git?.createdTime) | ||
: pageB.frontmatter.date | ||
); | ||
}, | ||
} = localeOption; | ||
const pages = this.app.pages | ||
.filter((page) => page.pathLocale === localePath) | ||
.filter(filter) | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
.sort(sorter) | ||
.slice(0, localeOption.count || 100); | ||
let count = 0; | ||
for (const page of this.pages) { | ||
for (const page of pages) { | ||
const item = new FeedPage( | ||
this.app, | ||
this.options, | ||
localeOption, | ||
page, | ||
this.feed | ||
feed | ||
).getFeedItem(); | ||
if (item) { | ||
this.feed.addItem(item); | ||
feed.addItem(item); | ||
count += 1; | ||
@@ -40,3 +79,7 @@ } | ||
logger.succeed(`added ${chalk.cyan(`${count} page(s)`)} as feed item(s)`); | ||
logger.succeed( | ||
`added ${chalk.cyan( | ||
`${count} page(s)` | ||
)} as feed item(s) in route ${chalk.cyan(localePath)}` | ||
); | ||
} | ||
@@ -46,50 +89,52 @@ | ||
const { dest } = this.app.dir; | ||
const { atomOutputFilename, jsonOutputFilename, rssOutputFilename } = | ||
getFilename(this.options); | ||
this.addPages(); | ||
await Promise.all( | ||
Object.entries(this.options).map(async ([localePath, localeOptions]) => { | ||
// current locale has valid output | ||
if (localeOptions.atom || localeOptions.json || localeOptions.rss) { | ||
const feed = this.feedMap[localePath]; | ||
const { atomOutputFilename, jsonOutputFilename, rssOutputFilename } = | ||
getFilename(localeOptions, localePath); | ||
// generate atom files | ||
if (this.options.atom) { | ||
logger.load("Generating Atom Feed"); | ||
this.addPages(localePath); | ||
await fs.ensureDir(dirname(dest(atomOutputFilename))); | ||
await fs.outputFile(dest(atomOutputFilename), this.feed.atom()); | ||
// generate atom files | ||
if (localeOptions.atom) { | ||
await fs.ensureDir(dirname(dest(atomOutputFilename))); | ||
await fs.outputFile(dest(atomOutputFilename), feed.atom()); | ||
logger.update( | ||
`Atom feed file generated and saved to ${chalk.cyan( | ||
atomOutputFilename | ||
)}` | ||
); | ||
logger.succeed(); | ||
} | ||
logger.succeed( | ||
`Atom feed file generated and saved to ${chalk.cyan( | ||
atomOutputFilename | ||
)}` | ||
); | ||
} | ||
// generate json files | ||
if (this.options.json) { | ||
logger.load("Generating JSON Feed"); | ||
// generate json files | ||
if (localeOptions.json) { | ||
await fs.ensureDir(dirname(dest(jsonOutputFilename))); | ||
await fs.outputFile(dest(jsonOutputFilename), feed.json()); | ||
await fs.ensureDir(dirname(dest(jsonOutputFilename))); | ||
await fs.outputFile(dest(jsonOutputFilename), this.feed.json()); | ||
logger.succeed( | ||
`JSON feed file generated and saved to ${chalk.cyan( | ||
jsonOutputFilename | ||
)}` | ||
); | ||
} | ||
logger.update( | ||
`JSON feed file generated and saved to ${chalk.cyan( | ||
jsonOutputFilename | ||
)}` | ||
); | ||
logger.succeed(); | ||
} | ||
// generate rss files | ||
if (localeOptions.rss) { | ||
await fs.ensureDir(dirname(dest(rssOutputFilename))); | ||
await fs.outputFile(dest(rssOutputFilename), feed.rss()); | ||
// generate rss files | ||
if (this.options.rss) { | ||
logger.load("Generating RSS Feed"); | ||
await fs.ensureDir(dirname(dest(rssOutputFilename))); | ||
await fs.outputFile(dest(rssOutputFilename), this.feed.rss()); | ||
logger.update( | ||
`RSS feed file generated and saved to ${chalk.cyan(rssOutputFilename)}` | ||
); | ||
logger.succeed(); | ||
} | ||
logger.succeed( | ||
`RSS feed file generated and saved to ${chalk.cyan( | ||
rssOutputFilename | ||
)}` | ||
); | ||
} | ||
} | ||
}) | ||
); | ||
} | ||
} |
@@ -1,47 +0,112 @@ | ||
import { getFilename, resolveUrl } from "./utils"; | ||
import { getFilename } from "./options"; | ||
import { resolveUrl } from "./utils"; | ||
import type { App, HeadConfig } from "@vuepress/core"; | ||
import type { FeedOptions } from "../shared"; | ||
import type { ResolvedFeedOptionsMap } from "./options"; | ||
export const injectLinkstoHead = (app: App, options: FeedOptions): void => { | ||
export const injectLinkstoHead = ( | ||
app: App, | ||
options: ResolvedFeedOptionsMap | ||
): void => { | ||
const { base } = app.options; | ||
const { siteData } = app; | ||
const { atomOutputFilename, jsonOutputFilename, rssOutputFilename } = | ||
getFilename(options); | ||
const localePaths = Object.keys(options); | ||
const getHeadItem = ( | ||
name: string, | ||
fileName: string, | ||
type: string | ||
): HeadConfig => { | ||
return [ | ||
"link", | ||
{ | ||
rel: "alternate", | ||
type, | ||
href: resolveUrl(options.hostname, base, fileName), | ||
title: `${siteData.title || ""} ${name} Feed`, | ||
}, | ||
]; | ||
}; | ||
// there is only one language, so we append it to siteData | ||
if (localePaths.length === 1) { | ||
const { atomOutputFilename, jsonOutputFilename, rssOutputFilename } = | ||
getFilename(options["/"]); | ||
if (!siteData.head) siteData.head = []; | ||
const getHeadItem = ( | ||
name: string, | ||
fileName: string, | ||
type: string | ||
): HeadConfig => { | ||
return [ | ||
"link", | ||
{ | ||
rel: "alternate", | ||
type, | ||
href: resolveUrl(options["/"].hostname, base, fileName), | ||
title: `${ | ||
siteData.title || siteData.locales["/"].title || "" | ||
} ${name} Feed`, | ||
}, | ||
]; | ||
}; | ||
// add atom link | ||
if (options.atom) | ||
siteData.head.push( | ||
getHeadItem("Atom", atomOutputFilename, "application/atom+xml") | ||
); | ||
// ensure head exists | ||
if (!siteData.head) siteData.head = []; | ||
// add json link | ||
if (options.json) | ||
siteData.head.push( | ||
getHeadItem("JSON", jsonOutputFilename, "application/json") | ||
); | ||
// add atom link | ||
if (options.atom) | ||
siteData.head.push( | ||
getHeadItem("Atom", atomOutputFilename, "application/atom+xml") | ||
); | ||
// add rss link | ||
if (options.rss) | ||
siteData.head.push( | ||
getHeadItem("RSS", rssOutputFilename, "application/rss+xml") | ||
); | ||
// add json link | ||
if (options.json) | ||
siteData.head.push( | ||
getHeadItem("JSON", jsonOutputFilename, "application/json") | ||
); | ||
// add rss link | ||
if (options.rss) | ||
siteData.head.push( | ||
getHeadItem("RSS", rssOutputFilename, "application/rss+xml") | ||
); | ||
} | ||
// there are mutiple languages, so we should append to page | ||
else | ||
app.pages.forEach((page) => { | ||
const { pathLocale } = page; | ||
const localeOptions = options[pathLocale]; | ||
if (localePaths.includes(pathLocale)) { | ||
const { atomOutputFilename, jsonOutputFilename, rssOutputFilename } = | ||
getFilename(localeOptions, pathLocale); | ||
const getHeadItem = ( | ||
name: string, | ||
fileName: string, | ||
type: string | ||
): HeadConfig => { | ||
return [ | ||
"link", | ||
{ | ||
rel: "alternate", | ||
type, | ||
href: resolveUrl(localeOptions.hostname, base, fileName), | ||
title: `${ | ||
siteData.locales[pathLocale].title || | ||
siteData.title || | ||
siteData.locales["/"].title || | ||
"" | ||
} ${name} Feed`, | ||
}, | ||
]; | ||
}; | ||
// ensure head exists | ||
if (!page.frontmatter.head) page.frontmatter.head = []; | ||
// add atom link | ||
if (localeOptions.atom) | ||
page.frontmatter.head.push( | ||
getHeadItem("Atom", atomOutputFilename, "application/atom+xml") | ||
); | ||
// add json link | ||
if (localeOptions.json) | ||
page.frontmatter.head.push( | ||
getHeadItem("JSON", jsonOutputFilename, "application/json") | ||
); | ||
// add rss link | ||
if (localeOptions.rss) | ||
page.frontmatter.head.push( | ||
getHeadItem("RSS", rssOutputFilename, "application/rss+xml") | ||
); | ||
} | ||
}); | ||
}; |
/* eslint-disable @typescript-eslint/naming-convention */ | ||
import { stripTags } from "@mr-hope/vuepress-shared"; | ||
import type { Feed } from "../feed"; | ||
@@ -45,3 +47,9 @@ import type { FeedAuthor } from "../../shared"; | ||
id: item.guid || item.link, | ||
...(item.description ? { summary: item.description } : {}), | ||
...(item.description | ||
? { | ||
summary: item.description.startsWith("html:") | ||
? stripTags(item.description.substring(5)) | ||
: item.description, | ||
} | ||
: {}), | ||
@@ -48,0 +56,0 @@ // json_feed distinguishes between html and text content |
@@ -1,21 +0,104 @@ | ||
import { deepAssign, getRootLang } from "@mr-hope/vuepress-shared"; | ||
import { getFilename, resolveUrl } from "./utils"; | ||
import { deepAssign } from "@mr-hope/vuepress-shared"; | ||
import { compareDate, resolveUrl } from "./utils"; | ||
import type { App } from "@vuepress/core"; | ||
import type { FeedChannelOption, FeedLinks, FeedOptions } from "../shared"; | ||
import type { App, Page } from "@vuepress/core"; | ||
import type { GitData } from "@vuepress/plugin-git"; | ||
import type { | ||
BaseFeedOptions, | ||
FeedChannelOption, | ||
FeedLinks, | ||
FeedOptions, | ||
} from "../shared"; | ||
export type ResolvedFeedOptions = BaseFeedOptions & { hostname: string }; | ||
export type ResolvedFeedOptionsMap = Record<string, ResolvedFeedOptions>; | ||
export const ensureHostName = (options: Partial<FeedOptions>): boolean => { | ||
// make sure hostname do not end with `/` | ||
if (options.hostname) { | ||
options.hostname = options.hostname.replace(/\/?$/u, ""); | ||
return true; | ||
} | ||
return false; | ||
}; | ||
export const checkOutput = (options: Partial<FeedOptions>): boolean => | ||
// some locales request output | ||
(options.locales && | ||
Object.entries(options.locales).some( | ||
([, { atom, json, rss }]) => atom || json || rss | ||
)) || | ||
// root option requsts output | ||
Boolean(options.atom || options.json || options.rss); | ||
export const getFeedOptions = ( | ||
app: App, | ||
options: FeedOptions | ||
): ResolvedFeedOptionsMap => | ||
Object.fromEntries( | ||
Object.keys({ | ||
// root locale must exists | ||
"/": {}, | ||
...app.siteData.locales, | ||
}).map((localePath) => { | ||
return [ | ||
localePath, | ||
{ | ||
// default values | ||
filter: ({ frontmatter, filePathRelative }: Page): boolean => | ||
!( | ||
frontmatter.home || | ||
!filePathRelative || | ||
frontmatter.article === false || | ||
frontmatter.feed === false | ||
), | ||
sorter: ( | ||
pageA: Page<Record<string, never>, { git?: GitData }>, | ||
pageB: Page<Record<string, never>, { git?: GitData }> | ||
): number => { | ||
return compareDate( | ||
pageA.git?.createdTime | ||
? new Date(pageA.git?.createdTime) | ||
: pageA.frontmatter.date, | ||
pageB.git?.createdTime | ||
? new Date(pageB.git?.createdTime) | ||
: pageB.frontmatter.date | ||
); | ||
}, | ||
...options, | ||
...options.locales?.[localePath], | ||
// make sure hostname is not been overided | ||
hostname: options.hostname, | ||
} as ResolvedFeedOptions, | ||
]; | ||
}) | ||
); | ||
export const getFeedChannelOption = ( | ||
app: App, | ||
options: FeedOptions | ||
options: FeedOptions, | ||
localePath = "" | ||
): FeedChannelOption => { | ||
const { hostname, icon, image } = options; | ||
const { base } = app.options; | ||
const { title, description } = app.siteData; | ||
const author = options.channel?.author?.name; | ||
const defaultChannelOpion: FeedChannelOption = { | ||
title, | ||
link: resolveUrl(hostname, base), | ||
description, | ||
language: getRootLang(app), | ||
title: | ||
app.siteData.locales[localePath]?.title || | ||
app.siteData.title || | ||
app.siteData.locales["/"]?.title || | ||
"", | ||
link: resolveUrl(hostname, base, localePath), | ||
description: | ||
app.siteData.locales[localePath]?.description || | ||
app.siteData.description || | ||
app.siteData.locales["/"]?.description || | ||
"", | ||
language: app.siteData.locales[localePath]?.lang || app.siteData.lang, | ||
copyright: author ? `Copyright by ${author}` : "", | ||
@@ -26,9 +109,3 @@ pubDate: new Date(), | ||
...(image ? { image } : {}), | ||
...(author | ||
? { | ||
author: { | ||
name: author, | ||
}, | ||
} | ||
: {}), | ||
...(author ? { author: { name: author } } : {}), | ||
}; | ||
@@ -39,2 +116,21 @@ | ||
export const getFilename = ( | ||
options: ResolvedFeedOptions, | ||
prefix = "/" | ||
): { | ||
atomOutputFilename: string; | ||
jsonOutputFilename: string; | ||
rssOutputFilename: string; | ||
} => ({ | ||
atomOutputFilename: `${prefix.replace(/^\//, "")}${ | ||
options.atomOutputFilename || "atom.xml" | ||
}`, | ||
jsonOutputFilename: `${prefix.replace(/^\//, "")}${ | ||
options.jsonOutputFilename || "feed.json" | ||
}`, | ||
rssOutputFilename: `${prefix.replace(/^\//, "")}${ | ||
options.rssOutputFilename || "rss.xml" | ||
}`, | ||
}); | ||
export const getFeedLinks = (app: App, options: FeedOptions): FeedLinks => { | ||
@@ -41,0 +137,0 @@ const { base } = app.options; |
@@ -69,3 +69,3 @@ import { | ||
if (this.page.excerpt) | ||
return resolveHTML(this.app.markdown.render(this.page.excerpt)); | ||
return `html:${resolveHTML(this.app.markdown.render(this.page.excerpt))}`; | ||
@@ -179,3 +179,3 @@ return undefined; | ||
const result = /!\[.*?\]\((.*?)\)/iu.exec(this.page.content); | ||
const result = /!\[.*?\]\(.*?\)/iu.exec(this.page.content); | ||
@@ -182,0 +182,0 @@ if (result) { |
@@ -1,15 +0,11 @@ | ||
import { getFeedChannelOption, getFeedLinks } from "./options"; | ||
import { checkOutput, ensureHostName, getFeedOptions } from "./options"; | ||
import { injectLinkstoHead } from "./injectHead"; | ||
import { FeedGenerator } from "./generator"; | ||
import { compareDate, logger } from "./utils"; | ||
import { logger } from "./utils"; | ||
import type { Page, Plugin, PluginConfig } from "@vuepress/core"; | ||
import type { GitData } from "@vuepress/plugin-git"; | ||
import type { Plugin, PluginConfig } from "@vuepress/core"; | ||
import type { FeedOptions } from "../shared"; | ||
export const feedPlugin: Plugin<FeedOptions> = (options, app) => { | ||
// make sure hostname do not end with `/` | ||
if (options.hostname) | ||
options.hostname = options.hostname.replace(/\/?$/u, ""); | ||
else { | ||
if (!ensureHostName(options)) { | ||
logger.error("Option 'hostname' is required!"); | ||
@@ -22,6 +18,3 @@ | ||
const feedOptions = options as FeedOptions; | ||
const channelOptions = getFeedChannelOption(app, feedOptions); | ||
if (!feedOptions.atom && !feedOptions.json && !feedOptions.rss) { | ||
if (!checkOutput(options)) { | ||
logger.info("No requested output, the plugin won't start!"); | ||
@@ -34,2 +27,4 @@ | ||
const feedOptions = getFeedOptions(app, options as FeedOptions); | ||
return { | ||
@@ -41,36 +36,3 @@ name: "vuepress-plugin-feed2", | ||
onGenerated: async (app): Promise<void> => { | ||
const { | ||
filter = ({ frontmatter, filePathRelative }: Page): boolean => | ||
!( | ||
frontmatter.home || | ||
!filePathRelative || | ||
frontmatter.article === false || | ||
frontmatter.feed === false | ||
), | ||
sorter = ( | ||
pageA: Page<Record<string, never>, { git?: GitData }>, | ||
pageB: Page<Record<string, never>, { git?: GitData }> | ||
): number => { | ||
return compareDate( | ||
pageA.git?.createdTime | ||
? new Date(pageA.git?.createdTime) | ||
: pageA.frontmatter.date, | ||
pageB.git?.createdTime | ||
? new Date(pageB.git?.createdTime) | ||
: pageB.frontmatter.date | ||
); | ||
}, | ||
} = feedOptions; | ||
const feedPages = app.pages | ||
.filter(filter) | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
.sort(sorter) | ||
.slice(0, feedOptions.count || 100); | ||
await new FeedGenerator(app, feedOptions, feedPages, { | ||
channel: channelOptions, | ||
links: getFeedLinks(app, feedOptions), | ||
}).generateFeed(); | ||
await new FeedGenerator(app, feedOptions).generateFeed(); | ||
}, | ||
@@ -77,0 +39,0 @@ }; |
@@ -0,4 +1,5 @@ | ||
import { stripTags } from "@mr-hope/vuepress-shared"; | ||
import { encodeCDATA, encodeXML, isUrl } from "@mr-hope/vuepress-shared"; | ||
import * as convert from "xml-js"; | ||
import { generator } from "../utils"; | ||
import { FEED_GENERATOR } from "../utils"; | ||
@@ -94,3 +95,3 @@ import type { Feed } from "../feed"; | ||
}, | ||
generator: { _text: generator }, | ||
generator: { _text: FEED_GENERATOR }, | ||
docs: { | ||
@@ -141,3 +142,7 @@ _text: "https://validator.w3.org/feed/docs/rss2.html", | ||
if (entry.description) | ||
item.description = { _text: encodeXML(entry.description) }; | ||
item.description = { | ||
_text: entry.description.startsWith("html:") | ||
? stripTags(entry.description.substring(5)) | ||
: entry.description, | ||
}; | ||
@@ -144,0 +149,0 @@ /** |
import { Logger } from "@mr-hope/vuepress-shared"; | ||
import type { FeedOptions } from "../shared"; | ||
export const logger = new Logger("vuepress-plugin-feed2"); | ||
@@ -68,15 +66,2 @@ | ||
export const getFilename = ( | ||
options: FeedOptions | ||
): { | ||
atomOutputFilename: string; | ||
jsonOutputFilename: string; | ||
rssOutputFilename: string; | ||
} => ({ | ||
atomOutputFilename: options.atomOutputFilename || "atom.xml", | ||
jsonOutputFilename: options.jsonOutputFilename || "feed.json", | ||
rssOutputFilename: options.rssOutputFilename || "rss.xml", | ||
}); | ||
export const generator = "vuepress-plugin-feed2"; | ||
export const FEED_GENERATOR = "vuepress-plugin-feed2"; |
import type { Page } from "@vuepress/core"; | ||
import type { FeedChannelOption, FeedGetter } from "./feed"; | ||
export interface FeedOptions { | ||
export interface BaseFeedOptions { | ||
/** | ||
* Deploy hostname | ||
* | ||
* 部署的域名 | ||
*/ | ||
hostname: string; | ||
/** | ||
* Whether to output Atom syntax files. | ||
@@ -121,1 +114,13 @@ * | ||
} | ||
export interface FeedOptions extends BaseFeedOptions { | ||
/** | ||
* Deploy hostname | ||
* | ||
* 部署的域名 | ||
*/ | ||
hostname: string; | ||
/** Locales for feed */ | ||
locales?: Record<string, BaseFeedOptions>; | ||
} |
Sorry, the diff of this file is not supported yet
102011
2464
+ Added@mr-hope/vuepress-shared@2.0.0-beta.8(transitive)
- Removed@mr-hope/vuepress-shared@2.0.0-beta.7(transitive)