diff --git a/.github/preview.mjs b/.github/preview.mjs new file mode 100644 index 00000000..ce41ba63 --- /dev/null +++ b/.github/preview.mjs @@ -0,0 +1,78 @@ +//Imports + import fs from "fs/promises" + import paths from "path" + import url from "url" + import setup from "../source/app/metrics/setup.mjs" + +//Paths + const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") + const __templates = paths.join(paths.join(__metrics, "source/templates/")) + const __node_modules = paths.join(paths.join(__metrics, "node_modules")) + const __web = paths.join(paths.join(__metrics, "source/app/web/statics")) + const __web_about = paths.join(paths.join(__web, "about")) + + const __preview = paths.join(paths.join(__web, "preview")) + const __preview_js = paths.join(__preview, ".js") + const __preview_css = paths.join(__preview, ".css") + const __preview_templates = paths.join(__preview, ".templates") + const __preview_templates_ = paths.join(__preview, ".templates_") + const __preview_about = paths.join(__preview, "about/.statics") + +//Extract from web server + const {conf, Templates} = await setup({nosettings:true, log:false}) + const templates = Object.entries(Templates).map(([name]) => ({name, enabled:true})) + const metadata = Object.fromEntries(Object.entries(conf.metadata.plugins) + .map(([key, value]) => [key, Object.fromEntries(Object.entries(value).filter(([key]) => ["name", "icon", "categorie", "web", "supports"].includes(key)))]) + .map(([key, value]) => [key, key === "core" ? {...value, web:Object.fromEntries(Object.entries(value.web).filter(([key]) => /^config[.]/.test(key)).map(([key, value]) => [key.replace(/^config[.]/, ""), value]))} : value])) + + +//Directories + await fs.mkdir(__preview, {recursive:true}) + await fs.mkdir(__preview_js, {recursive:true}) + await fs.mkdir(__preview_css, {recursive:true}) + await fs.mkdir(__preview_templates, {recursive:true}) + await fs.mkdir(__preview_templates_, {recursive:true}) + await fs.mkdir(__preview_about, {recursive:true}) + +//Web + fs.copyFile(paths.join(__web, "index.html"), paths.join(__preview, "index.html")) + fs.copyFile(paths.join(__web, "favicon.png"), paths.join(__preview, ".favicon.png")) + fs.copyFile(paths.join(__web, "opengraph.png"), paths.join(__preview, ".opengraph.png")) +//Plugins and templates + fs.writeFile(paths.join(__preview, ".plugins"), JSON.stringify(Object.entries(metadata).filter(([_name, {categorie}]) => categorie !== "core").map(([name]) => ({name, enabled:false})))) + fs.writeFile(paths.join(__preview, ".plugins.base"), JSON.stringify(conf.settings.plugins.base.parts)) + fs.writeFile(paths.join(__preview, ".plugins.metadata"), JSON.stringify(metadata)) + fs.writeFile(paths.join(__preview, ".templates__"), JSON.stringify(templates)) + for (const template in conf.templates) { + fs.writeFile(paths.join(__preview_templates_, template), JSON.stringify(conf.templates[template])) + const __partials = paths.join(__templates, template, "partials") + const __preview_partials = paths.join(__preview_templates, template, "partials") + await fs.mkdir(__preview_partials, {recursive:true}) + for (const file of await fs.readdir(__partials)) + fs.copyFile(paths.join(__partials, file), paths.join(__preview_partials, file)) + } +//Styles + fs.copyFile(paths.join(__web, "style.css"), paths.join(__preview_css, "style.css")) + fs.copyFile(paths.join(__web, "style.vars.css"), paths.join(__preview_css, "style.vars.css")) + fs.copyFile(paths.join(__node_modules, "prismjs/themes/prism-tomorrow.css"), paths.join(__preview_css, "style.prism.css")) +//Scripts + fs.writeFile(paths.join(__preview_js, "app.js"), `${await fs.readFile(paths.join(__web, "app.js"))}`) + fs.writeFile(paths.join(__preview_js, "app.placeholder.js"), `${await fs.readFile(paths.join(__web, "app.placeholder.js"))}`) + fs.copyFile(paths.join(__node_modules, "ejs/ejs.min.js"), paths.join(__preview_js, "ejs.min.js")) + fs.copyFile(paths.join(__node_modules, "faker/dist/faker.min.js"), paths.join(__preview_js, "faker.min.js")) + fs.copyFile(paths.join(__node_modules, "axios/dist/axios.min.js"), paths.join(__preview_js, "axios.min.js")) + fs.copyFile(paths.join(__node_modules, "axios/dist/axios.min.map"), paths.join(__preview_js, "axios.min.map")) + fs.copyFile(paths.join(__node_modules, "vue/dist/vue.min.js"), paths.join(__preview_js, "vue.min.js")) + fs.copyFile(paths.join(__node_modules, "vue-prism-component/dist/vue-prism-component.min.js"), paths.join(__preview_js, "vue.prism.min.js")) + fs.copyFile(paths.join(__node_modules, "vue-prism-component/dist/vue-prism-component.min.js.map"), paths.join(__preview_js, "vue-prism-component.min.js.map")) + fs.copyFile(paths.join(__node_modules, "prismjs/prism.js"), paths.join(__preview_js, "prism.min.js")) + fs.copyFile(paths.join(__node_modules, "prismjs/components/prism-yaml.min.js"), paths.join(__preview_js, "prism.yaml.min.js")) + fs.copyFile(paths.join(__node_modules, "prismjs/components/prism-markdown.min.js"), paths.join(__preview_js, "prism.markdown.min.js")) +//Meta + fs.writeFile(paths.join(__preview, ".version"), JSON.stringify(`${conf.package.version}-preview`)) + fs.writeFile(paths.join(__preview, ".hosted"), JSON.stringify({by:"metrics", link:"https://github.com/lowlighter/metrics"})) +//About + fs.copyFile(paths.join(__web, "about", "index.html"), paths.join(__preview, "about", "index.html")) + for (const file of await fs.readdir(__web_about)) + if (file !== ".statics") + fs.copyFile(paths.join(__web_about, file), paths.join(__preview_about, file)) diff --git a/.gitignore b/.gitignore index d94d98b7..de396e8f 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,7 @@ settings.json # Community templates source/templates/.community -source/templates/@* \ No newline at end of file +source/templates/@* + +# Preview +source/app/web/statics/preview \ No newline at end of file diff --git a/package.json b/package.json index d5a2fdec..7587cdfd 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test": "jest --runInBand", "index": "node .github/index.mjs", "quickstart": "node .github/quickstart/index.mjs", + "preview": "node .github/preview.mjs", "linter": "eslint source/**/*.mjs", "dev": "nodemon source/app/web/index.mjs -e mjs,css,ejs,json", "postinstall": "node node_modules/puppeteer/install.js" diff --git a/source/app/web/statics/about/index.html b/source/app/web/statics/about/index.html index b95e7dd6..ce88bb2c 100644 --- a/source/app/web/statics/about/index.html +++ b/source/app/web/statics/about/index.html @@ -28,7 +28,7 @@ Search a GitHub user - {{ requests.remaining }} GitHub requests remaining + {{ requests.remaining }} GitHub requests remaining Send feedback on GitHub discussions!
@@ -58,6 +58,10 @@ An error occurred while generating metrics :(
{{ error.message }}
+ + Metrics insights are rendered by metrics.lecoq.io in preview mode.
+ Any backend editions won't be reflected but client-side rendering can still be tested. +
- {{ requests.remaining }} GitHub requests remaining + {{ requests.remaining }} GitHub requests remaining + + Metrics are rendered by metrics.lecoq.io in preview mode. + Any backend editions won't be reflected but client-side rendering can still be tested. +
Metrics cannot be generated because the following plugins are not available on this web instance: {{ unusable.join(", ") }}
diff --git a/source/app/web/statics/style.css b/source/app/web/statics/style.css index c1429a39..9d0240a5 100644 --- a/source/app/web/statics/style.css +++ b/source/app/web/statics/style.css @@ -271,6 +271,10 @@ border-radius: 6px; } + .error-text { + color: var(--color-alert-error-text); + } + /* Warning */ .warning { padding: .5rem .75rem; diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..5280cde0 --- /dev/null +++ b/vercel.json @@ -0,0 +1,15 @@ +{ + "rewrites":[ + {"source": "/.templates", "destination": "/.templates__"}, + {"source": "/.templates/:template", "destination": "/.templates_/:template"}, + {"source": "/:login([-\\w]+)", "destination": "https://metrics.lecoq.io/:login"}, + {"source": "/:login([-\\w]+)/:repository([-\\w]+)", "destination": "https://metrics.lecoq.io/:login/:repository"}, + {"source": "/about/query/:login", "destination": "https://metrics.lecoq.io/about/query/:login"}, + {"source": "/.uncache", "destination": "https://metrics.lecoq.io/.uncache"}, + {"source": "/.requests", "destination": "https://metrics.lecoq.io/.requests"} + ], + "github":{ + "silent": true, + "autoJobCancelation": true + } +} \ No newline at end of file