diff --git a/scripts/build-compatibility-table.js b/scripts/build-compatibility-table.js index 45070e72..8c418830 100644 --- a/scripts/build-compatibility-table.js +++ b/scripts/build-compatibility-table.js @@ -24,36 +24,55 @@ const WIKI_COMPATIBILITY_TABLE_PATH = path.resolve( COMPATIBILITY_TABLE_OUTPUT_FILENAME, ); +/** + * @typedef {Object} CompatibilityItem + * @property {string} adg + * @property {string} abp + * @property {string} ubo + */ + +/** + * @typedef {Object} CompatibilityData + * @property {CompatibilityItem[]} scriptlets + * @property {CompatibilityItem[]} redirects + */ + /** * Returns data for compatibility table + * + * @returns {CompatibilityData} input compatibility data from json */ const getTableData = () => { const rawData = fs.readFileSync(COMPATIBILITY_TABLE_DATA_PATH); return JSON.parse(rawData); }; -// FIXME: update jsdoc - id arg is missed /** * Returns markdown row of compatibility table - * @param {{ - * adg: string, - * ubo: string, - * abp: string - * }} item { an } + * + * @param {'scriptlets'|'redirects'} id + * @param {CompatibilityItem} item params object + * @param {string} item.adg AdGuard name + * @param {string} item.abp Adblock Plus name + * @param {string} item.ubo uBlock name + * + * @returns {string} markdown table row */ -const getRow = (id, item) => { +const getRow = (id, { adg, abp, ubo }) => { let adgCell = ''; - if (item.adg) { - adgCell = item.adg.includes(REMOVED_MARKER) - ? item.adg - : `[${item.adg}](${WIKI_DIR_PATH}/about-${id}.md#${item.adg})`; + if (adg) { + adgCell = adg.includes(REMOVED_MARKER) + ? adg + : `[${adg}](${WIKI_DIR_PATH}/about-${id}.md#${adg})`; } - return `| ${adgCell} | ${item.ubo || ''} | ${item.abp || ''} |${EOL}`; + return `| ${adgCell} | ${ubo || ''} | ${abp || ''} |${EOL}`; }; /** * Generates table header + * + * @returns {string} */ const getTableHeader = () => { let res = `| AdGuard | uBO | Adblock Plus |${EOL}`; @@ -61,10 +80,13 @@ const getTableHeader = () => { return res; }; -// FIXME: add missed args, add @returns, fix Array type /** * Builds markdown string of scriptlets/redirect compatibility table - * @param {Array} data array of scriptlets/redirect names + * @param {string} title title for scriptlets or redirects + * @param {CompatibilityItem[]} data array of scriptlets or redirects compatibility data items + * @param {'scriptlets'|'redirects'} id + * + * @returns {string} scriptlets or redirects compatibility table */ const buildTable = (title, data = [], id = '') => { // title @@ -82,7 +104,7 @@ const buildTable = (title, data = [], id = '') => { /** * Saves tables to compatibility table * - * @param {string[]} args + * @param {string[]} args */ const saveTables = (...args) => { const res = args.join(`${EOL}${EOL}`); @@ -109,6 +131,7 @@ const buildCompatibilityTable = () => { saveTables(scriptletsTable, redirectsTable); }; +// TODO: check whether the execution is needed here buildCompatibilityTable(); module.exports = { diff --git a/scripts/build-corelibs.js b/scripts/build-corelibs.js index d335ef0f..95b6b637 100644 --- a/scripts/build-corelibs.js +++ b/scripts/build-corelibs.js @@ -4,7 +4,7 @@ import { minify } from 'terser'; import * as scriptletList from '../src/scriptlets/scriptlets-list'; import { version } from '../package.json'; -import { writeFile } from './helpers'; +import { writeFileAsync } from './helpers'; const buildCorelibsJson = async () => { const { getScriptletFunction } = require('../tmp/scriptlets-func'); // eslint-disable-line import/no-unresolved,global-require @@ -37,6 +37,6 @@ const buildCorelibsJson = async () => { export const buildScriptletsForCorelibs = async () => { console.log('Start building corelibs...'); const json = await buildCorelibsJson(); - await writeFile(path.resolve(__dirname, '../dist/scriptlets.corelibs.json'), json, 'utf8'); + await writeFileAsync(path.resolve(__dirname, '../dist/scriptlets.corelibs.json'), json, 'utf8'); console.log('Corelibs built'); }; diff --git a/scripts/build-docs.js b/scripts/build-docs.js index ad54b90f..bf007dcb 100644 --- a/scripts/build-docs.js +++ b/scripts/build-docs.js @@ -56,13 +56,25 @@ const manageDataFromFiles = () => { return { scriptletsData, redirectsData }; }; -// FIXME: check type of `data` /** - * Generates markdown list and describing text - * @param {Object} data array of filtered objects - scriptlets or redirects + * @typedef { import('./helpers').DescribingCommentData } DescribingCommentData */ -const getMarkdownData = (data) => { - const output = data.reduce((acc, { + +/** + * @typedef {Object} MarkdownData + * @property {string} list table of content + * @property {string} body main content which + */ + +/** + * Generates markdown list and describing text. + * + * @param {DescribingCommentData[]} dataItems array of comment data objects + * + * @returns {MarkdownData} + */ +const getMarkdownData = (dataItems) => { + const output = dataItems.reduce((acc, { name, type, description, @@ -89,6 +101,8 @@ ${description}${EOL} /** * Generates markdown list and describing text for static redirect resources + * + * @returns {MarkdownData} */ const getMarkdownDataForStaticRedirects = () => { const staticRedirects = fs.readFileSync(path.resolve(__dirname, staticRedirectsPath), { encoding: 'utf8' }); @@ -118,6 +132,8 @@ ${description} /** * Generates markdown list and describing text for blocking redirect resources, i.e click2load.html + * + * @returns {MarkdownData} */ const getMarkdownDataForBlockingRedirects = () => { const BLOCKING_REDIRECTS_SOURCE_SUB_DIR = 'blocking-redirects'; diff --git a/scripts/build-funcs.js b/scripts/build-funcs.js index 57683c13..44a8a942 100644 --- a/scripts/build-funcs.js +++ b/scripts/build-funcs.js @@ -18,7 +18,7 @@ import path from 'path'; import { minify } from 'terser'; import { addCall, attachDependencies } from '../src/helpers/injector'; -import { writeFile } from './helpers'; +import { writeFileAsync } from './helpers'; /** * Method creates string for file with scriptlets functions, @@ -67,7 +67,7 @@ export const buildScriptletsFunc = async () => { format: { beautify: true }, }); - await writeFile(path.resolve(__dirname, '../tmp/scriptlets-func.js'), beautifiedScriptletFunctions.code); + await writeFileAsync(path.resolve(__dirname, '../tmp/scriptlets-func.js'), beautifiedScriptletFunctions.code); console.log('Scriptlets functions built successfully'); }; diff --git a/scripts/build-redirects-map.js b/scripts/build-redirects-map.js index cea37bf9..52dada05 100644 --- a/scripts/build-redirects-map.js +++ b/scripts/build-redirects-map.js @@ -3,7 +3,7 @@ import { minify } from 'terser'; import path from 'path'; import { getPreparedRedirects } from './build-redirects'; -import { writeFile } from './helpers'; +import { writeFileAsync } from './helpers'; const createRedirectsMap = (redirects) => { const map = {}; @@ -41,7 +41,7 @@ export const buildRedirectsMap = async () => { format: { beautify: true }, }); - await writeFile(path.resolve(__dirname, '../tmp/redirects-map.js'), beautifiedScriptletFunctions.code); + await writeFileAsync(path.resolve(__dirname, '../tmp/redirects-map.js'), beautifiedScriptletFunctions.code); console.log('Finish building redirect map'); }; diff --git a/scripts/build-redirects.js b/scripts/build-redirects.js index 5f826ece..d9758e1a 100644 --- a/scripts/build-redirects.js +++ b/scripts/build-redirects.js @@ -14,7 +14,7 @@ import { minify } from 'terser'; import * as redirectsList from '../src/redirects/redirects-list'; import { version } from '../package.json'; import { rollupStandard } from './rollup-runners'; -import { writeFile, getDataFromFiles } from './helpers'; +import { writeFileAsync, getDataFromFiles } from './helpers'; import { redirectsFilenames, REDIRECTS_SRC_RELATIVE_DIR_PATH } from './constants'; const FILE_NAME = 'redirects.yml'; @@ -202,7 +202,7 @@ export const getPreparedRedirects = async (options) => { const buildJsRedirectFiles = async (redirectsData) => { const saveRedirectData = async (redirect) => { const redirectPath = `${REDIRECT_FILES_PATH}/${redirect.file}`; - await writeFile(redirectPath, redirect.content); + await writeFileAsync(redirectPath, redirect.content); }; await Promise.all(Object.values(redirectsData) @@ -230,9 +230,9 @@ const buildStaticRedirectFiles = async (redirectsData) => { // replace them all because base64 isn't supposed to have them contentToWrite = content.replace(/(\r\n|\n|\r|\s)/gm, ''); const buff = Buffer.from(contentToWrite, 'base64'); - await writeFile(redirectPath, buff); + await writeFileAsync(redirectPath, buff); } else { - await writeFile(redirectPath, contentToWrite); + await writeFileAsync(redirectPath, contentToWrite); } }; @@ -251,7 +251,7 @@ const buildRedirectsYamlFile = async (mergedRedirects) => { // add version and title to the top yamlRedirects = `${banner}${yamlRedirects}`; - await writeFile(RESULT_PATH, yamlRedirects); + await writeFileAsync(RESULT_PATH, yamlRedirects); }; export const prebuildRedirects = async () => { @@ -372,7 +372,7 @@ export const buildRedirectsForCorelibs = async () => { try { const jsonString = JSON.stringify(base64Redirects, null, 4); - await writeFile(CORELIBS_RESULT_PATH, jsonString); + await writeFileAsync(CORELIBS_RESULT_PATH, jsonString); } catch (e) { // eslint-disable-next-line no-console console.log(`Couldn't save to ${CORELIBS_RESULT_PATH}, because of: ${e.message}`); diff --git a/scripts/build-txt.js b/scripts/build-txt.js index 8797c944..aa449bb3 100644 --- a/scripts/build-txt.js +++ b/scripts/build-txt.js @@ -1,5 +1,5 @@ import path from 'path'; -import { writeFile } from './helpers'; +import { writeFileAsync } from './helpers'; import { version } from '../package.json'; const PATH = '../dist'; @@ -7,5 +7,5 @@ const FILENAME = 'build.txt'; export const buildTxt = async () => { const content = `version=${version}`; - await writeFile(path.resolve(__dirname, PATH, FILENAME), content); + await writeFileAsync(path.resolve(__dirname, PATH, FILENAME), content); }; diff --git a/scripts/helpers.js b/scripts/helpers.js index 566319f9..ac4fe03b 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -3,7 +3,7 @@ const fs = require('fs-extra'); const dox = require('dox'); // FIXME: rename to 'writeFileAsync' and add jsdoc comment -const writeFile = async (filePath, content) => { +const writeFileAsync = async (filePath, content) => { const dirname = path.dirname(filePath); await fs.ensureDir(dirname); @@ -21,70 +21,101 @@ const getFilesList = (relativeDirPath) => { }; /** - * Gets required comments from file. + * @typedef {Object} CommentTag + * @property {string} type tag name + * @property {string} string text following the tag + */ + +/** + * Returns parsed tags data which we use to describe the sources: + * - `@scriptlet`/`@redirect` to describe the type and name of source; + * - `@description` actual description for scriptlet or redirect. + * required comments from file. * In one file might be comments describing scriptlet and redirect as well. - * @param {string} srcPath path to file + * + * @param {string} filePath absolute path to file + * + * @returns {CommentTag[]} */ -const getComments = (srcPath) => { - const srcCode = fs.readFileSync(srcPath, { encoding: 'utf8' }); - const parsedCommentsFromFile = dox.parseComments(srcCode); - // TODO: simplify - // because only `tags` are needed - const describingComment = Object.values(parsedCommentsFromFile) - .filter((comment) => { - const [base] = comment.tags; - const isNeededComment = (base - && (base.type === 'scriptlet' || base.type === 'redirect')); - return isNeededComment; +const getDescribingCommentTags = (filePath) => { + const fileContent = fs.readFileSync(filePath, { encoding: 'utf8' }); + const parsedFileComments = dox.parseComments(fileContent); + const describingComment = parsedFileComments + // get rid of not needed comments data + .filter(({ tags }) => { + // '@scriptlet', '@redirect', and 'description' + // are parser by dox.parseComments() as `tags` + if (tags.length === 0) { + return false; + } + const [base] = tags; + return base?.type === 'scriptlet' + || base?.type === 'redirect'; + // get `tags` from needed parser comment }); if (describingComment.length === 0) { - throw new Error(`No description in ${srcPath}. + throw new Error(`No description in ${filePath}. Please add one OR edit the list of NON_SCRIPTLETS_FILES / NON_REDIRECTS_FILES.`); } - return describingComment; + if (describingComment.length > 1) { + throw new Error(`File should have one description comment: ${filePath}.`); + } + + // eventually only one comment data item should left + return describingComment[0].tags; }; -// FIXME: add type for requiredComments after it is simplified in `getComments` -// FIXME: add type for object which is returned by prepareCommentsData /** - * Convert parsed comments to objects + * @typedef {Object} DescribingCommentData * - * @param {Object} requiredComments parsed comments for one file + * Collected data from jsdoc-type comment for every scriptlet or redirect. + * + * @property {string} type parsed instance tag: + * 'scriptlet' for '@scriptlet', 'redirect' for '@redirect' + * @property {string} name name of instance which goes after the instance tag + * @property {string} description description, goes after `@description` tag + * @property {string} source relative path to source of scriptlet or redirect from wiki/about page + */ + +/** + * Converts parsed comment to data object. + * + * @param {CommentTag[]} commentTags parsed tags from describing comment * @param {string} source relative path to file * - * @returns {Object} + * @returns {DescribingCommentData} */ -const prepareCommentsData = (requiredComments, source) => { - return requiredComments.map((el) => { - const [base, sup] = el.tags; - return { - type: base.type, - name: base.string, - description: sup.string, - source, - }; - }); +const prepareCommentsData = (commentTags, source) => { + const [base, sup] = commentTags; + return { + type: base.type, + name: base.string, + description: sup.string, + source, + }; }; /** * Gets data objects which describe every required comment in one directory * @param {string[]} filesList list of files in directory * @param {string} relativeDirPath relative path to directory + * + * @returns {DescribingCommentData} */ const getDataFromFiles = (filesList, relativeDirPath) => { const pathToDir = path.resolve(__dirname, relativeDirPath); return filesList.map((file) => { const pathToFile = path.resolve(pathToDir, file); - const requiredComments = getComments(pathToFile); + const requiredCommentTags = getDescribingCommentTags(pathToFile); - return prepareCommentsData(requiredComments, `${relativeDirPath}/${file}`); + return prepareCommentsData(requiredCommentTags, `${relativeDirPath}/${file}`); }); }; module.exports = { - writeFile, + writeFileAsync, getFilesList, getDataFromFiles, };