diff --git a/scripts/adopters/index.js b/scripts/adopters/index.js index 6a11697ad68f..a8c8363d25ab 100644 --- a/scripts/adopters/index.js +++ b/scripts/adopters/index.js @@ -1,6 +1,6 @@ const { resolve } = require('path'); -const writeJSON = require('../utils/readAndWriteJson.js') +const writeJSON = require('../utils/readAndWriteJson.js'); module.exports = async function buildAdoptersList() { - writeJSON('config/adopters.yml',resolve(__dirname, '../../config', 'adopters.json')); + writeJSON('config/adopters.yml', resolve(__dirname, '../../config', 'adopters.json')); }; diff --git a/scripts/build-docs.js b/scripts/build-docs.js index ac47b6751cee..48693f176ba8 100644 --- a/scripts/build-docs.js +++ b/scripts/build-docs.js @@ -1,23 +1,32 @@ -const sortBy = require('lodash/sortBy') +const sortBy = require('lodash/sortBy'); + function buildNavTree(navItems) { try { const tree = { - 'welcome': { - item: { title: 'Welcome', weight: 0, isRootSection: true, isSection: true, rootSectionId: 'welcome', sectionWeight: 0, slug: '/docs' }, + welcome: { + item: { + title: 'Welcome', + weight: 0, + isRootSection: true, + isSection: true, + rootSectionId: 'welcome', + sectionWeight: 0, + slug: '/docs' + }, children: {} } - } + }; - //first we make sure that list of items lists main section items and then sub sections, documents last + // first we make sure that list of items lists main section items and then sub sections, documents last const sortedItems = sortBy(navItems, ['isRootSection', 'weight', 'isSection']); - sortedItems.forEach(item => { - //identify main sections + sortedItems.forEach((item) => { + // identify main sections if (item.isRootSection) { - tree[item.rootSectionId] = { item, children: {} } + tree[item.rootSectionId] = { item, children: {} }; } - //identify subsections + // identify subsections if (item.parent) { if (!tree[item.parent]) { throw new Error(`Parent section ${item.parent} not found for item ${item.title}`); @@ -27,9 +36,12 @@ function buildNavTree(navItems) { if (!item.isSection) { if (item.sectionId) { - let section = tree[item.rootSectionId]?.children[item.sectionId]; + const section = tree[item.rootSectionId]?.children[item.sectionId]; if (!section) { - tree[item.rootSectionId].children[item.sectionId] = { item, children: [] }; + tree[item.rootSectionId].children[item.sectionId] = { + item, + children: [] + }; } tree[item.rootSectionId].children[item.sectionId].children.push(item); } else { @@ -51,7 +63,7 @@ function buildNavTree(navItems) { return obj; }, {}); - //handling subsections + // handling subsections if (allChildrenKeys.length > 1) { for (const key of allChildrenKeys) { if (allChildren[key].children) { @@ -62,73 +74,70 @@ function buildNavTree(navItems) { // point in slug for specification subgroup to the latest specification version if (rootKey === 'reference' && key === 'specification') { - allChildren[key].item.href = allChildren[key].children.find(c => c.isPrerelease === undefined).slug; + allChildren[key].item.href = allChildren[key].children.find((c) => c.isPrerelease === undefined).slug; } } } } return tree; - } catch (err) { throw new Error(`Failed to build navigation tree: ${err.message}`); } } -// A recursion function, works on the logic of Depth First Search to traverse all the root and child posts of the +// A recursion function, works on the logic of Depth First Search to traverse all the root and child posts of the // DocTree to get sequential order of the Doc Posts const convertDocPosts = (docObject) => { try { - let docsArray = [] + let docsArray = []; // certain entries in the DocPosts are either a parent to many posts or itself a post. - docsArray.push(docObject?.item || docObject) + docsArray.push(docObject?.item || docObject); if (docObject.children) { - let children = docObject.children + const { children } = docObject; Object.keys(children).forEach((child) => { - let docChildArray = convertDocPosts(children[child]) - docsArray = [...docsArray, ...docChildArray] - }) + const docChildArray = convertDocPosts(children[child]); + docsArray = [...docsArray, ...docChildArray]; + }); } - return docsArray - } - catch (err) { + return docsArray; + } catch (err) { throw new Error('Error in convertDocPosts:', err); } -} - +}; function addDocButtons(docPosts, treePosts) { let structuredPosts = []; - let rootSections = []; + const rootSections = []; try { // Traversing the whole DocTree and storing each post inside them in sequential order Object.keys(treePosts).forEach((rootElement) => { structuredPosts.push(treePosts[rootElement].item); if (treePosts[rootElement].children) { - let children = treePosts[rootElement].children; + const { children } = treePosts[rootElement]; Object.keys(children).forEach((child) => { - let docChildArray = convertDocPosts(children[child]); + const docChildArray = convertDocPosts(children[child]); structuredPosts = [...structuredPosts, ...docChildArray]; }); } }); // Appending the content of welcome page of Docs from the posts.json - structuredPosts[0] = docPosts.filter(p => p.slug === '/docs')[0]; + structuredPosts[0] = docPosts.filter((p) => p.slug === '/docs')[0]; // Traversing the structuredPosts in order to add `nextPage` and `prevPage` details for each page - let countDocPages = structuredPosts.length; + const countDocPages = structuredPosts.length; structuredPosts = structuredPosts.map((post, index) => { - // post item specifying the root Section or sub-section in the docs are excluded as - // they doesn't comprise any Doc Page or content to be shown in website. + // post item specifying the root Section or sub-section in the docs are excluded as + // they doesn't comprise any Doc Page or content to be shown in website. if (post?.isRootSection || post?.isSection || index == 0) { - if (post?.isRootSection || index == 0) - rootSections.push(post.title) - return post + if (post?.isRootSection || index == 0) rootSections.push(post.title); + return post; } - let nextPage = {}, prevPage = {} + let nextPage = {}; + let prevPage = {}; let docPost = post; // checks whether the next page for the current docPost item exists or not @@ -139,14 +148,14 @@ function addDocButtons(docPosts, treePosts) { nextPage = { title: structuredPosts[index + 1].title, href: structuredPosts[index + 1].slug - } + }; } else { nextPage = { title: `${structuredPosts[index + 1].title} - ${structuredPosts[index + 2].title}`, href: structuredPosts[index + 2].slug - } + }; } - docPost = { ...docPost, nextPage } + docPost = { ...docPost, nextPage }; } // checks whether the previous page for the current docPost item exists or not @@ -157,8 +166,8 @@ function addDocButtons(docPosts, treePosts) { prevPage = { title: structuredPosts[index - 1].title, href: structuredPosts[index - 1].slug - } - docPost = { ...docPost, prevPage } + }; + docPost = { ...docPost, prevPage }; } else { // additonal check for the first page of Docs so that it doesn't give any Segementation fault if (index - 2 >= 0) { @@ -172,11 +181,10 @@ function addDocButtons(docPosts, treePosts) { } return docPost; }); - } catch (err) { - throw new Error("An error occurred while adding doc buttons:", err); + throw new Error('An error occurred while adding doc buttons:', err); } return structuredPosts; } -module.exports = { buildNavTree, addDocButtons, convertDocPosts } \ No newline at end of file +module.exports = { buildNavTree, addDocButtons, convertDocPosts }; diff --git a/scripts/build-meetings.js b/scripts/build-meetings.js index ee95803d9d44..ac556b7578d4 100644 --- a/scripts/build-meetings.js +++ b/scripts/build-meetings.js @@ -9,11 +9,10 @@ async function buildMeetings(writePath) { try { auth = new google.auth.GoogleAuth({ scopes: ['https://www.googleapis.com/auth/calendar'], - credentials: process.env.CALENDAR_SERVICE_ACCOUNT ? JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT) : undefined, + credentials: process.env.CALENDAR_SERVICE_ACCOUNT ? JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT) : undefined }); calendar = google.calendar({ version: 'v3', auth }); - } catch (err) { throw new Error(`Authentication failed: ${err.message}`); } @@ -21,19 +20,15 @@ async function buildMeetings(writePath) { let eventsItems; try { - //cron job runs this always on midnight + // cron job runs this always on midnight const currentTime = new Date(Date.now()).toISOString(); - const timeMin = new Date( - Date.parse(currentTime) - 100 * 24 * 60 * 60 * 1000 - ).toISOString(); - const timeMax = new Date( - Date.parse(currentTime) + 30 * 24 * 60 * 60 * 1000 - ).toISOString(); + const timeMin = new Date(Date.parse(currentTime) - 100 * 24 * 60 * 60 * 1000).toISOString(); + const timeMax = new Date(Date.parse(currentTime) + 30 * 24 * 60 * 60 * 1000).toISOString(); const eventsList = await calendar.events.list({ calendarId: process.env.CALENDAR_ID, - timeMax: timeMax, - timeMin: timeMin, + timeMax, + timeMin }); eventsItems = eventsList.data.items.map((e) => { @@ -43,9 +38,8 @@ async function buildMeetings(writePath) { url: e.extendedProperties?.private && `https://github.com/asyncapi/community/issues/${e.extendedProperties.private.ISSUE_ID}`, - banner: - e.extendedProperties?.private && e.extendedProperties.private.BANNER, - date: new Date(e.start.dateTime), + banner: e.extendedProperties?.private && e.extendedProperties.private.BANNER, + date: new Date(e.start.dateTime) }; }); @@ -53,7 +47,6 @@ async function buildMeetings(writePath) { console.log('The following events got fetched', eventsForHuman); writeFileSync(writePath, eventsForHuman); - } catch (err) { throw new Error(`Failed to fetch or process events: ${err.message}`); } diff --git a/scripts/build-newsroom-videos.js b/scripts/build-newsroom-videos.js index 383927765d36..40ad1617362e 100644 --- a/scripts/build-newsroom-videos.js +++ b/scripts/build-newsroom-videos.js @@ -3,49 +3,51 @@ const { resolve } = require('path'); const fetch = require('node-fetch-2'); async function buildNewsroomVideos(writePath) { - try { - const response = await fetch('https://youtube.googleapis.com/youtube/v3/search?' + new URLSearchParams({ - key: process.env.YOUTUBE_TOKEN, - part: 'snippet', - channelId: 'UCIz9zGwDLbrYQcDKVXdOstQ', - eventType: 'completed', - type: 'video', - order: 'Date', - maxResults: 5, - })); - - if (!response.ok) { - throw new Error(`HTTP error! with status code: ${response.status}`); - } - - const data = await response.json(); - console.log(data); - - if (!data.items || !Array.isArray(data.items)) { - throw new Error('Invalid data structure received from YouTube API'); - } - - const videoDataItems = data.items.map((video) => ({ - image_url: video.snippet.thumbnails.high.url, - title: video.snippet.title, - description: video.snippet.description, - videoId: video.id.videoId, - })); - - const videoData = JSON.stringify(videoDataItems, null, ' '); - console.log('The following are the Newsroom Youtube videos: ', videoData); - - writeFileSync(writePath, videoData); - - return videoData; - } catch (err) { - throw new Error(`Failed to build newsroom videos: ${err.message}`); + try { + const response = await fetch( + `https://youtube.googleapis.com/youtube/v3/search?${new URLSearchParams({ + key: process.env.YOUTUBE_TOKEN, + part: 'snippet', + channelId: 'UCIz9zGwDLbrYQcDKVXdOstQ', + eventType: 'completed', + type: 'video', + order: 'Date', + maxResults: 5 + })}` + ); + + if (!response.ok) { + throw new Error(`HTTP error! with status code: ${response.status}`); } + + const data = await response.json(); + console.log(data); + + if (!data.items || !Array.isArray(data.items)) { + throw new Error('Invalid data structure received from YouTube API'); + } + + const videoDataItems = data.items.map((video) => ({ + image_url: video.snippet.thumbnails.high.url, + title: video.snippet.title, + description: video.snippet.description, + videoId: video.id.videoId + })); + + const videoData = JSON.stringify(videoDataItems, null, ' '); + console.log('The following are the Newsroom Youtube videos: ', videoData); + + writeFileSync(writePath, videoData); + + return videoData; + } catch (err) { + throw new Error(`Failed to build newsroom videos: ${err.message}`); + } } /* istanbul ignore next */ if (require.main === module) { - buildNewsroomVideos(resolve(__dirname, '../config', 'newsroom_videos.json')) + buildNewsroomVideos(resolve(__dirname, '../config', 'newsroom_videos.json')); } module.exports = { buildNewsroomVideos }; diff --git a/scripts/build-pages.js b/scripts/build-pages.js index 48b3553e96b2..287d44f046b0 100644 --- a/scripts/build-pages.js +++ b/scripts/build-pages.js @@ -57,4 +57,4 @@ function copyAndRenameFiles(srcDir, targetDir) { copyAndRenameFiles(SRC_DIR, TARGET_DIR); -module.exports = {copyAndRenameFiles,capitalizeJsxTags} \ No newline at end of file +module.exports = { copyAndRenameFiles, capitalizeJsxTags }; diff --git a/scripts/build-post-list.js b/scripts/build-post-list.js index 288d7dc0c54e..3cfee35032ec 100644 --- a/scripts/build-post-list.js +++ b/scripts/build-post-list.js @@ -1,21 +1,21 @@ -const { readdirSync, statSync, existsSync, readFileSync, writeFileSync } = require('fs') -const { resolve, basename } = require('path') -const frontMatter = require('gray-matter') -const toc = require('markdown-toc') -const { slugify } = require('markdown-toc/lib/utils') -const readingTime = require('reading-time') -const { markdownToTxt } = require('markdown-to-txt') -const { buildNavTree, addDocButtons } = require('./build-docs') +const { readdirSync, statSync, existsSync, readFileSync, writeFileSync } = require('fs'); +const { resolve, basename } = require('path'); +const frontMatter = require('gray-matter'); +const toc = require('markdown-toc'); +const { slugify } = require('markdown-toc/lib/utils'); +const readingTime = require('reading-time'); +const { markdownToTxt } = require('markdown-to-txt'); +const { buildNavTree, addDocButtons } = require('./build-docs'); -let specWeight = 100 +let specWeight = 100; const result = { docs: [], blog: [], about: [], docsTree: {} -} -const releaseNotes = [] -const basePath = 'pages' +}; +const releaseNotes = []; +const basePath = 'pages'; const postDirectories = [ // order of these directories is important, as the blog should come before docs, to create a list of available release notes, which will later be used to release-note-link for spec docs [`${basePath}/blog`, '/blog'], @@ -24,138 +24,139 @@ const postDirectories = [ ]; const addItem = (details) => { - if(details.slug.startsWith('/docs')) - result["docs"].push(details) - else if(details.slug.startsWith('/blog')) - result["blog"].push(details) - else if(details.slug.startsWith('/about')) - result["about"].push(details) - else {} -} + if (details.slug.startsWith('/docs')) result.docs.push(details); + else if (details.slug.startsWith('/blog')) result.blog.push(details); + else if (details.slug.startsWith('/about')) result.about.push(details); + else { + } +}; module.exports = async function buildPostList() { - walkDirectories(postDirectories, result) - const treePosts = buildNavTree(result["docs"].filter((p) => p.slug.startsWith('/docs/'))) - result["docsTree"] = treePosts - result["docs"] = addDocButtons(result["docs"], treePosts) + walkDirectories(postDirectories, result); + const treePosts = buildNavTree(result.docs.filter((p) => p.slug.startsWith('/docs/'))); + result.docsTree = treePosts; + result.docs = addDocButtons(result.docs, treePosts); if (process.env.NODE_ENV === 'production') { // console.log(inspect(result, { depth: null, colors: true })) } - writeFileSync(resolve(__dirname, '..', 'config', 'posts.json'), JSON.stringify(result, null, ' ')) -} + writeFileSync(resolve(__dirname, '..', 'config', 'posts.json'), JSON.stringify(result, null, ' ')); +}; function walkDirectories(directories, result, sectionWeight = 0, sectionTitle, sectionId, rootSectionId) { - for (let dir of directories) { - let directory = dir[0] - let sectionSlug = dir[1] || '' - let files = readdirSync(directory); + for (const dir of directories) { + const directory = dir[0]; + const sectionSlug = dir[1] || ''; + const files = readdirSync(directory); - for (let file of files) { - let details - const fileName = [directory, file].join('/') - const fileNameWithSection = [fileName, '_section.mdx'].join('/') - const slug = fileName.replace(new RegExp(`^${basePath}`), '') + for (const file of files) { + let details; + const fileName = [directory, file].join('/'); + const fileNameWithSection = [fileName, '_section.mdx'].join('/'); + const slug = fileName.replace(new RegExp(`^${basePath}`), ''); const slugElements = slug.split('/'); if (isDirectory(fileName)) { if (existsSync(fileNameWithSection)) { // Passing a second argument to frontMatter disables cache. See https://github.com/asyncapi/website/issues/1057 - details = frontMatter(readFileSync(fileNameWithSection, 'utf-8'), {}).data - details.title = details.title || capitalize(basename(fileName)) + details = frontMatter(readFileSync(fileNameWithSection, 'utf-8'), {}).data; + details.title = details.title || capitalize(basename(fileName)); } else { details = { - title: capitalize(basename(fileName)), - } + title: capitalize(basename(fileName)) + }; } - details.isSection = true + details.isSection = true; if (slugElements.length > 3) { - details.parent = slugElements[slugElements.length - 2] - details.sectionId = slugElements[slugElements.length - 1] + details.parent = slugElements[slugElements.length - 2]; + details.sectionId = slugElements[slugElements.length - 1]; } if (!details.parent) { - details.isRootSection = true - details.rootSectionId = slugElements[slugElements.length - 1] + details.isRootSection = true; + details.rootSectionId = slugElements[slugElements.length - 1]; } - details.sectionWeight = sectionWeight - details.slug = slug - addItem(details) - const rootId = details.parent || details.rootSectionId - walkDirectories([[fileName, slug]], result, details.weight, details.title, details.sectionId, rootId) + details.sectionWeight = sectionWeight; + details.slug = slug; + addItem(details); + const rootId = details.parent || details.rootSectionId; + walkDirectories([[fileName, slug]], result, details.weight, details.title, details.sectionId, rootId); } else if (file.endsWith('.mdx') && !fileName.endsWith('/_section.mdx')) { - const fileContent = readFileSync(fileName, 'utf-8') + const fileContent = readFileSync(fileName, 'utf-8'); // Passing a second argument to frontMatter disables cache. See https://github.com/asyncapi/website/issues/1057 - const { data, content } = frontMatter(fileContent, {}) - details = data - details.toc = toc(content, { slugify: slugifyToC }).json - details.readingTime = Math.ceil(readingTime(content).minutes) - details.excerpt = details.excerpt || markdownToTxt(content).substr(0, 200) - details.sectionSlug = sectionSlug || slug.replace(/\.mdx$/, '') - details.sectionWeight = sectionWeight - details.sectionTitle = sectionTitle - details.sectionId = sectionId - details.rootSectionId = rootSectionId - details.id = fileName - details.isIndex = fileName.endsWith('/index.mdx') - details.slug = details.isIndex ? sectionSlug : slug.replace(/\.mdx$/, '') - if(details.slug.includes('/reference/specification/') && !details.title) { - const fileBaseName = basename(data.slug) // ex. v2.0.0 | v2.1.0-next-spec.1 - const fileName = fileBaseName.split('-')[0] // v2.0.0 | v2.1.0 - details.weight = specWeight-- + const { data, content } = frontMatter(fileContent, {}); + details = data; + details.toc = toc(content, { slugify: slugifyToC }).json; + details.readingTime = Math.ceil(readingTime(content).minutes); + details.excerpt = details.excerpt || markdownToTxt(content).substr(0, 200); + details.sectionSlug = sectionSlug || slug.replace(/\.mdx$/, ''); + details.sectionWeight = sectionWeight; + details.sectionTitle = sectionTitle; + details.sectionId = sectionId; + details.rootSectionId = rootSectionId; + details.id = fileName; + details.isIndex = fileName.endsWith('/index.mdx'); + details.slug = details.isIndex ? sectionSlug : slug.replace(/\.mdx$/, ''); + if (details.slug.includes('/reference/specification/') && !details.title) { + const fileBaseName = basename(data.slug); // ex. v2.0.0 | v2.1.0-next-spec.1 + const fileName = fileBaseName.split('-')[0]; // v2.0.0 | v2.1.0 + details.weight = specWeight--; if (fileName.startsWith('v')) { - details.title = capitalize(fileName.slice(1)) + details.title = capitalize(fileName.slice(1)); } else { - details.title = capitalize(fileName) + details.title = capitalize(fileName); } - if(releaseNotes.includes(details.title)){ - details.releaseNoteLink = `/blog/release-notes-${details.title}` + if (releaseNotes.includes(details.title)) { + details.releaseNoteLink = `/blog/release-notes-${details.title}`; } if (fileBaseName.includes('next-spec') || fileBaseName.includes('next-major-spec')) { - details.isPrerelease = true + details.isPrerelease = true; // this need to be separate because the `-` in "Pre-release" will get removed by `capitalize()` function - details.title += " (Pre-release)" + details.title += ' (Pre-release)'; } if (fileBaseName.includes('explorer')) { - details.title += " - Explorer" + details.title += ' - Explorer'; } } // To create a list of available ReleaseNotes list, which will be used to add details.releaseNoteLink attribute. - if(file.startsWith("release-notes") && dir[1] === "/blog"){ - const fileName_without_extension = file.slice(0,-4) + if (file.startsWith('release-notes') && dir[1] === '/blog') { + const fileName_without_extension = file.slice(0, -4); // removes the file extension. For example, release-notes-2.1.0.md -> release-notes-2.1.0 - const version = fileName_without_extension.slice(fileName_without_extension.lastIndexOf("-")+1) + const version = fileName_without_extension.slice(fileName_without_extension.lastIndexOf('-') + 1); // gets the version from the name of the releaseNote .md file (from /blog). For example, version = 2.1.0 if fileName_without_extension = release-notes-2.1.0 - releaseNotes.push(version) + releaseNotes.push(version); // releaseNotes is the list of all available releaseNotes } - addItem(details) + addItem(details); } } } } function slugifyToC(str) { - let slug + let slug; // Try to match heading ids like {# myHeadingId} - const headingIdMatch = str.match(/[\s]?\{\#([\w\d\-_]+)\}/) + const headingIdMatch = str.match(/[\s]?\{\#([\w\d\-_]+)\}/); if (headingIdMatch && headingIdMatch.length >= 2) { - slug = headingIdMatch[1] + slug = headingIdMatch[1]; } else { // Try to match heading ids like {} - const anchorTagMatch = str.match(/[\s]*= 2) slug = anchorTagMatch[1] + const anchorTagMatch = str.match(/[\s]*= 2) slug = anchorTagMatch[1]; } - return slug || slugify(str, { firsth1: true, maxdepth: 6 }) + return slug || slugify(str, { firsth1: true, maxdepth: 6 }); } function isDirectory(dir) { - return statSync(dir).isDirectory() + return statSync(dir).isDirectory(); } function capitalize(text) { - return text.split(/[\s\-]/g).map(word => `${word[0].toUpperCase()}${word.substr(1)}`).join(' ') + return text + .split(/[\s\-]/g) + .map((word) => `${word[0].toUpperCase()}${word.substr(1)}`) + .join(' '); } diff --git a/scripts/build-rss.js b/scripts/build-rss.js index 673da1398fe0..75195ea53ccf 100644 --- a/scripts/build-rss.js +++ b/scripts/build-rss.js @@ -1,26 +1,25 @@ -const fs = require('fs').promises -const json2xml = require('jgexml/json2xml') +const fs = require('fs').promises; +const json2xml = require('jgexml/json2xml'); function getAllPosts() { return require('../config/posts.json'); } function clean(s) { - s = s.split('<span>').join('') - s = s.split('&').join('&') - s = s.split(''').join("'") - s = s.split('<').join('<') - s = s.split('>').join('>') - s = s.split('"').join('"') - return s + s = s.split('<span>').join(''); + s = s.split('&').join('&'); + s = s.split(''').join("'"); + s = s.split('<').join('<'); + s = s.split('>').join('>'); + s = s.split('"').join('"'); + return s; } module.exports = async function rssFeed(type, title, desc, outputPath) { try { - - let posts = getAllPosts()[`${type}`] - const missingDatePosts = posts.filter(post => !post.date); - posts = posts.filter(post => post.date); + let posts = getAllPosts()[`${type}`]; + const missingDatePosts = posts.filter((post) => !post.date); + posts = posts.filter((post) => post.date); posts.sort((i1, i2) => { const i1Date = new Date(i1.date); const i2Date = new Date(i2.date); @@ -30,40 +29,38 @@ module.exports = async function rssFeed(type, title, desc, outputPath) { }); if (missingDatePosts.length > 0) { - throw new Error(`Missing date in posts: ${missingDatePosts.map(p => p.title || p.slug).join(', ')}`); + throw new Error(`Missing date in posts: ${missingDatePosts.map((p) => p.title || p.slug).join(', ')}`); } - const base = 'https://www.asyncapi.com' + const base = 'https://www.asyncapi.com'; const tracking = '?utm_source=rss'; - const feed = {} - const rss = {} - rss['@version'] = '2.0' - rss["@xmlns:atom"] = 'http://www.w3.org/2005/Atom' - rss.channel = {} - rss.channel.title = title - rss.channel.link = `${base}/${outputPath}` - rss.channel["atom:link"] = {} - rss.channel["atom:link"]["@rel"] = 'self' - rss.channel["atom:link"]["@href"] = rss.channel.link - rss.channel["atom:link"]["@type"] = 'application/rss+xml' - rss.channel.description = desc + const feed = {}; + const rss = {}; + rss['@version'] = '2.0'; + rss['@xmlns:atom'] = 'http://www.w3.org/2005/Atom'; + rss.channel = {}; + rss.channel.title = title; + rss.channel.link = `${base}/${outputPath}`; + rss.channel['atom:link'] = {}; + rss.channel['atom:link']['@rel'] = 'self'; + rss.channel['atom:link']['@href'] = rss.channel.link; + rss.channel['atom:link']['@type'] = 'application/rss+xml'; + rss.channel.description = desc; rss.channel.language = 'en-gb'; rss.channel.copyright = 'Made with :love: by the AsyncAPI Initiative.'; - rss.channel.webMaster = 'info@asyncapi.io (AsyncAPI Initiative)' - rss.channel.pubDate = new Date().toUTCString() - rss.channel.generator = 'next.js' - rss.channel.item = [] + rss.channel.webMaster = 'info@asyncapi.io (AsyncAPI Initiative)'; + rss.channel.pubDate = new Date().toUTCString(); + rss.channel.generator = 'next.js'; + rss.channel.item = []; - const invalidPosts = posts.filter(post => - !post.title || !post.slug || !post.excerpt || !post.date - ); + const invalidPosts = posts.filter((post) => !post.title || !post.slug || !post.excerpt || !post.date); if (invalidPosts.length > 0) { - throw new Error(`Missing required fields in posts: ${invalidPosts.map(p => p.title || p.slug).join(', ')}`); + throw new Error(`Missing required fields in posts: ${invalidPosts.map((p) => p.title || p.slug).join(', ')}`); } - for (let post of posts) { + for (const post of posts) { const link = `${base}${post.slug}${tracking}`; const { title, excerpt, date } = post; const pubDate = new Date(date).toUTCString(); @@ -79,21 +76,21 @@ module.exports = async function rssFeed(type, title, desc, outputPath) { }; if (post.cover) { const enclosure = {}; - enclosure["@url"] = base + post.cover; - enclosure["@length"] = 15026; // dummy value, anything works - enclosure["@type"] = 'image/jpeg'; - if (typeof enclosure["@url"] === 'string') { - let tmp = enclosure["@url"].toLowerCase(); - if (tmp.indexOf('.png') >= 0) enclosure["@type"] = 'image/png'; - if (tmp.indexOf('.svg') >= 0) enclosure["@type"] = 'image/svg+xml'; - if (tmp.indexOf('.webp') >= 0) enclosure["@type"] = 'image/webp'; + enclosure['@url'] = base + post.cover; + enclosure['@length'] = 15026; // dummy value, anything works + enclosure['@type'] = 'image/jpeg'; + if (typeof enclosure['@url'] === 'string') { + const tmp = enclosure['@url'].toLowerCase(); + if (tmp.indexOf('.png') >= 0) enclosure['@type'] = 'image/png'; + if (tmp.indexOf('.svg') >= 0) enclosure['@type'] = 'image/svg+xml'; + if (tmp.indexOf('.webp') >= 0) enclosure['@type'] = 'image/webp'; } item.enclosure = enclosure; } - rss.channel.item.push(item) + rss.channel.item.push(item); } - feed.rss = rss + feed.rss = rss; const xml = json2xml.getXml(feed, '@', '', 2); await fs.writeFile(`./public/${outputPath}`, xml, 'utf8'); diff --git a/scripts/build-tools.js b/scripts/build-tools.js index c5cce74a7cb1..83db76534f11 100644 --- a/scripts/build-tools.js +++ b/scripts/build-tools.js @@ -1,13 +1,13 @@ +const fs = require('fs-extra'); +const { resolve } = require('path'); const { getData } = require('./tools/extract-tools-github'); const { convertTools } = require('./tools/tools-object'); const { combineTools } = require('./tools/combine-tools'); -const fs = require('fs-extra'); -const { resolve } = require('path'); const buildTools = async (automatedToolsPath, manualToolsPath, toolsPath, tagsPath) => { try { - let githubExtractData = await getData(); - let automatedTools = await convertTools(githubExtractData); + const githubExtractData = await getData(); + const automatedTools = await convertTools(githubExtractData); await fs.writeFile(automatedToolsPath, JSON.stringify(automatedTools, null, ' ')); diff --git a/scripts/casestudies/index.js b/scripts/casestudies/index.js index 77695e06fd38..22011781f1be 100644 --- a/scripts/casestudies/index.js +++ b/scripts/casestudies/index.js @@ -1,19 +1,19 @@ const { readdir, writeFile, readFile } = require('fs').promises; -const { convertToJson } = require('../../scripts/utils'); +const { convertToJson } = require('../utils'); module.exports = async function buildCaseStudiesList(dirWithCaseStudy, writeFilePath) { - try { - let files = await readdir(dirWithCaseStudy); - let caseStudiesList = []; - for (let file of files) { - const caseStudyFileName = [dirWithCaseStudy, file].join('/'); - const caseStudyContent = await readFile(caseStudyFileName, 'utf-8'); - const jsonContent = convertToJson(caseStudyContent); + try { + const files = await readdir(dirWithCaseStudy); + const caseStudiesList = []; + for (const file of files) { + const caseStudyFileName = [dirWithCaseStudy, file].join('/'); + const caseStudyContent = await readFile(caseStudyFileName, 'utf-8'); + const jsonContent = convertToJson(caseStudyContent); - caseStudiesList.push(jsonContent); - await writeFile(writeFilePath, JSON.stringify(caseStudiesList)) - } - } catch (err) { - throw new Error(err); + caseStudiesList.push(jsonContent); + await writeFile(writeFilePath, JSON.stringify(caseStudiesList)); } + } catch (err) { + throw new Error(err); + } }; diff --git a/scripts/compose.js b/scripts/compose.js index 8c4f0e3a4a36..18d8d724420d 100644 --- a/scripts/compose.js +++ b/scripts/compose.js @@ -2,25 +2,21 @@ * Script based on https://github.com/timlrx/tailwind-nextjs-starter-blog/blob/master/scripts/compose.js */ -const fs = require('fs') -const inquirer = require('inquirer') -const dedent = require('dedent') -const moment = require('moment') +const fs = require('fs'); +const inquirer = require('inquirer'); +const dedent = require('dedent'); +const moment = require('moment'); const genFrontMatter = (answers) => { - let d = new Date() - const date = [ - d.getFullYear(), - ('0' + (d.getMonth() + 1)).slice(-2), - ('0' + d.getDate()).slice(-2), - ].join('-') - const tagArray = answers.tags.split(',') - tagArray.forEach((tag, index) => (tagArray[index] = tag.trim())) - const tags = "'" + tagArray.join("','") + "'" + const d = new Date(); + const date = [d.getFullYear(), `0${d.getMonth() + 1}`.slice(-2), `0${d.getDate()}`.slice(-2)].join('-'); + const tagArray = answers.tags.split(','); + tagArray.forEach((tag, index) => (tagArray[index] = tag.trim())); + const tags = `'${tagArray.join("','")}'`; let frontMatter = dedent`--- title: ${answers.title ? answers.title : 'Untitled'} - date: ${moment().format("YYYY-MM-DDTh:mm:ssZ")} + date: ${moment().format('YYYY-MM-DDTh:mm:ssZ')} type: ${answers.type} canonical: ${answers.canonical ? answers.canonical : ''} tags: [${answers.tags ? tags : ''}] @@ -90,41 +86,41 @@ const genFrontMatter = (answers) => {
- ` + `; - frontMatter = frontMatter + '\n---' + frontMatter += '\n---'; - return frontMatter -} + return frontMatter; +}; inquirer .prompt([ { name: 'title', message: 'Enter post title:', - type: 'input', + type: 'input' }, { name: 'excerpt', message: 'Enter post excerpt:', - type: 'input', + type: 'input' }, { name: 'tags', message: 'Any Tags? Separate them with , or leave empty if no tags.', - type: 'input', + type: 'input' }, { name: 'type', message: 'Enter the post type:', type: 'list', - choices: ['Communication', 'Community', 'Engineering', 'Marketing', 'Strategy', 'Video'], + choices: ['Communication', 'Community', 'Engineering', 'Marketing', 'Strategy', 'Video'] }, { name: 'canonical', message: 'Enter the canonical URL if any:', - type: 'input', - }, + type: 'input' + } ]) .then((answers) => { // Remove special characters and replace space with - @@ -132,22 +128,22 @@ inquirer .toLowerCase() .replace(/[^a-zA-Z0-9 ]/g, '') .replace(/ /g, '-') - .replace(/-+/g, '-') - const frontMatter = genFrontMatter(answers) - const filePath = `pages/blog/${fileName ? fileName : 'untitled'}.md` + .replace(/-+/g, '-'); + const frontMatter = genFrontMatter(answers); + const filePath = `pages/blog/${fileName || 'untitled'}.md`; fs.writeFile(filePath, frontMatter, { flag: 'wx' }, (err) => { if (err) { - throw err + throw err; } else { - console.log(`Blog post generated successfully at ${filePath}`) + console.log(`Blog post generated successfully at ${filePath}`); } - }) + }); }) .catch((error) => { - console.error(error) + console.error(error); if (error.isTtyError) { - console.log("Prompt couldn't be rendered in the current environment") + console.log("Prompt couldn't be rendered in the current environment"); } else { - console.log('Something went wrong, sorry!') + console.log('Something went wrong, sorry!'); } - }) + }); diff --git a/scripts/dashboard/build-dashboard.js b/scripts/dashboard/build-dashboard.js index c20be204e87b..00e2049a6338 100644 --- a/scripts/dashboard/build-dashboard.js +++ b/scripts/dashboard/build-dashboard.js @@ -81,8 +81,8 @@ async function processHotDiscussions(batch) { const finalInteractionsCount = isPR ? interactionsCount + - discussion.reviews.totalCount + - discussion.reviews.nodes.reduce((acc, curr) => acc + curr.comments.totalCount, 0) + discussion.reviews.totalCount + + discussion.reviews.nodes.reduce((acc, curr) => acc + curr.comments.totalCount, 0) : interactionsCount; return { @@ -181,4 +181,14 @@ if (require.main === module) { start(resolve(__dirname, '..', '..', 'dashboard.json')); } -module.exports = { getLabel, monthsSince, mapGoodFirstIssues, getHotDiscussions, getDiscussionByID, getDiscussions, writeToFile, start, processHotDiscussions }; +module.exports = { + getLabel, + monthsSince, + mapGoodFirstIssues, + getHotDiscussions, + getDiscussionByID, + getDiscussions, + writeToFile, + start, + processHotDiscussions +}; diff --git a/scripts/dashboard/issue-queries.js b/scripts/dashboard/issue-queries.js index 629214a9ea90..4b2e5a87853c 100644 --- a/scripts/dashboard/issue-queries.js +++ b/scripts/dashboard/issue-queries.js @@ -274,5 +274,5 @@ query($first: Int!, $after: String) { resetAt } } -`, +` }); diff --git a/scripts/finance/index.js b/scripts/finance/index.js index 3f4a5edcfb6e..c5ab35346c0d 100644 --- a/scripts/finance/index.js +++ b/scripts/finance/index.js @@ -1,25 +1,25 @@ const { - promises: { mkdir } + promises: { mkdir } } = require('fs'); const { resolve } = require('path'); const writeJSON = require('../utils/readAndWriteJson.js'); module.exports = async function buildFinanceInfoList({ currentDir, configDir, financeDir, year, jsonDataDir }) { - try { - const expensesPath = resolve(currentDir, configDir, financeDir, year, 'Expenses.yml'); - const expensesLinkPath = resolve(currentDir, configDir, financeDir, year, 'ExpensesLink.yml'); + try { + const expensesPath = resolve(currentDir, configDir, financeDir, year, 'Expenses.yml'); + const expensesLinkPath = resolve(currentDir, configDir, financeDir, year, 'ExpensesLink.yml'); - // Ensure the directory exists before writing the files - const jsonDirectory = resolve(currentDir, configDir, financeDir, jsonDataDir); - await mkdir(jsonDirectory, { recursive: true }); + // Ensure the directory exists before writing the files + const jsonDirectory = resolve(currentDir, configDir, financeDir, jsonDataDir); + await mkdir(jsonDirectory, { recursive: true }); - // Write Expenses and ExpensesLink to JSON files - const expensesJsonPath = resolve(jsonDirectory, 'Expenses.json'); - await writeJSON(expensesPath, expensesJsonPath); + // Write Expenses and ExpensesLink to JSON files + const expensesJsonPath = resolve(jsonDirectory, 'Expenses.json'); + await writeJSON(expensesPath, expensesJsonPath); - const expensesLinkJsonPath = resolve(jsonDirectory, 'ExpensesLink.json'); - await writeJSON(expensesLinkPath, expensesLinkJsonPath); - } catch (err) { - throw new Error(err); - } -}; \ No newline at end of file + const expensesLinkJsonPath = resolve(jsonDirectory, 'ExpensesLink.json'); + await writeJSON(expensesLinkPath, expensesLinkJsonPath); + } catch (err) { + throw new Error(err); + } +}; diff --git a/scripts/index.js b/scripts/index.js index 33125fe7533b..afe99ff7ca11 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -8,16 +8,8 @@ const buildFinanceInfoList = require('./finance'); async function start() { await buildPostList(); - rssFeed( - 'blog', - 'AsyncAPI Initiative Blog RSS Feed', - 'AsyncAPI Initiative Blog', - 'rss.xml' - ); - await buildCaseStudiesList( - 'config/casestudies', - resolve(__dirname, '../config', 'case-studies.json') - ); + rssFeed('blog', 'AsyncAPI Initiative Blog RSS Feed', 'AsyncAPI Initiative Blog', 'rss.xml'); + await buildCaseStudiesList('config/casestudies', resolve(__dirname, '../config', 'case-studies.json')); await buildAdoptersList(); const financeDir = resolve('.', 'config', 'finance'); diff --git a/scripts/markdown/check-markdown.js b/scripts/markdown/check-markdown.js index 8979f7e0b4ab..7088a1c4d0fc 100644 --- a/scripts/markdown/check-markdown.js +++ b/scripts/markdown/check-markdown.js @@ -8,12 +8,12 @@ const path = require('path'); * @returns {boolean} True if the string is a valid URL, false otherwise. */ function isValidURL(str) { - try { - new URL(str); - return true; - } catch (err) { - return false; - } + try { + new URL(str); + return true; + } catch (err) { + return false; + } } /** @@ -23,51 +23,51 @@ function isValidURL(str) { * @returns {string[]|null} An array of validation error messages, or null if no errors. */ function validateBlogs(frontmatter) { - const requiredAttributes = ['title', 'date', 'type', 'tags', 'cover', 'authors']; - const errors = []; + const requiredAttributes = ['title', 'date', 'type', 'tags', 'cover', 'authors']; + const errors = []; - // Check for required attributes - requiredAttributes.forEach(attr => { - if (!frontmatter.hasOwnProperty(attr)) { - errors.push(`${attr} is missing`); - } - }); - - // Validate date format - if (frontmatter.date && Number.isNaN(Date.parse(frontmatter.date))) { - errors.push(`Invalid date format: ${frontmatter.date}`); + // Check for required attributes + requiredAttributes.forEach((attr) => { + if (!frontmatter.hasOwnProperty(attr)) { + errors.push(`${attr} is missing`); } - - // Validate tags format (must be an array) - if (frontmatter.tags && !Array.isArray(frontmatter.tags)) { - errors.push(`Tags should be an array`); - } - - // Validate cover is a string - if (frontmatter.cover && typeof frontmatter.cover !== 'string') { - errors.push(`Cover must be a string`); - } - - // Validate authors (must be an array with valid attributes) - if (frontmatter.authors) { - if (!Array.isArray(frontmatter.authors)) { - errors.push('Authors should be an array'); - } else { - frontmatter.authors.forEach((author, index) => { - if (!author.name) { - errors.push(`Author at index ${index} is missing a name`); - } - if (author.link && !isValidURL(author.link)) { - errors.push(`Invalid URL for author at index ${index}: ${author.link}`); - } - if (!author.photo) { - errors.push(`Author at index ${index} is missing a photo`); - } - }); + }); + + // Validate date format + if (frontmatter.date && Number.isNaN(Date.parse(frontmatter.date))) { + errors.push(`Invalid date format: ${frontmatter.date}`); + } + + // Validate tags format (must be an array) + if (frontmatter.tags && !Array.isArray(frontmatter.tags)) { + errors.push(`Tags should be an array`); + } + + // Validate cover is a string + if (frontmatter.cover && typeof frontmatter.cover !== 'string') { + errors.push(`Cover must be a string`); + } + + // Validate authors (must be an array with valid attributes) + if (frontmatter.authors) { + if (!Array.isArray(frontmatter.authors)) { + errors.push('Authors should be an array'); + } else { + frontmatter.authors.forEach((author, index) => { + if (!author.name) { + errors.push(`Author at index ${index} is missing a name`); } + if (author.link && !isValidURL(author.link)) { + errors.push(`Invalid URL for author at index ${index}: ${author.link}`); + } + if (!author.photo) { + errors.push(`Author at index ${index} is missing a photo`); + } + }); } + } - return errors.length ? errors : null; + return errors.length ? errors : null; } /** @@ -77,19 +77,19 @@ function validateBlogs(frontmatter) { * @returns {string[]|null} An array of validation error messages, or null if no errors. */ function validateDocs(frontmatter) { - const errors = []; + const errors = []; - // Check if title exists and is a string - if (!frontmatter.title || typeof frontmatter.title !== 'string') { - errors.push('Title is missing or not a string'); - } + // Check if title exists and is a string + if (!frontmatter.title || typeof frontmatter.title !== 'string') { + errors.push('Title is missing or not a string'); + } - // Check if weight exists and is a number - if (frontmatter.weight === undefined || typeof frontmatter.weight !== 'number') { - errors.push('Weight is missing or not a number'); - } + // Check if weight exists and is a number + if (frontmatter.weight === undefined || typeof frontmatter.weight !== 'number') { + errors.push('Weight is missing or not a number'); + } - return errors.length ? errors : null; + return errors.length ? errors : null; } /** @@ -99,44 +99,44 @@ function validateDocs(frontmatter) { * @param {string} [relativePath=''] - The relative path of the folder for logging purposes. */ function checkMarkdownFiles(folderPath, validateFunction, relativePath = '') { - fs.readdir(folderPath, (err, files) => { + fs.readdir(folderPath, (err, files) => { + if (err) { + console.error('Error reading directory:', err); + return; + } + + files.forEach((file) => { + const filePath = path.join(folderPath, file); + const relativeFilePath = path.join(relativePath, file); + + // Skip the folder 'docs/reference/specification' + if (relativeFilePath.includes('reference/specification')) { + return; + } + + fs.stat(filePath, (err, stats) => { if (err) { - console.error('Error reading directory:', err); - return; + console.error('Error reading file stats:', err); + return; } - files.forEach(file => { - const filePath = path.join(folderPath, file); - const relativeFilePath = path.join(relativePath, file); - - // Skip the folder 'docs/reference/specification' - if (relativeFilePath.includes('reference/specification')) { - return; - } - - fs.stat(filePath, (err, stats) => { - if (err) { - console.error('Error reading file stats:', err); - return; - } - - // Recurse if directory, otherwise validate markdown file - if (stats.isDirectory()) { - checkMarkdownFiles(filePath, validateFunction, relativeFilePath); - } else if (path.extname(file) === '.md') { - const fileContent = fs.readFileSync(filePath, 'utf-8'); - const { data: frontmatter } = matter(fileContent); - - const errors = validateFunction(frontmatter); - if (errors) { - console.log(`Errors in file ${relativeFilePath}:`); - errors.forEach(error => console.log(` - ${error}`)); - process.exitCode = 1; - } - } - }); - }); + // Recurse if directory, otherwise validate markdown file + if (stats.isDirectory()) { + checkMarkdownFiles(filePath, validateFunction, relativeFilePath); + } else if (path.extname(file) === '.md') { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const { data: frontmatter } = matter(fileContent); + + const errors = validateFunction(frontmatter); + if (errors) { + console.log(`Errors in file ${relativeFilePath}:`); + errors.forEach((error) => console.log(` - ${error}`)); + process.exitCode = 1; + } + } + }); }); + }); } const docsFolderPath = path.resolve(__dirname, '../../markdown/docs'); diff --git a/scripts/tools/categorylist.js b/scripts/tools/categorylist.js index 11fcc3790e9e..dc81be15a320 100644 --- a/scripts/tools/categorylist.js +++ b/scripts/tools/categorylist.js @@ -1,100 +1,106 @@ // Various categories to define the category in which a tool has to be listed const categoryList = [ { - name: "APIs", - tag: "api", - description: "The following is a list of APIs that expose functionality related to AsyncAPI." + name: 'APIs', + tag: 'api', + description: 'The following is a list of APIs that expose functionality related to AsyncAPI.' }, { - name: "Code-first tools", - tag: "code-first", - description: "The following is a list of tools that generate AsyncAPI documents from your code." + name: 'Code-first tools', + tag: 'code-first', + description: 'The following is a list of tools that generate AsyncAPI documents from your code.' }, { - name: "Code Generators", - tag: "code-generator", - description: "The following is a list of tools that generate code from an AsyncAPI document; not the other way around." + name: 'Code Generators', + tag: 'code-generator', + description: + 'The following is a list of tools that generate code from an AsyncAPI document; not the other way around.' }, { - name: "Converters", - tag: "converter", - description: "The following is a list of tools that do not yet belong to any specific category but are also useful for the community." + name: 'Converters', + tag: 'converter', + description: + 'The following is a list of tools that do not yet belong to any specific category but are also useful for the community.' }, { - name: "Directories", - tag: "directory", - description: "The following is a list of directories that index public AsyncAPI documents." + name: 'Directories', + tag: 'directory', + description: 'The following is a list of directories that index public AsyncAPI documents.' }, { - name: "Documentation Generators", - tag: "documentation-generator", - description: "The following is a list of tools that generate human-readable documentation from an AsyncAPI document." + name: 'Documentation Generators', + tag: 'documentation-generator', + description: + 'The following is a list of tools that generate human-readable documentation from an AsyncAPI document.' }, { - name: "Editors", - tag: "editor", - description: "The following is a list of editors or related tools that allow editing of AsyncAPI document." + name: 'Editors', + tag: 'editor', + description: 'The following is a list of editors or related tools that allow editing of AsyncAPI document.' }, { - name: "UI components", - tag: "ui-component", - description: "The following is a list of UI components to view AsyncAPI documents." + name: 'UI components', + tag: 'ui-component', + description: 'The following is a list of UI components to view AsyncAPI documents.' }, { - name: "DSL", - tag: "dsl", - description: "Writing YAML by hand is no fun, and maybe you don't want a GUI, so use a Domain Specific Language to write AsyncAPI in your language of choice." + name: 'DSL', + tag: 'dsl', + description: + "Writing YAML by hand is no fun, and maybe you don't want a GUI, so use a Domain Specific Language to write AsyncAPI in your language of choice." }, { - name: "Frameworks", - tag: "framework", - description: "The following is a list of API/application frameworks that make use of AsyncAPI." + name: 'Frameworks', + tag: 'framework', + description: 'The following is a list of API/application frameworks that make use of AsyncAPI.' }, { - name: "GitHub Actions", - tag: "github-action", - description: "The following is a list of GitHub Actions that you can use in your workflows" + name: 'GitHub Actions', + tag: 'github-action', + description: 'The following is a list of GitHub Actions that you can use in your workflows' }, { - name: "Mocking and Testing", - tag: "mocking-and-testing", - description: "The tools below take specification documents as input, then publish fake messages to broker destinations for simulation purposes. They may also check that publisher messages are compliant with schemas." + name: 'Mocking and Testing', + tag: 'mocking-and-testing', + description: + 'The tools below take specification documents as input, then publish fake messages to broker destinations for simulation purposes. They may also check that publisher messages are compliant with schemas.' }, { - name: "Validators", - tag: "validator", - description: "The following is a list of tools that validate AsyncAPI documents." + name: 'Validators', + tag: 'validator', + description: 'The following is a list of tools that validate AsyncAPI documents.' }, { - name: "Compare tools", - tag: "compare-tool", - description: "The following is a list of tools that compare AsyncAPI documents." + name: 'Compare tools', + tag: 'compare-tool', + description: 'The following is a list of tools that compare AsyncAPI documents.' }, { - name: "CLIs", - tag: "cli", - description: "The following is a list of tools that you can work with in terminal or do some CI/CD automation." + name: 'CLIs', + tag: 'cli', + description: 'The following is a list of tools that you can work with in terminal or do some CI/CD automation.' }, { - name: "Bundlers", - tag: "bundler", - description: "The following is a list of tools that you can work with to bundle AsyncAPI documents." + name: 'Bundlers', + tag: 'bundler', + description: 'The following is a list of tools that you can work with to bundle AsyncAPI documents.' }, { - name: "IDE Extensions", - tag: "ide-extension", - description: "The following is a list of extensions for different IDEs like VSCode, IntelliJ IDEA and others" + name: 'IDE Extensions', + tag: 'ide-extension', + description: 'The following is a list of extensions for different IDEs like VSCode, IntelliJ IDEA and others' }, { - name: "AsyncAPI Generator Templates", - tag: "generator-template", - description: "The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents." + name: 'AsyncAPI Generator Templates', + tag: 'generator-template', + description: + 'The following is a list of templates compatible with AsyncAPI Generator. You can use them to generate apps, clients or documentation from your AsyncAPI documents.' }, { - name: "Others", - tag: "other", - description: "The following is a list of tools that comes under Other category." + name: 'Others', + tag: 'other', + description: 'The following is a list of tools that comes under Other category.' } -] +]; -module.exports = {categoryList} +module.exports = { categoryList }; diff --git a/scripts/tools/combine-tools.js b/scripts/tools/combine-tools.js index 602262428fa1..01be1ec876dc 100644 --- a/scripts/tools/combine-tools.js +++ b/scripts/tools/combine-tools.js @@ -1,142 +1,142 @@ -const { languagesColor, technologiesColor } = require("./tags-color") -const { categoryList } = require("./categorylist.js") -const { createToolObject } = require("./tools-object") -const fs = require('fs') -const schema = require("./tools-schema.json"); -const Ajv = require("ajv") -const addFormats = require("ajv-formats") -const Fuse = require("fuse.js"); -const ajv = new Ajv() -addFormats(ajv, ["uri"]) -const validate = ajv.compile(schema) +const fs = require('fs'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); +const Fuse = require('fuse.js'); +const { languagesColor, technologiesColor } = require('./tags-color'); +const { categoryList } = require('./categorylist.js'); +const { createToolObject } = require('./tools-object'); +const schema = require('./tools-schema.json'); -let finalTools = {}; -for (var category of categoryList) { - finalTools[category.name] = { - description: category.description, - toolsList: [] - }; +const ajv = new Ajv(); +addFormats(ajv, ['uri']); +const validate = ajv.compile(schema); + +const finalTools = {}; +for (const category of categoryList) { + finalTools[category.name] = { + description: category.description, + toolsList: [] + }; } // Config options set for the Fuse object const options = { - includeScore: true, - shouldSort: true, - threshold: 0.39, - keys: ['name', 'color', 'borderColor'] -} + includeScore: true, + shouldSort: true, + threshold: 0.39, + keys: ['name', 'color', 'borderColor'] +}; -// Two seperate lists and Fuse objects initialised to search languages and technologies tags +// Two seperate lists and Fuse objects initialised to search languages and technologies tags // from specified list of same. -let languageList = [...languagesColor], technologyList = [...technologiesColor]; -let languageFuse = new Fuse(languageList, options), technologyFuse = new Fuse(technologyList, options) +const languageList = [...languagesColor]; +const technologyList = [...technologiesColor]; +let languageFuse = new Fuse(languageList, options); +let technologyFuse = new Fuse(technologyList, options); -// takes individual tool object and inserts borderColor and backgroundColor of the tags of +// takes individual tool object and inserts borderColor and backgroundColor of the tags of // languages and technologies, for Tool Card in website. const getFinalTool = async (toolObject) => { - let finalObject = toolObject; + const finalObject = toolObject; - //there might be a tool without language - if (toolObject.filters.language) { - let languageArray = [] - if (typeof toolObject.filters.language === 'string') { - const languageSearch = await languageFuse.search(toolObject.filters.language) - if (languageSearch.length) { - languageArray.push(languageSearch[0].item); - } else { - // adds a new language object in the Fuse list as well as in tool object - // so that it isn't missed out in the UI. - let languageObject = { - name: toolObject.filters.language, - color: 'bg-[#57f281]', - borderColor: 'border-[#37f069]' - } - languageList.push(languageObject); - languageArray.push(languageObject) - languageFuse = new Fuse(languageList, options) - } + // there might be a tool without language + if (toolObject.filters.language) { + const languageArray = []; + if (typeof toolObject.filters.language === 'string') { + const languageSearch = await languageFuse.search(toolObject.filters.language); + if (languageSearch.length) { + languageArray.push(languageSearch[0].item); + } else { + // adds a new language object in the Fuse list as well as in tool object + // so that it isn't missed out in the UI. + const languageObject = { + name: toolObject.filters.language, + color: 'bg-[#57f281]', + borderColor: 'border-[#37f069]' + }; + languageList.push(languageObject); + languageArray.push(languageObject); + languageFuse = new Fuse(languageList, options); + } + } else { + for (const language of toolObject?.filters?.language) { + const languageSearch = await languageFuse.search(language); + if (languageSearch.length > 0) { + languageArray.push(languageSearch[0].item); } else { - for (const language of toolObject?.filters?.language) { - const languageSearch = await languageFuse.search(language) - if (languageSearch.length > 0) { - languageArray.push(languageSearch[0].item); - } - else { - // adds a new language object in the Fuse list as well as in tool object - // so that it isn't missed out in the UI. - let languageObject = { - name: language, - color: 'bg-[#57f281]', - borderColor: 'border-[#37f069]' - } - languageList.push(languageObject); - languageArray.push(languageObject) - languageFuse = new Fuse(languageList, options) - } - } + // adds a new language object in the Fuse list as well as in tool object + // so that it isn't missed out in the UI. + const languageObject = { + name: language, + color: 'bg-[#57f281]', + borderColor: 'border-[#37f069]' + }; + languageList.push(languageObject); + languageArray.push(languageObject); + languageFuse = new Fuse(languageList, options); } - finalObject.filters.language = languageArray + } } - let technologyArray = []; - if (toolObject.filters.technology) { - for (const technology of toolObject?.filters?.technology) { - const technologySearch = await technologyFuse.search(technology) - if (technologySearch.length > 0) { - technologyArray.push(technologySearch[0].item); - } - else { - // adds a new technology object in the Fuse list as well as in tool object - // so that it isn't missed out in the UI. - let technologyObject = { - name: technology, - color: 'bg-[#61d0f2]', - borderColor: 'border-[#40ccf7]' - } - technologyList.push(technologyObject); - technologyArray.push(technologyObject); - technologyFuse = new Fuse(technologyList, options) - } - } + finalObject.filters.language = languageArray; + } + const technologyArray = []; + if (toolObject.filters.technology) { + for (const technology of toolObject?.filters?.technology) { + const technologySearch = await technologyFuse.search(technology); + if (technologySearch.length > 0) { + technologyArray.push(technologySearch[0].item); + } else { + // adds a new technology object in the Fuse list as well as in tool object + // so that it isn't missed out in the UI. + const technologyObject = { + name: technology, + color: 'bg-[#61d0f2]', + borderColor: 'border-[#40ccf7]' + }; + technologyList.push(technologyObject); + technologyArray.push(technologyObject); + technologyFuse = new Fuse(technologyList, options); + } } - finalObject.filters.technology = technologyArray; - return finalObject; -} - + } + finalObject.filters.technology = technologyArray; + return finalObject; +}; -// Combine the automated tools and manual tools list into single JSON object file, and +// Combine the automated tools and manual tools list into single JSON object file, and // lists down all the language and technology tags in one JSON file. const combineTools = async (automatedTools, manualTools, toolsPath, tagsPath) => { - for (const key in automatedTools) { - let finalToolsList = []; - if (automatedTools[key].toolsList.length) { - for (const tool of automatedTools[key].toolsList) { - finalToolsList.push(await getFinalTool(tool)) - } - } - if (manualTools[key] && manualTools[key].toolsList.length) { - for (const tool of manualTools[key].toolsList) { - let isAsyncAPIrepo; - const isValid = await validate(tool) - if (isValid) { - if (tool?.links?.repoUrl) { - const url = new URL(tool.links.repoUrl) - isAsyncAPIrepo = url.href.startsWith("https://github.com/asyncapi/") - } else isAsyncAPIrepo = false - let toolObject = await createToolObject(tool, "", "", isAsyncAPIrepo) - finalToolsList.push(await getFinalTool(toolObject)) - } else { - console.error('Script is not failing, it is just dropping errors for further investigation'); - console.error(`Invalid ${tool.title} .asyncapi-tool file.`); - console.error(`Located in manual-tools.json file`); - console.error('Validation errors:', JSON.stringify(validate.errors, null, 2)); - } - } + for (const key in automatedTools) { + const finalToolsList = []; + if (automatedTools[key].toolsList.length) { + for (const tool of automatedTools[key].toolsList) { + finalToolsList.push(await getFinalTool(tool)); + } + } + if (manualTools[key] && manualTools[key].toolsList.length) { + for (const tool of manualTools[key].toolsList) { + let isAsyncAPIrepo; + const isValid = await validate(tool); + if (isValid) { + if (tool?.links?.repoUrl) { + const url = new URL(tool.links.repoUrl); + isAsyncAPIrepo = url.href.startsWith('https://github.com/asyncapi/'); + } else isAsyncAPIrepo = false; + const toolObject = await createToolObject(tool, '', '', isAsyncAPIrepo); + finalToolsList.push(await getFinalTool(toolObject)); + } else { + console.error('Script is not failing, it is just dropping errors for further investigation'); + console.error(`Invalid ${tool.title} .asyncapi-tool file.`); + console.error(`Located in manual-tools.json file`); + console.error('Validation errors:', JSON.stringify(validate.errors, null, 2)); } - finalToolsList.sort((tool, anotherTool) => tool.title.localeCompare(anotherTool.title)); - finalTools[key].toolsList = finalToolsList + } } - fs.writeFileSync(toolsPath,JSON.stringify(finalTools)); - fs.writeFileSync(tagsPath,JSON.stringify({ languages: languageList, technologies: technologyList }),) -} + finalToolsList.sort((tool, anotherTool) => tool.title.localeCompare(anotherTool.title)); + finalTools[key].toolsList = finalToolsList; + } + fs.writeFileSync(toolsPath, JSON.stringify(finalTools)); + fs.writeFileSync(tagsPath, JSON.stringify({ languages: languageList, technologies: technologyList })); +}; -module.exports = { combineTools } \ No newline at end of file +module.exports = { combineTools }; diff --git a/scripts/tools/extract-tools-github.js b/scripts/tools/extract-tools-github.js index 55e96124b752..1db138a95add 100644 --- a/scripts/tools/extract-tools-github.js +++ b/scripts/tools/extract-tools-github.js @@ -1,17 +1,14 @@ const axios = require('axios'); -require('dotenv').config() +require('dotenv').config(); const getData = async () => { try { - const result = await axios.get( - 'https://api.github.com/search/code?q=filename:.asyncapi-tool', - { - headers: { - accept: 'application/vnd.github.text-match+json', - authorization: `token ${process.env.GITHUB_TOKEN}`, - }, + const result = await axios.get('https://api.github.com/search/code?q=filename:.asyncapi-tool', { + headers: { + accept: 'application/vnd.github.text-match+json', + authorization: `token ${process.env.GITHUB_TOKEN}` } - ); + }); return result.data; } catch (err) { @@ -19,4 +16,4 @@ const getData = async () => { } }; -module.exports = { getData }; \ No newline at end of file +module.exports = { getData }; diff --git a/scripts/tools/tags-color.js b/scripts/tools/tags-color.js index 9a18ca2058d5..3483de2b0b08 100644 --- a/scripts/tools/tags-color.js +++ b/scripts/tools/tags-color.js @@ -1,178 +1,178 @@ // Language and Technology tags along with their colors in UI are defined here. const languagesColor = [ - { - name: "Go/Golang", - color: "bg-[#8ECFDF]", - borderColor: "border-[#00AFD9]" - }, - { - name: "Java", - color: "bg-[#ECA2A4]", - borderColor: "border-[#EC2125]" - }, - { - name: "JavaScript", - color: "bg-[#F2F1C7]", - borderColor: "border-[#BFBE86]" - }, - { - name: "HTML", - color: "bg-[#E2A291]", - borderColor: "border-[#E44D26]" - }, - { - name: "C/C++", - color: "bg-[#93CDEF]", - borderColor: "border-[#0080CC]" - }, - { - name: "C#", - color: "bg-[#E3AFE0]", - borderColor: "border-[#9B4F96]" - }, - { - name: "Python", - color: "bg-[#A8D0EF]", - borderColor: "border-[#3878AB]" - }, - { - name: "TypeScript", - color: "bg-[#7DBCFE]", - borderColor: "border-[#2C78C7]" - }, - { - name: "Kotlin", - color: "bg-[#B1ACDF]", - borderColor: "border-[#756BD9]" - }, - { - name: "Scala", - color: "bg-[#FFA299]", - borderColor: "border-[#DF301F]" - }, - { - name: "Markdown", - color: "bg-[#BABEBF]", - borderColor: "border-[#445B64]" - }, - { - name: "YAML", - color: "bg-[#FFB764]", - borderColor: "border-[#F1901F]" - }, - { - name: "R", - color: "bg-[#84B5ED]", - borderColor: "border-[#246BBE]" - }, - { - name: "Ruby", - color: "bg-[#FF8289]", - borderColor: "border-[#FF000F]" - }, - { - name: "Rust", - color: "bg-[#FFB8AA]", - borderColor: "border-[#E43716]" - }, - { - name: "Shell", - color: "bg-[#87D4FF]", - borderColor: "border-[#389ED7]" - }, - { - name: "Groovy", - color: "bg-[#B6D5E5]", - borderColor: "border-[#609DBC]" - } -] + { + name: 'Go/Golang', + color: 'bg-[#8ECFDF]', + borderColor: 'border-[#00AFD9]' + }, + { + name: 'Java', + color: 'bg-[#ECA2A4]', + borderColor: 'border-[#EC2125]' + }, + { + name: 'JavaScript', + color: 'bg-[#F2F1C7]', + borderColor: 'border-[#BFBE86]' + }, + { + name: 'HTML', + color: 'bg-[#E2A291]', + borderColor: 'border-[#E44D26]' + }, + { + name: 'C/C++', + color: 'bg-[#93CDEF]', + borderColor: 'border-[#0080CC]' + }, + { + name: 'C#', + color: 'bg-[#E3AFE0]', + borderColor: 'border-[#9B4F96]' + }, + { + name: 'Python', + color: 'bg-[#A8D0EF]', + borderColor: 'border-[#3878AB]' + }, + { + name: 'TypeScript', + color: 'bg-[#7DBCFE]', + borderColor: 'border-[#2C78C7]' + }, + { + name: 'Kotlin', + color: 'bg-[#B1ACDF]', + borderColor: 'border-[#756BD9]' + }, + { + name: 'Scala', + color: 'bg-[#FFA299]', + borderColor: 'border-[#DF301F]' + }, + { + name: 'Markdown', + color: 'bg-[#BABEBF]', + borderColor: 'border-[#445B64]' + }, + { + name: 'YAML', + color: 'bg-[#FFB764]', + borderColor: 'border-[#F1901F]' + }, + { + name: 'R', + color: 'bg-[#84B5ED]', + borderColor: 'border-[#246BBE]' + }, + { + name: 'Ruby', + color: 'bg-[#FF8289]', + borderColor: 'border-[#FF000F]' + }, + { + name: 'Rust', + color: 'bg-[#FFB8AA]', + borderColor: 'border-[#E43716]' + }, + { + name: 'Shell', + color: 'bg-[#87D4FF]', + borderColor: 'border-[#389ED7]' + }, + { + name: 'Groovy', + color: 'bg-[#B6D5E5]', + borderColor: 'border-[#609DBC]' + } +]; const technologiesColor = [ - { - name: "Node.js", - color: "bg-[#BDFF67]", - borderColor: "border-[#84CE24]" - }, - { - name: "Hermes", - color: "bg-[#8AEEBD]", - borderColor: "border-[#2AB672]" - }, - { - name: "React JS", - color: "bg-[#9FECFA]", - borderColor: "border-[#08D8FE]" - }, - { - name: ".NET", - color: "bg-[#A184FF]", - borderColor: "border-[#5026D4]" - }, - { - name: "ASP.NET", - color: "bg-[#71C2FB]", - borderColor: "border-[#1577BC]" - }, - { - name: "Springboot", - color: "bg-[#98E279]", - borderColor: "border-[#68BC44]" - }, - { - name: "AWS", - color: "bg-[#FF9F59]", - borderColor: "border-[#EF6703]" - }, - { - name: "Docker", - color: "bg-[#B8E0FF]", - borderColor: "border-[#2596ED]" - }, - { - name: "Node-RED", - color: "bg-[#FF7474]", - borderColor: "border-[#8F0101]" - }, - { - name: "Maven", - color: "bg-[#FF6B80]", - borderColor: "border-[#CA1A33]" - }, - { - name: "Saas", - color: "bg-[#6AB8EC]", - borderColor: "border-[#2275AD]" - }, - { - name: "Kubernetes-native", - color: "bg-[#D7C7F2]", - borderColor: "border-[#A387D2]" - }, - { - name: "Scala", - color: "bg-[#D7C7F2]", - borderColor: "border-[#A387D2]" - }, - { - name: "Azure", - color: "bg-[#4B93FF]", - borderColor: "border-[#015ADF]" - }, - { - name: "Jenkins", - color: "bg-[#D7C7F2]", - borderColor: "border-[#A387D2]" - }, - { - name: "Flask", - color: "bg-[#D7C7F2]", - borderColor: "border-[#A387D2]" - }, - { - name: "Nest Js", - color: "bg-[#E1224E]", - borderColor: "border-[#B9012b]" - } -] + { + name: 'Node.js', + color: 'bg-[#BDFF67]', + borderColor: 'border-[#84CE24]' + }, + { + name: 'Hermes', + color: 'bg-[#8AEEBD]', + borderColor: 'border-[#2AB672]' + }, + { + name: 'React JS', + color: 'bg-[#9FECFA]', + borderColor: 'border-[#08D8FE]' + }, + { + name: '.NET', + color: 'bg-[#A184FF]', + borderColor: 'border-[#5026D4]' + }, + { + name: 'ASP.NET', + color: 'bg-[#71C2FB]', + borderColor: 'border-[#1577BC]' + }, + { + name: 'Springboot', + color: 'bg-[#98E279]', + borderColor: 'border-[#68BC44]' + }, + { + name: 'AWS', + color: 'bg-[#FF9F59]', + borderColor: 'border-[#EF6703]' + }, + { + name: 'Docker', + color: 'bg-[#B8E0FF]', + borderColor: 'border-[#2596ED]' + }, + { + name: 'Node-RED', + color: 'bg-[#FF7474]', + borderColor: 'border-[#8F0101]' + }, + { + name: 'Maven', + color: 'bg-[#FF6B80]', + borderColor: 'border-[#CA1A33]' + }, + { + name: 'Saas', + color: 'bg-[#6AB8EC]', + borderColor: 'border-[#2275AD]' + }, + { + name: 'Kubernetes-native', + color: 'bg-[#D7C7F2]', + borderColor: 'border-[#A387D2]' + }, + { + name: 'Scala', + color: 'bg-[#D7C7F2]', + borderColor: 'border-[#A387D2]' + }, + { + name: 'Azure', + color: 'bg-[#4B93FF]', + borderColor: 'border-[#015ADF]' + }, + { + name: 'Jenkins', + color: 'bg-[#D7C7F2]', + borderColor: 'border-[#A387D2]' + }, + { + name: 'Flask', + color: 'bg-[#D7C7F2]', + borderColor: 'border-[#A387D2]' + }, + { + name: 'Nest Js', + color: 'bg-[#E1224E]', + borderColor: 'border-[#B9012b]' + } +]; -module.exports = {languagesColor, technologiesColor} \ No newline at end of file +module.exports = { languagesColor, technologiesColor }; diff --git a/scripts/tools/tools-object.js b/scripts/tools/tools-object.js index e5a30334f7d3..57b152e49570 100644 --- a/scripts/tools/tools-object.js +++ b/scripts/tools/tools-object.js @@ -1,32 +1,32 @@ -const schema = require("./tools-schema.json"); -const axios = require('axios') -const Ajv = require("ajv") -const addFormats = require("ajv-formats") -const Fuse = require("fuse.js") -const { categoryList } = require("./categorylist") -const ajv = new Ajv() -addFormats(ajv, ["uri"]) -const validate = ajv.compile(schema) -const { convertToJson } = require('../utils'); +const axios = require('axios'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); +const Fuse = require('fuse.js'); +const schema = require('./tools-schema.json'); +const { categoryList } = require('./categorylist'); +const ajv = new Ajv(); +addFormats(ajv, ['uri']); +const validate = ajv.compile(schema); +const { convertToJson } = require('../utils'); // Config options set for the Fuse object const options = { includeScore: true, shouldSort: true, threshold: 0.4, - keys: ["tag"] -} + keys: ['tag'] +}; -const fuse = new Fuse(categoryList, options) +const fuse = new Fuse(categoryList, options); -// using the contents of each toolFile (extracted from Github), along with Github URL -// (repositoryUrl) of the tool, it's repository description (repoDescription) and -// isAsyncAPIrepo boolean variable to define whether the tool repository is under -// AsyncAPI organization or not, to create a JSON tool object as required in the frontend +// using the contents of each toolFile (extracted from Github), along with Github URL +// (repositoryUrl) of the tool, it's repository description (repoDescription) and +// isAsyncAPIrepo boolean variable to define whether the tool repository is under +// AsyncAPI organization or not, to create a JSON tool object as required in the frontend // side to show ToolCard. -const createToolObject = async (toolFile, repositoryUrl='', repoDescription='', isAsyncAPIrepo='') => { - let resultantObject = { +const createToolObject = async (toolFile, repositoryUrl = '', repoDescription = '', isAsyncAPIrepo = '') => { + const resultantObject = { title: toolFile.title, description: toolFile?.description ? toolFile.description : repoDescription, links: { @@ -42,57 +42,62 @@ const createToolObject = async (toolFile, repositoryUrl='', repoDescription='', return resultantObject; }; -// Each result obtained from the Github API call will be tested and verified +// Each result obtained from the Github API call will be tested and verified // using the defined JSON schema, categorising each tool inside their defined categories -// and creating a JSON tool object in which all the tools are listed in defined +// and creating a JSON tool object in which all the tools are listed in defined // categories order, which is then updated in `automated-tools.json` file. async function convertTools(data) { - let finalToolsObject = {}; + const finalToolsObject = {}; const dataArray = data.items; // initialising finalToolsObject with all categories inside it with proper elements in each category - for (var index in categoryList) { + for (const index in categoryList) { finalToolsObject[categoryList[index].name] = { description: categoryList[index].description, toolsList: [] }; } - for (let tool of dataArray) { + for (const tool of dataArray) { try { if (tool.name.startsWith('.asyncapi-tool')) { // extracting the reference id of the repository which will be used to extract the path of the .asyncapi-tool file in the Tools repository // ex: for a url = "https://api.github.com/repositories/351453552/contents/.asyncapi-tool?ref=61855e7365a881e98c2fe667a658a0005753d873" // the text (id) present after '=' gives us a reference id for the repo - let reference_id = tool.url.split("=")[1]; - let download_url = `https://raw.githubusercontent.com/${tool.repository.full_name}/${reference_id}/${tool.path}`; + const reference_id = tool.url.split('=')[1]; + const download_url = `https://raw.githubusercontent.com/${tool.repository.full_name}/${reference_id}/${tool.path}`; const { data: toolFileContent } = await axios.get(download_url); - //some stuff can be YAML - const jsonToolFileContent = await convertToJson(toolFileContent) + // some stuff can be YAML + const jsonToolFileContent = await convertToJson(toolFileContent); - //validating against JSON Schema for tools file - const isValid = await validate(jsonToolFileContent) + // validating against JSON Schema for tools file + const isValid = await validate(jsonToolFileContent); if (isValid) { - let repositoryUrl = tool.repository.html_url; - let repoDescription = tool.repository.description; - let isAsyncAPIrepo = tool.repository.owner.login === "asyncapi"; - let toolObject = await createToolObject(jsonToolFileContent, repositoryUrl, repoDescription, isAsyncAPIrepo); + const repositoryUrl = tool.repository.html_url; + const repoDescription = tool.repository.description; + const isAsyncAPIrepo = tool.repository.owner.login === 'asyncapi'; + const toolObject = await createToolObject( + jsonToolFileContent, + repositoryUrl, + repoDescription, + isAsyncAPIrepo + ); // Tool Object is appended to each category array according to Fuse search for categories inside Tool Object jsonToolFileContent.filters.categories.forEach(async (category) => { const categorySearch = await fuse.search(category); if (categorySearch.length) { - let searchedCategoryName = categorySearch[0].item.name - if (!finalToolsObject[searchedCategoryName].toolsList.find((element => element === toolObject))) + const searchedCategoryName = categorySearch[0].item.name; + if (!finalToolsObject[searchedCategoryName].toolsList.find((element) => element === toolObject)) finalToolsObject[searchedCategoryName].toolsList.push(toolObject); } else { // if Tool object has a category, not defined in our categorylist, then this provides a `other` category to the tool. - if (!finalToolsObject['Others'].toolsList.find((element => element === toolObject))) - finalToolsObject['Others'].toolsList.push(toolObject); + if (!finalToolsObject.Others.toolsList.find((element) => element === toolObject)) + finalToolsObject.Others.toolsList.push(toolObject); } }); } else { @@ -103,11 +108,11 @@ async function convertTools(data) { } } } catch (err) { - console.error(err) + console.error(err); throw err; } } return finalToolsObject; } -module.exports = {convertTools, createToolObject} \ No newline at end of file +module.exports = { convertTools, createToolObject }; diff --git a/scripts/tools/tools-schema.json b/scripts/tools/tools-schema.json index e11968a1b2e1..74bcb3d783b4 100644 --- a/scripts/tools/tools-schema.json +++ b/scripts/tools/tools-schema.json @@ -1,220 +1,209 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema for AsyncAPI tool discovery file.", - "type": "object", - "additionalProperties": false, - "required": [ - "title", - "filters" - ], - "properties": { - "title": { - "type": "string", - "description": "Human-readable name of the tool that will be visible to people in the list of tools.", - "examples": [ - "AsyncAPI Generator", - "Cupid" - ] + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSON Schema for AsyncAPI tool discovery file.", + "type": "object", + "additionalProperties": false, + "required": ["title", "filters"], + "properties": { + "title": { + "type": "string", + "description": "Human-readable name of the tool that will be visible to people in the list of tools.", + "examples": ["AsyncAPI Generator", "Cupid"] + }, + "description": { + "type": "string", + "description": "By default scripts read description of repository there project is stored. You can override this behaviour by providing custom description." + }, + "links": { + "type": "object", + "additionalProperties": false, + "properties": { + "websiteUrl": { + "type": "string", + "description": "You can provide URL to the website where your project hosts some demo or project landing page.", + "format": "uri" }, - "description": { - "type": "string", - "description": "By default scripts read description of repository there project is stored. You can override this behaviour by providing custom description." + "docsUrl": { + "type": "string", + "description": "You can provide URL to project documentation in case you have more than just a readme file.", + "format": "uri" }, - "links": { - "type": "object", - "additionalProperties": false, - "properties": { - "websiteUrl": { - "type": "string", - "description": "You can provide URL to the website where your project hosts some demo or project landing page.", - "format": "uri" - }, - "docsUrl": { - "type": "string", - "description": "You can provide URL to project documentation in case you have more than just a readme file.", - "format": "uri" + "repoUrl": { + "type": "string", + "description": "You can provide URL to project codebase in case you have more than one tool present inside single repository.", + "format": "uri" + } + } + }, + "filters": { + "type": "object", + "additionalProperties": false, + "required": ["categories"], + "properties": { + "language": { + "description": "The language referred to is the runtime language selected by the user, not the generator or library language. For example, the Generator written in JavaScript generates Python code from the JavaScript template and the result of generation is a Python app, so the language for Generator is specified as Python. But for the Bundler library, users need to know if it can be integrated into their TypeScript codebase, so its language is specified as TypeScript. If some language in the schema's enum is omitted, it can be added through a pull request to the AsyncAPI website repository.", + "anyOf": [ + { + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "Go", + "Java", + "JavaScript", + "HTML", + "C/C++", + "C#", + "Python", + "TypeScript", + "Kotlin", + "Scala", + "Markdown", + "YAML", + "R", + "Ruby", + "Rust", + "Shell", + "Groovy" + ] }, - "repoUrl": { - "type": "string", - "description": "You can provide URL to project codebase in case you have more than one tool present inside single repository.", - "format": "uri" + { + "type": "string" } - } - }, - "filters": { - "type": "object", - "additionalProperties": false, - "required": [ - "categories" - ], - "properties": { - "language": { - "description": "The language referred to is the runtime language selected by the user, not the generator or library language. For example, the Generator written in JavaScript generates Python code from the JavaScript template and the result of generation is a Python app, so the language for Generator is specified as Python. But for the Bundler library, users need to know if it can be integrated into their TypeScript codebase, so its language is specified as TypeScript. If some language in the schema's enum is omitted, it can be added through a pull request to the AsyncAPI website repository.", - "anyOf": [ - { - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "Go", - "Java", - "JavaScript", - "HTML", - "C/C++", - "C#", - "Python", - "TypeScript", - "Kotlin", - "Scala", - "Markdown", - "YAML", - "R", - "Ruby", - "Rust", - "Shell", - "Groovy" - ] - }, - { - "type": "string" - } - ] - }, - { - "type": "array", - "items": { - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "Go", - "Java", - "JavaScript", - "HTML", - "C/C++", - "C#", - "Python", - "TypeScript", - "Kotlin", - "Scala", - "Markdown", - "YAML", - "R", - "Ruby", - "Rust", - "Shell", - "Groovy" - ] - }, - { - "type": "string" - } - ] - } - } - ] - }, - "technology": { - "type": "array", - "description": "Provide a list of different technologies used in the tool. Put details useful for tool user and tool contributor.", - "items": { - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "Node js", - "Hermes", - "React JS", - ".NET", - "ASP.NET", - "Springboot", - "AWS", - "Docker", - "Node-red", - "Maven", - "Saas", - "Kubernetes-native", - "Scala", - "Azure", - "Jenkins", - "Flask" - ] - }, - { - "type": "string" - } - ] - }, - "examples": [ - "Express.js", - "Kafka" - ] - }, - "categories": { - "type": "array", - "description": "Categories are used to group tools by different use case, like documentation or code generation. If have a list of fixed categories. If you use different one that your tool lands under \"other\" category. Feel free to add your category through a pull request to AsyncAPI website repository.", - "items": { - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "api", - "code-first", - "code-generator", - "converter", - "directory", - "documentation-generator", - "editor", - "ui-component", - "dsl", - "framework", - "github-action", - "mocking-and-testing", - "validator", - "compare-tool", - "other", - "cli", - "bundler", - "ide-extension" - ] - }, - { - "type": "string" - } - ] - }, - "minItems": 1, - "examples": [ - "api", - "code-first", - "code-generator", - "converter", - "directory", - "documentation-generator", - "editor", - "ui-component", - "dsl", - "framework", - "github-action", - "mocking-and-testing", - "validator", - "compare-tool", - "other", - "cli", - "bundler", - "ide-extension" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "Go", + "Java", + "JavaScript", + "HTML", + "C/C++", + "C#", + "Python", + "TypeScript", + "Kotlin", + "Scala", + "Markdown", + "YAML", + "R", + "Ruby", + "Rust", + "Shell", + "Groovy" ] - }, - "hasCommercial": { - "type": "boolean", - "description": "Indicate if your tool is open source or commercial offering, like SAAS for example", - "default": false - } + }, + { + "type": "string" + } + ] + } } + ] + }, + "technology": { + "type": "array", + "description": "Provide a list of different technologies used in the tool. Put details useful for tool user and tool contributor.", + "items": { + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "Node js", + "Hermes", + "React JS", + ".NET", + "ASP.NET", + "Springboot", + "AWS", + "Docker", + "Node-red", + "Maven", + "Saas", + "Kubernetes-native", + "Scala", + "Azure", + "Jenkins", + "Flask" + ] + }, + { + "type": "string" + } + ] + }, + "examples": ["Express.js", "Kafka"] + }, + "categories": { + "type": "array", + "description": "Categories are used to group tools by different use case, like documentation or code generation. If have a list of fixed categories. If you use different one that your tool lands under \"other\" category. Feel free to add your category through a pull request to AsyncAPI website repository.", + "items": { + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "api", + "code-first", + "code-generator", + "converter", + "directory", + "documentation-generator", + "editor", + "ui-component", + "dsl", + "framework", + "github-action", + "mocking-and-testing", + "validator", + "compare-tool", + "other", + "cli", + "bundler", + "ide-extension" + ] + }, + { + "type": "string" + } + ] + }, + "minItems": 1, + "examples": [ + "api", + "code-first", + "code-generator", + "converter", + "directory", + "documentation-generator", + "editor", + "ui-component", + "dsl", + "framework", + "github-action", + "mocking-and-testing", + "validator", + "compare-tool", + "other", + "cli", + "bundler", + "ide-extension" + ] + }, + "hasCommercial": { + "type": "boolean", + "description": "Indicate if your tool is open source or commercial offering, like SAAS for example", + "default": false } + } } -} \ No newline at end of file + } +} diff --git a/scripts/utils.js b/scripts/utils.js index c740ae91eaef..fa893a6de22e 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,26 +1,26 @@ const yaml = require('yaml'); function convertToJson(contentYAMLorJSON) { - // Axios handles conversion to JSON by default, if data returned from the server allows it - // So if returned content is not a string (not YAML), we just return JSON back - if (typeof contentYAMLorJSON !== "string") { - return contentYAMLorJSON; - } + // Axios handles conversion to JSON by default, if data returned from the server allows it + // So if returned content is not a string (not YAML), we just return JSON back + if (typeof contentYAMLorJSON !== 'string') { + return contentYAMLorJSON; + } - // Check if the content is valid JSON before attempting to parse as YAML + // Check if the content is valid JSON before attempting to parse as YAML + try { + const jsonContent = JSON.parse(contentYAMLorJSON); + return jsonContent; + } catch (jsonError) { + // If it's not valid JSON, try parsing it as YAML try { - const jsonContent = JSON.parse(contentYAMLorJSON); - return jsonContent; - } catch (jsonError) { - // If it's not valid JSON, try parsing it as YAML - try { - const yamlContent = yaml.parse(contentYAMLorJSON); - return yamlContent; - } catch (yamlError) { - // If parsing as YAML also fails, throw an error - throw new Error(`Invalid content format:\nJSON Parse Error: ${jsonError}\nYAML Parse Error: ${yamlError}`); - } + const yamlContent = yaml.parse(contentYAMLorJSON); + return yamlContent; + } catch (yamlError) { + // If parsing as YAML also fails, throw an error + throw new Error(`Invalid content format:\nJSON Parse Error: ${jsonError}\nYAML Parse Error: ${yamlError}`); } + } } module.exports = { convertToJson }; diff --git a/scripts/utils/readAndWriteJson.js b/scripts/utils/readAndWriteJson.js index 3c7f05d2308b..e3e2a6f2b6e3 100644 --- a/scripts/utils/readAndWriteJson.js +++ b/scripts/utils/readAndWriteJson.js @@ -1,28 +1,30 @@ -const { promises: { readFile, writeFile } } = require('fs'); -const { convertToJson } = require("../utils"); +const { + promises: { readFile, writeFile } +} = require('fs'); +const { convertToJson } = require('../utils'); module.exports = async function writeJSON(readPath, writePath) { - let readContent; - let jsonContent; + let readContent; + let jsonContent; - // Attempt to read the file - try { - readContent = await readFile(readPath, 'utf-8'); - } catch (err) { - throw new Error(`Error while reading file\nError: ${err}`); - } + // Attempt to read the file + try { + readContent = await readFile(readPath, 'utf-8'); + } catch (err) { + throw new Error(`Error while reading file\nError: ${err}`); + } - // Attempt to convert content to JSON - try { - jsonContent = convertToJson(readContent); - } catch (err) { - throw new Error(`Error while conversion\nError: ${err}`); - } + // Attempt to convert content to JSON + try { + jsonContent = convertToJson(readContent); + } catch (err) { + throw new Error(`Error while conversion\nError: ${err}`); + } - // Attempt to write the JSON content to file - try { - await writeFile(writePath, JSON.stringify(jsonContent)); - } catch (err) { - throw new Error(`Error while writing file\nError: ${err}`); - } -}; \ No newline at end of file + // Attempt to write the JSON content to file + try { + await writeFile(writePath, JSON.stringify(jsonContent)); + } catch (err) { + throw new Error(`Error while writing file\nError: ${err}`); + } +};