Add linter and minor bug fixes (#107)

This commit is contained in:
Simon Lecoq
2021-02-05 23:45:48 +01:00
committed by GitHub
parent 61e2f6e1a1
commit 882a93dea5
74 changed files with 1544 additions and 712 deletions

View File

@@ -1,8 +1,8 @@
//Imports
import util from "util"
import ejs from "ejs"
import SVGO from "svgo"
import * as utils from "./utils.mjs"
import ejs from "ejs"
import util from "util"
import SVGO from "svgo"
//Setup
export default async function metrics({login, q, dflags = []}, {graphql, rest, plugins, conf, die = false, verify = false, convert = null}, {Plugins, Templates}) {
@@ -23,7 +23,7 @@
//Initialization
const pending = []
const queries = conf.queries
const {queries} = conf
const data = {animated:true, base:{}, config:{}, errors:[], plugins:{}, computed:{}}
const imports = {plugins:Plugins, templates:Templates, metadata:conf.metadata, ...utils}
@@ -47,7 +47,7 @@
if (errors.length) {
console.warn(`metrics/compute/${login} > ${errors.length} errors !`)
if (die)
throw new Error(`An error occured during rendering, dying`)
throw new Error("An error occured during rendering, dying")
else
console.warn(util.inspect(errors, {depth:Infinity, maxStringLength:256}))
}

View File

@@ -1,10 +1,10 @@
//Imports
import fs from "fs"
import path from "path"
import yaml from "js-yaml"
import url from "url"
import yaml from "js-yaml"
/** Metadata descriptor parser */
/**Metadata descriptor parser */
export default async function metadata({log = true} = {}) {
//Paths
const __metrics = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "../../..")
@@ -16,7 +16,7 @@
//Load plugins metadata
let Plugins = {}
logger(`metrics/metadata > loading plugins metadata`)
logger("metrics/metadata > loading plugins metadata")
for (const name of await fs.promises.readdir(__plugins)) {
if (!(await fs.promises.lstat(path.join(__plugins, name))).isDirectory())
continue
@@ -29,7 +29,7 @@
//Load templates metadata
let Templates = {}
logger(`metrics/metadata > loading templates metadata`)
logger("metrics/metadata > loading templates metadata")
for (const name of await fs.promises.readdir(__templates)) {
if (!(await fs.promises.lstat(path.join(__templates, name))).isDirectory())
continue
@@ -46,8 +46,8 @@
return {plugins:Plugins, templates:Templates}
}
/** Metadata extractor for templates */
metadata.plugin = async function ({__plugins, name, logger}) {
/**Metadata extractor for templates */
metadata.plugin = async function({__plugins, name, logger}) {
try {
//Load meta descriptor
const raw = `${await fs.promises.readFile(path.join(__plugins, name, "metadata.yml"), "utf-8")}`
@@ -55,7 +55,7 @@
//Inputs parser
{
meta.inputs = function ({data:{user = null} = {}, q, account}, defaults = {}) {
meta.inputs = function({data:{user = null} = {}, q, account}, defaults = {}) {
//Support check
if (!account)
logger(`metrics/inputs > account type not set for plugin ${name}!`)
@@ -142,7 +142,7 @@
return value
}
}
})(defaults[key] ?? defaulted)
})(defaults[key] ?? defaulted),
]))
logger(`metrics/inputs > ${name} > ${JSON.stringify(result)}`)
return result
@@ -154,7 +154,7 @@
{
//Extract comments
const comments = {}
raw.split(/(\r?\n){2,}/m)
raw.split(/(?:\r?\n){2,}/m)
.map(x => x.trim()).filter(x => x)
.map(x => x.split("\n").map(y => y.trim()).join("\n"))
.map(x => {
@@ -168,12 +168,12 @@
key,
{
comment:comments[key] ?? "",
descriptor:yaml.dump({[key]:Object.fromEntries(Object.entries(value).filter(([key]) => ["description", "default", "required"].includes(key)))}, {quotingType:'"', noCompatMode:true})
}
descriptor:yaml.dump({[key]:Object.fromEntries(Object.entries(value).filter(([key]) => ["description", "default", "required"].includes(key)))}, {quotingType:'"', noCompatMode:true}),
},
]))
//Action inputs
meta.inputs.action = function ({core}) {
meta.inputs.action = function({core}) {
//Build query object from inputs
const q = {}
for (const key of Object.keys(inputs)) {
@@ -207,15 +207,14 @@
case "string":{
if (Array.isArray(values))
return {text, type:"select", values}
else
return {text, type:"text", placeholder:example ?? defaulted, defaulted}
return {text, type:"text", placeholder:example ?? defaulted, defaulted}
}
case "json":
return {text, type:"text", placeholder:example ?? defaulted, defaulted}
default:
return null
}
})()
})(),
]).filter(([key, value]) => (value)&&(key !== name)))
}
@@ -241,8 +240,8 @@
}
}
/** Metadata extractor for templates */
metadata.template = async function ({__templates, name, plugins, logger}) {
/**Metadata extractor for templates */
metadata.template = async function({__templates, name, plugins, logger}) {
try {
//Load meta descriptor
const raw = `${await fs.promises.readFile(path.join(__templates, name, "README.md"), "utf-8")}`
@@ -262,7 +261,7 @@
return {
name:raw.match(/^### (?<name>[\s\S]+?)\n/)?.groups?.name?.trim(),
readme:{
demo:raw.match(/(?<demo><table>[\s\S]*?<[/]table>)/)?.groups?.demo?.replace(/<[/]?(?:table|tr)>/g, "")?.trim() ?? (name === "community" ? `<td align="center">See <a href="/source/templates/community/README.md">documentation</a> 🌍</td>` : "<td></td>"),
demo:raw.match(/(?<demo><table>[\s\S]*?<[/]table>)/)?.groups?.demo?.replace(/<[/]?(?:table|tr)>/g, "")?.trim() ?? (name === "community" ? "<td align=\"center\">See <a href=\"/source/templates/community/README.md\">documentation</a> 🌍</td>" : "<td></td>"),
compatibility:{...compatibility, base:true},
},
}
@@ -273,10 +272,10 @@
}
}
/** Metadata converters */
/**Metadata converters */
metadata.to = {
query(key, {name = null} = {}) {
key = key.replace(/^plugin_/, "").replace(/_/g, ".")
return name ? key.replace(new RegExp(`^(${name}.)`, "g"), "") : key
}
}
},
}

View File

@@ -1,17 +1,17 @@
//Imports
import fs from "fs"
import metadata from "./metadata.mjs"
import path from "path"
import processes from "child_process"
import util from "util"
import url from "url"
import processes from "child_process"
import metadata from "./metadata.mjs"
//Templates and plugins
const Templates = {}
const Plugins = {}
/** Setup */
export default async function ({log = true, nosettings = false, community = {}} = {}) {
/**Setup */
export default async function({log = true, nosettings = false, community = {}} = {}) {
//Paths
const __metrics = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "../../..")
@@ -24,7 +24,7 @@
//Init
const logger = log ? console.debug : () => null
logger(`metrics/setup > setup`)
logger("metrics/setup > setup")
const conf = {
templates:{},
queries:{},
@@ -34,21 +34,21 @@
statics:__statics,
templates:__templates,
node_modules:__modules,
}
},
}
//Load settings
logger(`metrics/setup > load settings.json`)
logger("metrics/setup > load settings.json")
if (fs.existsSync(__settings)) {
if (nosettings)
logger(`metrics/setup > load settings.json > skipped because no settings is enabled`)
logger("metrics/setup > load settings.json > skipped because no settings is enabled")
else {
conf.settings = JSON.parse(`${await fs.promises.readFile(__settings)}`)
logger(`metrics/setup > load settings.json > success`)
logger("metrics/setup > load settings.json > success")
}
}
else
logger(`metrics/setup > load settings.json > (missing)`)
logger("metrics/setup > load settings.json > (missing)")
if (!conf.settings.templates)
conf.settings.templates = {default:"classic", enabled:[]}
if (!conf.settings.plugins)
@@ -59,13 +59,13 @@
logger(util.inspect(conf.settings, {depth:Infinity, maxStringLength:256}))
//Load package settings
logger(`metrics/setup > load package.json`)
logger("metrics/setup > load package.json")
conf.package = JSON.parse(`${await fs.promises.readFile(__package)}`)
logger(`metrics/setup > load package.json > success`)
logger("metrics/setup > load package.json > success")
//Load community templates
if ((typeof conf.settings.community.templates === "string")&&(conf.settings.community.templates.length)) {
logger(`metrics/setup > parsing community templates list`)
logger("metrics/setup > parsing community templates list")
conf.settings.community.templates = [...new Set([...decodeURIComponent(conf.settings.community.templates).split(",").map(v => v.trim().toLocaleLowerCase()).filter(v => v)])]
}
if ((Array.isArray(conf.settings.community.templates))&&(conf.settings.community.templates.length)) {
@@ -77,7 +77,7 @@
try {
//Parse community template
logger(`metrics/setup > load community template ${template}`)
const {repo, branch, name, trust = false} = template.match(/^(?<repo>[\s\S]+?)@(?<branch>[\s\S]+?):(?<name>[\s\S]+?)(?<trust>[+]trust)?$/)?.groups
const {repo, branch, name, trust = false} = template.match(/^(?<repo>[\s\S]+?)@(?<branch>[\s\S]+?):(?<name>[\s\S]+?)(?<trust>[+]trust)?$/)?.groups ?? null
const command = `git clone --single-branch --branch ${branch} https://github.com/${repo}.git ${path.join(__templates, ".community")}`
logger(`metrics/setup > run ${command}`)
//Clone remote repository
@@ -99,14 +99,15 @@
logger(`metrics/setup > clean ${repo}@${branch}`)
await fs.promises.rmdir(path.join(__templates, ".community"), {recursive:true})
logger(`metrics/setup > loaded community template ${name}`)
} catch (error) {
}
catch (error) {
logger(`metrics/setup > failed to load community template ${template}`)
logger(error)
}
}
}
else
logger(`metrics/setup > no community templates to install`)
logger("metrics/setup > no community templates to install")
//Load templates
for (const name of await fs.promises.readdir(__templates)) {
@@ -122,7 +123,7 @@
conf.templates[name] = {image, style, fonts, partials, views:[directory]}
//Cache templates scripts
Templates[name] = await (async () => {
Templates[name] = await (async() => {
const template = path.join(directory, "template.mjs")
const fallback = path.join(__templates, "classic", "template.mjs")
return (await import(url.pathToFileURL(fs.existsSync(template) ? template : fallback).href)).default
@@ -137,7 +138,7 @@
const partials = JSON.parse(`${fs.readFileSync(path.join(directory, "partials/_.json"))}`)
logger(`metrics/setup > reload template [${name}] > success`)
return {image, style, fonts, partials, views:[directory]}
}
},
})
}
}
@@ -156,11 +157,12 @@
const __queries = path.join(directory, "queries")
if (fs.existsSync(__queries)) {
//Alias for default query
const queries = conf.queries[name] = function () {
const queries = function() {
if (!queries[name])
throw new ReferenceError(`Default query for ${name} undefined`)
return queries[name](...arguments)
}
conf.queries[name] = queries
//Load queries
for (const file of await fs.promises.readdir(__queries)) {
//Cache queries
@@ -176,7 +178,7 @@
const raw = `${fs.readFileSync(path.join(__queries, file))}`
logger(`metrics/setup > reload query [${name}/${query}] > success`)
return raw
}
},
})
}
}
@@ -194,10 +196,12 @@
conf.metadata = await metadata({log})
//Set no token property
Object.defineProperty(conf.settings, "notoken", {get() { return conf.settings.token === "NOT_NEEDED" }})
Object.defineProperty(conf.settings, "notoken", {get() {
return conf.settings.token === "NOT_NEEDED"
}})
//Conf
logger(`metrics/setup > setup > success`)
logger("metrics/setup > setup > success")
return {Templates, Plugins, conf}
}
}

View File

@@ -8,48 +8,50 @@
import axios from "axios"
import puppeteer from "puppeteer"
import imgb64 from "image-to-base64"
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
dayjs.extend(utc);
import dayjs from "dayjs"
import utc from "dayjs/plugin/utc.js"
dayjs.extend(utc)
export {fs, os, paths, url, util, processes, axios, puppeteer, imgb64, dayjs};
export {fs, os, paths, url, util, processes, axios, puppeteer, imgb64, dayjs}
/** Returns module __dirname */
/**Returns module __dirname */
export function __module(module) {
return paths.join(paths.dirname(url.fileURLToPath(module)))
}
/** Plural formatter */
/**Plural formatter */
export function s(value, end = "") {
return value !== 1 ? {y:"ies", "":"s"}[end] : end
}
/** Formatter */
/**Formatter */
export function format(n, {sign = false} = {}) {
for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}]) {
if (n/v >= 1)
return `${(sign)&&(n > 0) ? "+" : ""}${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
}
return `${(sign)&&(n > 0) ? "+" : ""}${n}`
}
/** Bytes formatter */
/**Bytes formatter */
export function bytes(n) {
for (const {u, v} of [{u:"E", v:10**18}, {u:"P", v:10**15}, {u:"T", v:10**12}, {u:"G", v:10**9}, {u:"M", v:10**6}, {u:"k", v:10**3}])
for (const {u, v} of [{u:"E", v:10**18}, {u:"P", v:10**15}, {u:"T", v:10**12}, {u:"G", v:10**9}, {u:"M", v:10**6}, {u:"k", v:10**3}]) {
if (n/v >= 1)
return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")} ${u}B`
}
return `${n} byte${n > 1 ? "s" : ""}`
}
format.bytes = bytes
/** Percentage formatter */
/**Percentage formatter */
export function percentage(n, {rescale = true} = {}) {
return `${(n*(rescale ? 100 : 1)).toFixed(2)
.replace(/(?<=[.])([1-9]*)(0+)$/, (m, a, b) => a)
.replace(/(?<=[.])(?<decimal>[1-9]*)0+$/, "$<decimal>")
.replace(/[.]$/, "")}%`
}
format.percentage = percentage
/** Text ellipsis formatter */
/**Text ellipsis formatter */
export function ellipsis(text, {length = 20} = {}) {
text = `${text}`
if (text.length < length)
@@ -58,7 +60,7 @@
}
format.ellipsis = ellipsis
/** Array shuffler */
/**Array shuffler */
export function shuffle(array) {
for (let i = array.length-1; i > 0; i--) {
const j = Math.floor(Math.random()*(i+1))
@@ -67,7 +69,7 @@
return array
}
/** Escape html */
/**Escape html */
export function htmlescape(string, u = {"&":true, "<":true, ">":true, '"':true, "'":true}) {
return string
.replace(/&(?!(?:amp|lt|gt|quot|apos);)/g, u["&"] ? "&amp;" : "&")
@@ -77,18 +79,19 @@
.replace(/'/g, u["'"] ? "&apos;" : "'")
}
/** Expand url */
/**Expand url */
export async function urlexpand(url) {
try {
return (await axios.get(url)).request.res.responseUrl
} catch {
}
catch {
return url
}
}
/** Run command */
/**Run command */
export async function run(command, options) {
return await new Promise((solve, reject) => {
return new Promise((solve, reject) => {
console.debug(`metrics/command > ${command}`)
const child = processes.exec(command, options)
let [stdout, stderr] = ["", ""]
@@ -101,7 +104,7 @@
})
}
/** Render svg */
/**Render svg */
export async function svgresize(svg, {paddings = ["6%"], convert} = {}) {
//Instantiate browser if needed
if (!svgresize.browser) {
@@ -144,7 +147,7 @@
return {resized, mime}
}
/** Wait */
/**Wait */
export async function wait(seconds) {
await new Promise(solve => setTimeout(solve, seconds*1000))
}
await new Promise(solve => setTimeout(solve, seconds*1000)) //eslint-disable-line no-promise-executor-return
}