From ef466593c54f1c9adf4440b030057e0e6d845ec0 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Fri, 3 Mar 2023 23:39:33 +0100 Subject: [PATCH] Initial commit --- .github/workflows/ci.yml | 57 + .gitignore | 8 + LICENSE.md | 21 + README.md | 42 + build/posthtml-include.js | 41 + build/res/empty_index.html | 9 + build/rollup-posthtml-template.js | 31 + compose-db.js | 456 +++++++ package-lock.json | 1161 +++++++++++++++++ package.json | 27 + rollup.config.js | 116 ++ .../index/components/IndexDescription.js | 83 ++ src/paths/index/components/IndexHeader.js | 116 ++ src/paths/index/components/files/FileItem.js | 121 ++ src/paths/index/components/files/FileList.js | 76 ++ src/paths/index/entry.js | 110 ++ src/paths/index/template.html | 16 + src/shared/components/PageContent.js | 28 + src/shared/partials/body_content.html | 0 src/shared/partials/head_content.html | 9 + src/shared/scripts/global.js | 124 ++ src/shared/styles/global.css | 53 + src/shared/styles/normalize.css | 349 +++++ src/static/favicon.png | Bin 0 -> 1929 bytes src/static/icons/file.svg | 1 + src/static/icons/folder.svg | 1 + 26 files changed, 3056 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 build/posthtml-include.js create mode 100644 build/res/empty_index.html create mode 100644 build/rollup-posthtml-template.js create mode 100644 compose-db.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 rollup.config.js create mode 100644 src/paths/index/components/IndexDescription.js create mode 100644 src/paths/index/components/IndexHeader.js create mode 100644 src/paths/index/components/files/FileItem.js create mode 100644 src/paths/index/components/files/FileList.js create mode 100644 src/paths/index/entry.js create mode 100644 src/paths/index/template.html create mode 100644 src/shared/components/PageContent.js create mode 100644 src/shared/partials/body_content.html create mode 100644 src/shared/partials/head_content.html create mode 100644 src/shared/scripts/global.js create mode 100644 src/shared/styles/global.css create mode 100644 src/shared/styles/normalize.css create mode 100644 src/static/favicon.png create mode 100644 src/static/icons/file.svg create mode 100644 src/static/icons/folder.svg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..723e153 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: Continuous integration + +on: + push: + branches: [ master ] + schedule: + # Run every 15 minutes of every hour, starting at 5 minutes (5m, 20m, 35m, 50m). + # The slight offset is there to try and avoid the high load times. + - cron: '5/15 * * * *' + +# Make sure jobs cannot overlap (e.g. one from push and one from schedule). +concurrency: + group: pages-ci + cancel-in-progress: true + +jobs: + build: + name: Build and deploy to GitHub Pages + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build the static content using npm + run: npm run build + + - name: Fetch pull request data + run: npm run compose-db + env: + GRAPHQL_TOKEN: ${{ secrets.GRAPHQL_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: web-static + path: out + + - name: Deploy to GitHub Pages 🚀 + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: out + # Configure the commit author. + git-config-name: 'Godot Organization' + git-config-email: '<>' + # Don't keep the history. + single-commit: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f40a54f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Project folders. +node_modules/ +out/ +logs/ + +# Development environments. +.idea/ +.vscode/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cf60308 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright © 2023-present Godot Engine contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d400b4a --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Godot PRs by File + +This project is provided for Godot Engine contributors to quickly find open +PRs editing a specific file or folder. With the amount of work that goes into +Godot it becomes tricky to keep in mind every PR that touches every file, and +identify conflicts or duplicates. This project aims to help with that. + +Live website: https://godotengine.github.io/godot-prs-by-file/ + +## Contributing + +This project is written in JavaScript and is built using Node.JS. HTML and CSS are +used for the presentation. The end result of the build process is completely static +and can be server from any web server, no Node.JS required. + +Front-end is designed in a reactive manner using industry standard Web Components +(powered by `lit-element`). This provides native browser support, and results in a +small overhead from the build process. + +To build the project locally you need to have Node.JS installed (12.x and newer +should work just fine). + +This project uses GitHub's GraphQL API. To fetch live data you need to generate +a [personal OAuth token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). +You can supply your token to the scripts using the `GRAPHQL_TOKEN` environment +variable. Note, that if you don't have member access to the organization, you +may not be able to access all the information used when generating the database. + +1. Clone or download the project. +2. From the project root run `npm install` or `yarn` to install dependencies. +3. Run `npm run build` or `yarn run build` to build the pages. +4. Run `npm run compose-db` or `yarn run compose-db` to fetch the data from GitHub. +5. Serve the `out/` folder with your method of choice (e.g. using Python 3: + `python -m http.server 8080 -d ./out`). + +`rollup` is used for browser packing of scripts and copying of static assets. The +data fetching script is plain JavaScript with `node-fetch` used to polyfill +`fetch()`-like API. + +## License + +This project is provided under the [MIT License](LICENSE.md). diff --git a/build/posthtml-include.js b/build/posthtml-include.js new file mode 100644 index 0000000..7225c9e --- /dev/null +++ b/build/posthtml-include.js @@ -0,0 +1,41 @@ +import fs from "fs"; +import path from "path"; + +import parser from "posthtml-parser"; + +export default function(options) { + options = options || {}; + options.root = options.root || './'; + options.encoding = options.encoding || 'utf-8'; + + return function posthtmlInclude(tree) { + tree.match({ tag: 'include' }, function(node) { + if (!node.attrs.src) { + return { + tag: false, + content: null + }; + } + + const src = path.resolve(options.root, node.attrs.src); + const source = fs.readFileSync(src, options.encoding); + const subtree = parser(source); + subtree.match = tree.match; + const content = source.indexOf('include') !== -1? posthtmlInclude(subtree): subtree; + + if (tree.messages) { + tree.messages.push({ + type: "dependency", + file: src + }); + } + + return { + tag: false, + content: content + }; + }); + + return tree; + }; +}; \ No newline at end of file diff --git a/build/res/empty_index.html b/build/res/empty_index.html new file mode 100644 index 0000000..fd678f8 --- /dev/null +++ b/build/res/empty_index.html @@ -0,0 +1,9 @@ + + + + + Nothing to see here + + + + \ No newline at end of file diff --git a/build/rollup-posthtml-template.js b/build/rollup-posthtml-template.js new file mode 100644 index 0000000..f9cefc5 --- /dev/null +++ b/build/rollup-posthtml-template.js @@ -0,0 +1,31 @@ +import { promises as fs } from 'fs'; + +import posthtml from 'posthtml'; +import include from './posthtml-include'; +import { green } from 'colorette'; + +export default function(options = {}) { + return { + name: 'posthtml', + buildEnd: async () => { + if (!options.src || !options.dest) { + return; + } + const html = await fs.readFile(options.src, { encoding: 'utf-8' }); + + const plugins = [ + include({ + root: './src' + }) + ]; + const result = await posthtml(plugins).process(html); + + try { + await fs.unlink(options.dest); + } catch (exc) { } + + await fs.writeFile(options.dest, result.html, { encoding: 'utf-8' }); + console.log(green(`written html template ${options.dest}`)) + } + }; +} \ No newline at end of file diff --git a/compose-db.js b/compose-db.js new file mode 100644 index 0000000..a45d69b --- /dev/null +++ b/compose-db.js @@ -0,0 +1,456 @@ +const fs = require('fs').promises; +const fsConstants = require('fs').constants; +const path = require('path'); +const fetch = require('node-fetch'); + +const PULLS_PER_PAGE = 100; +let page_count = 1; +let last_cursor = ""; + +const ExitCodes = { + "RequestFailure": 1, + "ParseFailure": 2, +}; + +const API_REST_PATH = `https://api.github.com/repos/godotengine/godot`; +const API_REPOSITORY_ID = `owner:"godotengine" name:"godot"`; +const API_RATE_LIMIT = ` + rateLimit { + limit + cost + remaining + resetAt + } +`; + +class DataFetcher { + async _logResponse(data, name) { + try { + try { + await fs.access("logs", fsConstants.R_OK | fsConstants.W_OK); + } catch (err) { + await fs.mkdir("logs"); + } + + await fs.writeFile(`logs/${name}.json`, JSON.stringify(data, null, 4), {encoding: "utf-8"}); + } catch (err) { + console.error("Error saving log file: " + err); + } + } + + _handleResponseErrors(res) { + console.warn(` Failed to get pull requests for '${API_REPOSITORY_ID}'; server responded with ${res.status} ${res.statusText}`); + const retry_header = res.headers.get("Retry-After"); + if (retry_header) { + console.log(` Retry after: ${retry_header}`); + } + } + + _handleDataErrors(data) { + if (typeof data["errors"] === "undefined") { + return; + } + + console.warn(` Server handled the request, but there were errors:`); + data.errors.forEach((item) => { + console.log(` [${item.type}] ${item.message}`); + }); + } + + async fetchGithub(query) { + const init = {}; + init.method = "POST"; + init.headers = {}; + init.headers["Content-Type"] = "application/json"; + if (process.env.GRAPHQL_TOKEN) { + init.headers["Authorization"] = `token ${process.env.GRAPHQL_TOKEN}`; + } else if (process.env.GITHUB_TOKEN) { + init.headers["Authorization"] = `token ${process.env.GITHUB_TOKEN}`; + } + + init.body = JSON.stringify({ + query, + }); + + return await fetch("https://api.github.com/graphql", init); + } + + async fetchGithubRest(query) { + const init = {}; + init.method = "GET"; + init.headers = {}; + init.headers["Content-Type"] = "application/json"; + if (process.env.GRAPHQL_TOKEN) { + init.headers["Authorization"] = `token ${process.env.GRAPHQL_TOKEN}`; + } else if (process.env.GITHUB_TOKEN) { + init.headers["Authorization"] = `token ${process.env.GITHUB_TOKEN}`; + } + + return await fetch(`${API_REST_PATH}${query}`, init); + } + + async checkRates() { + try { + const query = ` + query { + ${API_RATE_LIMIT} + } + `; + + const res = await this.fetchGithub(query); + if (res.status !== 200) { + this._handleResponseErrors(res); + process.exitCode = ExitCodes.RequestFailure; + return; + } + + const data = await res.json(); + await this._logResponse(data, "_rate_limit"); + this._handleDataErrors(data); + + const rate_limit = data.data["rateLimit"]; + console.log(` [$${rate_limit.cost}] Available API calls: ${rate_limit.remaining}/${rate_limit.limit}; resets at ${rate_limit.resetAt}`); + } catch (err) { + console.error(" Error checking the API rate limits: " + err); + process.exitCode = ExitCodes.RequestFailure; + return; + } + } + + async fetchPulls(page) { + try { + let after_cursor = ""; + let after_text = "initial"; + if (last_cursor !== "") { + after_cursor = `after: "${last_cursor}"`; + after_text = after_cursor; + } + + const query = ` + query { + ${API_RATE_LIMIT} + repository(${API_REPOSITORY_ID}) { + pullRequests(first:${PULLS_PER_PAGE} ${after_cursor} states: OPEN) { + totalCount + pageInfo { + endCursor + hasNextPage + } + edges { + node { + id + number + url + title + state + isDraft + + createdAt + updatedAt + + baseRef { + name + } + + author { + login + avatarUrl + url + + ... on User { + id + } + } + + milestone { + id + title + url + } + + labels (first: 100) { + edges { + node { + id + name + color + } + } + } + + files (first: 100) { + edges { + node { + path + changeType + } + } + } + } + } + } + } + } + `; + + let page_text = page; + if (page_count > 1) { + page_text = `${page}/${page_count}`; + } + console.log(` Requesting page ${page_text} of pull request data (${after_text}).`); + + const res = await this.fetchGithub(query); + if (res.status !== 200) { + this._handleResponseErrors(res); + process.exitCode = ExitCodes.RequestFailure; + return []; + } + + const data = await res.json(); + await this._logResponse(data, `data_page_${page}`); + this._handleDataErrors(data); + + const rate_limit = data.data["rateLimit"]; + const repository = data.data["repository"]; + const pulls_data = mapNodes(repository.pullRequests); + + console.log(` [$${rate_limit.cost}] Retrieved ${pulls_data.length} pull requests; processing...`); + + last_cursor = repository.pullRequests.pageInfo.endCursor; + page_count = Math.ceil(repository.pullRequests.totalCount / PULLS_PER_PAGE); + + return pulls_data; + } catch (err) { + console.error(" Error fetching pull request data: " + err); + process.exitCode = ExitCodes.RequestFailure; + return []; + } + } + + async fetchFiles(branch) { + try { + const query = `/git/trees/${branch}?recursive=1`; + + const res = await this.fetchGithubRest(query); + if (res.status !== 200) { + this._handleResponseErrors(res); + process.exitCode = ExitCodes.RequestFailure; + return []; + } + + const data = await res.json(); + await this._logResponse(data, `data_files_${branch}`); + this._handleDataErrors(data); + + const files_data = data.tree; + + console.log(` [$0] Retrieved ${files_data.length} file system entries in '${branch}'; processing...`); + + return files_data; + } catch (err) { + console.error(" Error fetching pull request data: " + err); + process.exitCode = ExitCodes.RequestFailure; + return []; + } + } +} + +class DataProcessor { + constructor() { + this.authors = {}; + this.pulls = []; + this.files = []; + } + + _explainFileType(type) { + switch(type) { + case "blob": + return "file"; + case "tree": + return "folder"; + default: + return "unknown"; + } + } + + processPulls(pullsRaw) { + try { + pullsRaw.forEach((item) => { + // Compile basic information about a PR. + let pr = { + "id": item.id, + "public_id": item.number, + "url": item.url, + "diff_url": `${item.url}.diff`, + "patch_url": `${item.url}.patch`, + + "title": item.title, + "state": item.state, + "is_draft": item.isDraft, + "authored_by": null, + "created_at": item.createdAt, + "updated_at": item.updatedAt, + + "target_branch": item.baseRef.name, + + "labels": [], + "milestone": null, + + "files": [], + }; + + // Compose and link author information. + const author = { + "id": "", + "user": "ghost", + "avatar": "https://avatars.githubusercontent.com/u/10137?v=4", + "url": "https://github.com/ghost", + "pull_count": 0, + }; + if (item.author != null) { + author["id"] = item.author.id; + author["user"] = item.author.login; + author["avatar"] = item.author.avatarUrl; + author["url"] = item.author.url; + } + pr.authored_by = author.id; + + // Store the author if they haven't been stored. + if (typeof this.authors[author.id] === "undefined") { + this.authors[author.id] = author; + } + this.authors[author.id].pull_count++; + + // Add the milestone, if available. + if (item.milestone) { + pr.milestone = { + "id": item.milestone.id, + "title": item.milestone.title, + "url": item.milestone.url, + }; + } + + // Add labels, if available. + let labels = mapNodes(item.labels); + labels.forEach((labelItem) => { + pr.labels.push({ + "id": labelItem.id, + "name": labelItem.name, + "color": "#" + labelItem.color, + }); + }); + pr.labels.sort((a, b) => { + if (a.name > b.name) return 1; + if (a.name < b.name) return -1; + return 0; + }); + + // Add changed files. + let files = mapNodes(item.files); + files.forEach((fileItem) => { + pr.files.push({ + "path": fileItem.path, + "changeType": fileItem.changeType, + }); + }); + pr.files.sort((a, b) => { + if (a.name > b.name) return 1; + if (a.name < b.name) return -1; + return 0; + }); + + this.pulls.push(pr); + }); + } catch (err) { + console.error(" Error parsing pull request data: " + err); + process.exitCode = ExitCodes.ParseFailure; + } + } + + processFiles(filesRaw) { + try { + filesRaw.forEach((item) => { + let file = { + "type": this._explainFileType(item.type), + "name": item.path.split("/").pop(), + "path": item.path, + "parent": "", + }; + + let parentPath = item.path.split("/"); + parentPath.pop(); + if (parentPath.length > 0) { + file.parent = parentPath.join("/"); + } + + this.files.push(file); + }); + } catch (err) { + console.error(" Error parsing repository file system: " + err); + process.exitCode = ExitCodes.ParseFailure; + } + } +} + +function mapNodes(object) { + return object.edges.map((item) => item["node"]) +} + +async function main() { + // Internal utility methods. + const checkForExit = () => { + if (process.exitCode > 0) { + process.exit(); + } + } + const delay = async (msec) => { + return new Promise(resolve => setTimeout(resolve, msec)); + } + + console.log("[*] Building local pull request database."); + const dataFetcher = new DataFetcher(); + const dataProcessor = new DataProcessor(); + + console.log("[*] Checking the rate limits before."); + await dataFetcher.checkRates(); + checkForExit(); + + // console.log("[*] Fetching pull request data from GitHub."); + // // Pages are starting with 1 for better presentation. + // let page = 1; + // while (page <= page_count) { + // const pullsRaw = await dataFetcher.fetchPulls(page); + // dataProcessor.processPulls(pullsRaw); + // checkForExit(); + // page++; + + // // Wait for a bit before proceeding to avoid hitting the secondary rate limit in GitHub API. + // // See https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits. + // await delay(1500); + // } + + console.log("[*] Fetching repository file system from GitHub."); + const filesRaw = await dataFetcher.fetchFiles("master"); + dataProcessor.processFiles(filesRaw); + checkForExit(); + + console.log("[*] Checking the rate limits after.") + await dataFetcher.checkRates(); + checkForExit(); + + console.log("[*] Finalizing database.") + const output = { + "generated_at": Date.now(), + "authors": dataProcessor.authors, + "pulls": dataProcessor.pulls, + "files": dataProcessor.files, + }; + try { + console.log("[*] Storing database to file.") + await fs.writeFile("out/data.json", JSON.stringify(output), {encoding: "utf-8"}); + } catch (err) { + console.error("Error saving database file: " + err); + } +} + +main(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b44599b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1161 @@ +{ + "name": "godot-prs-by-file", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "godot-prs-by-file", + "version": "1.0.0", + "dependencies": { + "@babel/core": "^7.6.4", + "@babel/plugin-proposal-class-properties": "^7.5.5", + "@babel/plugin-proposal-decorators": "^7.6.0", + "dompurify": "^2.0.7", + "lit-element": "^2.2.1", + "marked": "^0.7.0", + "node-fetch": "^2.6.1", + "posthtml": "^0.12.0", + "rollup": "^1.24.0", + "rollup-plugin-babel": "^4.3.3", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-includepaths": "^0.2.3", + "rollup-plugin-node-resolve": "^5.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dependencies": { + "@babel/highlight": "^7.0.0" + } + }, + "node_modules/@babel/core": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.4.tgz", + "integrity": "sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==", + "dependencies": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.4", + "@babel/helpers": "^7.6.2", + "@babel/parser": "^7.6.4", + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.3", + "@babel/types": "^7.6.3", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", + "dependencies": { + "@babel/types": "^7.6.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz", + "integrity": "sha512-O1QWBko4fzGju6VoVvrZg0RROCVifcLxiApnGP3OWfWzvxRZFCoBD81K5ur5e3bVY2Vf/5rIJm8cqPKn8HUJng==", + "dependencies": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.5.5", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.5.5", + "@babel/helper-split-export-declaration": "^7.4.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz", + "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==", + "dependencies": { + "@babel/types": "^7.5.5" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz", + "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.5.5", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dependencies": { + "@babel/types": "^7.4.4" + } + }, + "node_modules/@babel/helpers": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.2.tgz", + "integrity": "sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==", + "dependencies": { + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.2", + "@babel/types": "^7.6.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz", + "integrity": "sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.5.5", + "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.6.0.tgz", + "integrity": "sha512-ZSyYw9trQI50sES6YxREXKu+4b7MAg6Qx2cvyDDYjP2Hpzd3FleOUwC9cqn1+za8d0A2ZU8SHujxFao956efUg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.6.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-decorators": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", + "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", + "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", + "dependencies": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.3", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "node_modules/@babel/types": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dependencies": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", + "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dependencies": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, + "node_modules/@types/node": { + "version": "12.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", + "integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A==" + }, + "node_modules/@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/colorette": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/dompurify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.7.tgz", + "integrity": "sha512-S3O0lk6rFJtO01ZTzMollCOGg+WAtCwS3U5E2WSDY/x/sy7q70RjEC4Dmrih5/UqzLLB9XoKJ8KqwBxaNvBu4A==" + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", + "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fastq": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", + "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", + "dependencies": { + "reusify": "^1.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dependencies": { + "isobject": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", + "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "dependencies": { + "@types/estree": "0.0.39" + } + }, + "node_modules/isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lit-element": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.2.1.tgz", + "integrity": "sha512-ipDcgQ1EpW6Va2Z6dWm79jYdimVepO5GL0eYkZrFvdr0OD/1N260Q9DH+K5HXHFrRoC7dOg+ZpED2XE0TgGdXw==", + "dependencies": { + "lit-html": "^1.0.0" + } + }, + "node_modules/lit-html": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.1.2.tgz", + "integrity": "sha512-FFlUMKHKi+qG1x1iHNZ1hrtc/zHmfYTyrSvs3/wBTvaNtpZjOZGWzU7efGYVpgp6KvWeKF6ql9/KsCq6Z/mEDA==" + }, + "node_modules/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "node_modules/magic-string": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", + "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/posthtml": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.12.0.tgz", + "integrity": "sha512-aNUEP/SfKUXAt+ghG51LC5MmafChBZeslVe/SSdfKIgLGUVRE68mrMF4V8XbH07ZifM91tCSuxY3eHIFLlecQw==", + "dependencies": { + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.4.1.tgz", + "integrity": "sha512-h7vXIQ21Ikz2w5wPClPakNP6mJeJCK6BT0GpqnQrNNABdR7/TchNlFyryL1Bz6Ww53YWCKkr6tdZuHlxY1AVdQ==", + "dependencies": { + "htmlparser2": "^3.9.2", + "object-assign": "^4.1.1" + } + }, + "node_modules/posthtml-render": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.5.tgz", + "integrity": "sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.24.0.tgz", + "integrity": "sha512-PiFETY/rPwodQ8TTC52Nz2DSCYUATznGh/ChnxActCr8rV5FIk3afBUb3uxNritQW/Jpbdn3kq1Rwh1HHYMwdQ==", + "dependencies": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + }, + "bin": { + "rollup": "dist/bin/rollup" + } + }, + "node_modules/rollup-plugin-babel": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.3.3.tgz", + "integrity": "sha512-tKzWOCmIJD/6aKNz0H1GMM+lW1q9KyFubbWzGiOG540zxPPifnEAHTZwjo0g991Y+DyOZcLqBgqOdqazYE5fkw==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-babel.", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "@babel/core": "7 || ^7.0.0-rc.2", + "rollup": ">=0.60.0 <2" + } + }, + "node_modules/rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-commonjs.", + "dependencies": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "rollup": ">=1.12.0" + } + }, + "node_modules/rollup-plugin-copy": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", + "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", + "dependencies": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-includepaths": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-includepaths/-/rollup-plugin-includepaths-0.2.3.tgz", + "integrity": "sha512-4QbSIZPDT+FL4SViEVCRi4cGCA64zQJu7u5qmCkO3ecHy+l9EQBsue15KfCpddfb6Br0q47V/v2+E2YUiqts9g==" + }, + "node_modules/rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-node-resolve.", + "dependencies": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "rollup": ">=1.11.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" + }, + "node_modules/safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fc4fc64 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "godot-prs-by-file", + "version": "1.0.0", + "description": "", + "scripts": { + "build": "rollup -c", + "compose-db": "node ./compose-db.js" + }, + "author": "Yuri Sizov ", + "private": true, + "dependencies": { + "@babel/core": "^7.6.4", + "@babel/plugin-proposal-class-properties": "^7.5.5", + "@babel/plugin-proposal-decorators": "^7.6.0", + "dompurify": "^2.0.7", + "lit-element": "^2.2.1", + "marked": "^0.7.0", + "node-fetch": "^2.6.1", + "posthtml": "^0.12.0", + "rollup": "^1.24.0", + "rollup-plugin-babel": "^4.3.3", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-includepaths": "^0.2.3", + "rollup-plugin-node-resolve": "^5.2.0" + } +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..398ebae --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,116 @@ +import { promises as fs } from 'fs'; +import path from 'path'; + +import nodeResolve from 'rollup-plugin-node-resolve'; +import includePaths from 'rollup-plugin-includepaths' +import commonjs from 'rollup-plugin-commonjs'; +import copy from 'rollup-plugin-copy'; +import posthtmlTemplate from './build/rollup-posthtml-template'; +import babel from 'rollup-plugin-babel'; + +const INPUT_ROOT = 'src'; +const INPUT_PATHS_ROOT = path.join(INPUT_ROOT, 'paths'); +const INPUT_SHARED_ROOT = path.join(INPUT_ROOT, 'shared'); +const INPUT_STATIC_ROOT = path.join(INPUT_ROOT, 'static'); + +const ENTRY_FILE_NAME = 'entry.js'; +const TEMPLATE_FILE_NAME = 'template.html'; +const GLOBAL_FILE_NAME = 'global.js'; + +const OUTPUT_ROOT = 'out'; +const OUTPUT_STYLES = path.join(OUTPUT_ROOT, 'styles'); +const OUTPUT_STYLES_SHARED = path.join(OUTPUT_STYLES, 'shared'); +const OUTPUT_SCRIPTS = path.join(OUTPUT_ROOT, 'scripts'); +const OUTPUT_SCRIPTS_SHARED = path.join(OUTPUT_SCRIPTS, 'shared'); + +const generateConfig = async () => { + let configs = []; + + getGlobalConfig(configs); + await getPathsConfigs(configs); + + return configs; +}; +const getGlobalConfig = (configs) => { + const globalScriptPath = path.join(INPUT_SHARED_ROOT, 'scripts', GLOBAL_FILE_NAME); + const outputPath = path.join(OUTPUT_SCRIPTS_SHARED, GLOBAL_FILE_NAME); + + const sharedStylesGlob = path.join(INPUT_SHARED_ROOT, 'styles/**/*.css').replace(/\\/g, '/'); // Windows path not supported by copy plugin + const staticGlob = path.join(INPUT_STATIC_ROOT, '/**/*.*').replace(/\\/g, '/'); // Windows path not supported by copy plugin + + configs.push({ + input: globalScriptPath, + output: { + name: 'global', + file: outputPath, + format: 'iife' + }, + plugins: [ + nodeResolve(), + copy({ + targets: [ + { src: sharedStylesGlob, dest: OUTPUT_STYLES_SHARED }, + { src: staticGlob, dest: OUTPUT_ROOT }, + ], + verbose: true + }) + ] + }) + +}; +const getPathsConfigs = async (configs) => { + try { + // Collect paths to process + const paths = await fs.readdir(INPUT_PATHS_ROOT); + + for (const itemPath of paths) { + const itemRoot = path.join(INPUT_PATHS_ROOT, itemPath); + const itemFiles = await fs.readdir(itemRoot); + + if (itemFiles.indexOf(ENTRY_FILE_NAME) < 0) { + throw Error(`Missing entry script for "${itemPath}" path`); + } + if (itemFiles.indexOf(TEMPLATE_FILE_NAME) < 0) { + throw Error(`Missing HTML template for "${itemPath}" path`); + } + + const entryPath = path.join(itemRoot, ENTRY_FILE_NAME); + const templatePath = path.join(itemRoot, TEMPLATE_FILE_NAME).replace(/\\/g, '/'); // Windows path not supported by copy plugin + const bundlePath = path.join(OUTPUT_ROOT, 'scripts', `${itemPath}.js`); + const htmlPath = path.join(OUTPUT_ROOT, `${itemPath}.html`); + + configs.push({ + input: entryPath, + output: { + name: itemPath, + file: bundlePath, + format: 'iife' + }, + plugins: [ + babel({ + exclude: 'node_modules/**', + plugins: [ + [ '@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true } ], + '@babel/plugin-proposal-class-properties' + ] + }), + includePaths({ + paths: [ './' ] + }), + nodeResolve(), + commonjs({ + sourceMap: false + }), + posthtmlTemplate({ + src: templatePath, + dest: htmlPath + }) + ] + }) + } + } catch (exc) { + console.error(exc); + } +}; + +export default generateConfig(); \ No newline at end of file diff --git a/src/paths/index/components/IndexDescription.js b/src/paths/index/components/IndexDescription.js new file mode 100644 index 0000000..4a00ba1 --- /dev/null +++ b/src/paths/index/components/IndexDescription.js @@ -0,0 +1,83 @@ +import { LitElement, html, css, customElement, property } from 'lit-element'; + +@customElement('gr-index-description') +export default class IndexDescription extends LitElement { + static get styles() { + return css` + /** Colors and variables **/ + :host { + } + @media (prefers-color-scheme: dark) { + :host { + } + } + + /** Component styling **/ + :host { + line-height: 22px; + } + + :host .header-description { + display: flex; + align-items: flex-end; + color: var(--dimmed-font-color); + } + + :host .header-description-column { + flex: 2; + } + :host .header-description-column.header-extra-links { + flex: 1; + text-align: right; + } + + :host .header-description a { + color: var(--link-font-color); + text-decoration: none; + } + :host .header-description a:hover { + color: var(--link-font-color-hover); + } + + :host hr { + border: none; + border-top: 1px solid var(--g-background-extra-color); + width: 30%; + } + + @media only screen and (max-width: 900px) { + :host .header-description { + padding: 0 8px; + flex-direction: column; + } + + :host .header-description-column { + width: 100%; + } + :host .header-description-column.header-extra-links { + text-align: center; + padding-top: 12px; + } + } + `; + } + + @property({ type: Date }) generated_at = null; + + render() { + return html` +
+
+ This page lists all open pull-requests (PRs) associated with the selected file + or folder. The goal here is to help contributors and maintainers identify possible + conflicts and duplication. +
+ +
+ `; + } +} diff --git a/src/paths/index/components/IndexHeader.js b/src/paths/index/components/IndexHeader.js new file mode 100644 index 0000000..f3b3669 --- /dev/null +++ b/src/paths/index/components/IndexHeader.js @@ -0,0 +1,116 @@ +import { LitElement, html, css, customElement, property } from 'lit-element'; + +@customElement('gr-index-entry') +export default class IndexHeader extends LitElement { + static get styles() { + return css` + /** Colors and variables **/ + :host { + --header-meta-color: #98a5b8; + } + @media (prefers-color-scheme: dark) { + :host { + --header-meta-color: #515c6c; + } + } + + /** Component styling **/ + :host { + } + + :host .header { + display: flex; + justify-content: space-between; + align-items: center; + } + + :host .header-metadata { + color: var(--header-meta-color); + text-align: right; + } + :host .header-metadata a { + color: var(--link-font-color); + text-decoration: none; + } + :host .header-metadata a:hover { + color: var(--link-font-color-hover); + } + + @media only screen and (max-width: 900px) { + :host .header { + flex-wrap: wrap; + text-align: center; + } + :host .header-title, + :host .header-metadata { + width: 100%; + } + :host .header-metadata { + padding-bottom: 12px; + text-align: center; + } + } + `; + } + + @property({ type: Date }) generated_at = null; + + constructor() { + super(); + + // Auto-refresh about once a minute so that the relative time of generation is always actual. + this._refreshTimeout = setTimeout(this._refresh.bind(this), 60 * 1000); + } + + _refresh() { + this.requestUpdate(); + + // Continue updating. + this._refreshTimeout = setTimeout(this._refresh.bind(this), 60 * 1000); + } + + render() { + let generatedAt = ""; + let generatedRel = ""; + + if (this.generated_at) { + generatedAt = greports.format.formatTimestamp(this.generated_at); + + let timeValue = (Date.now() - this.generated_at) / (1000 * 60); + let timeUnit = "minute"; + + if (timeValue < 1) { + generatedRel = "just now"; + } else { + if (timeValue > 60) { + timeValue = timeValue / 60; + timeUnit = "hour"; + } + + generatedRel = greports.format.formatTimespan(-Math.round(timeValue), timeUnit); + } + } + + return html` +
+

+ Godot PRs by File +

+ +
+ `; + } +} \ No newline at end of file diff --git a/src/paths/index/components/files/FileItem.js b/src/paths/index/components/files/FileItem.js new file mode 100644 index 0000000..0c7915f --- /dev/null +++ b/src/paths/index/components/files/FileItem.js @@ -0,0 +1,121 @@ +import { LitElement, html, css, customElement, property } from 'lit-element'; + +@customElement('gr-file-item') +export default class FileItem extends LitElement { + static get styles() { + return css` + /** Colors and variables **/ + :host { + --tab-hover-background-color: rgba(0, 0, 0, 0.14); + --tab-active-background-color: #d6e6ff; + --tab-active-border-color: #397adf; + } + @media (prefers-color-scheme: dark) { + :host { + --tab-hover-background-color: rgba(255, 255, 255, 0.14); + --tab-active-background-color: #2c3c55; + --tab-active-border-color: #397adf; + } + } + + /** Component styling **/ + :host { + max-width: 240px; + } + + :host .file-item { + border-left: 5px solid transparent; + color: var(--g-font-color); + cursor: pointer; + display: flex; + flex-direction: row; + gap: 8px; + padding: 3px 12px; + align-items: center; + } + :host .file-item:hover { + background-color: var(--tab-hover-background-color); + } + :host .file-item--active { + background-color: var(--tab-active-background-color); + border-left: 5px solid var(--tab-active-border-color); + } + + :host .file-icon { + background-size: cover; + border-radius: 2px; + display: inline-block; + width: 16px; + height: 16px; + min-width: 16px; + } + :host .file-icon--folder { + background-image: url('/folder.svg'); + } + :host .file-icon--file { + background-image: url('/file.svg'); + filter: brightness(0.5); + } + + :host .file-title { + font-size: 13px; + white-space: nowrap; + overflow: hidden; + } + + :host .file-pull-count { + color: var(--dimmed-font-color); + flex-grow: 1; + font-size: 13px; + text-align: right; + } + :host .file-pull-count--hot { + color: var(--g-font-color); + font-weight: 700; + } + + @media only screen and (max-width: 900px) { + :host .file-item { + padding: 6px 16px; + } + + :host .file-title, + :host .file-pull-count { + font-size: 16px; + } + } + `; + } + + @property({ type: String }) path = ""; + @property({ type: String, reflect: true }) name = ""; + @property({ type: String, reflect: true }) type = ""; + @property({ type: Boolean, reflect: true }) active = false; + @property({ type: Number }) pull_count = 0; + + render(){ + const classList = [ "file-item" ]; + if (this.active) { + classList.push("file-item--active"); + } + + const iconClassList = [ "file-icon", "file-icon--" + this.type ]; + + const countClassList = [ "file-pull-count" ]; + if (this.pull_count > 50) { + countClassList.push("file-pull-count--hot"); + } + + return html` +
+
+ + ${this.path} + + + ${this.pull_count} + +
+ `; + } +} diff --git a/src/paths/index/components/files/FileList.js b/src/paths/index/components/files/FileList.js new file mode 100644 index 0000000..64730ae --- /dev/null +++ b/src/paths/index/components/files/FileList.js @@ -0,0 +1,76 @@ +import { LitElement, html, css, customElement, property } from 'lit-element'; + +import FileItem from "./FileItem"; + +@customElement('gr-file-list') +export default class FileList extends LitElement { + static get styles() { + return css` + /** Colors and variables **/ + :host { + --files-background-color: #fcfcfa; + --files-border-color: #515c6c; + } + @media (prefers-color-scheme: dark) { + :host { + --files-background-color: #0d1117; + --files-border-color: #515c6c; + } + } + + /** Component styling **/ + :host { + } + + :host .file-list { + background-color: var(--files-background-color); + border-right: 2px solid var(--files-border-color); + width: 320px; + min-height: 216px; + } + + @media only screen and (max-width: 900px) { + :host { + width: 100% + } + + :host .file-list { + width: 100% !important; + } + } + `; + } + + @property({ type: Object }) files = {}; + @property({ type: String }) selected = ""; + + constructor() { + super(); + } + + render() { + const topLevel = this.files[""] || []; + + return html` +
+
+ ${(topLevel.length > 0) ? + topLevel.map((item) => { + return html` + + `; + }) : html` + There are no files + ` + } +
+
+ `; + } +} diff --git a/src/paths/index/entry.js b/src/paths/index/entry.js new file mode 100644 index 0000000..8d537de --- /dev/null +++ b/src/paths/index/entry.js @@ -0,0 +1,110 @@ +import { LitElement, html, css, customElement, property } from 'lit-element'; + +import PageContent from 'src/shared/components/PageContent'; +import IndexHeader from "./components/IndexHeader"; +import IndexDescription from "./components/IndexDescription"; + +import FileList from "./components/files/FileList"; + +@customElement('entry-component') +export default class EntryComponent extends LitElement { + static get styles() { + return css` + /** Colors and variables **/ + :host { + } + @media (prefers-color-scheme: dark) { + :host { + } + } + + /** Component styling **/ + :host { + } + `; + } + + constructor() { + super(); + + this._entryRequested = false; + this._isLoading = true; + this._generatedAt = null; + + this._files = {}; + + this._requestData(); + } + + performUpdate() { + this._requestData(); + super.performUpdate(); + } + + async _requestData() { + if (this._entryRequested) { + return; + } + this._entryRequested = true; + this._isLoading = true; + const data = await greports.api.getData(); + + if (data) { + this._generatedAt = data.generated_at; + + data.files.forEach((file) => { + if (file.type === "file" || file.type === "folder") { + if (typeof this._files[file.parent] === "undefined") { + this._files[file.parent] = []; + } + + this._files[file.parent].push(file); + } + }); + + for (let folderName in this._files) { + this._files[folderName].sort((a, b) => { + if (a.type === "folder" && b.type !== "folder") { + return -1; + } + if (b.type === "folder" && a.type !== "folder") { + return 1; + } + + const a_name = a.path.toLowerCase(); + const b_name = b.path.toLowerCase(); + + if (a_name > b_name) return 1; + if (a_name < b_name) return -1; + return 0; + }); + } + } else { + this._generatedAt = null; + + this._files = []; + } + + this._isLoading = false; + this.requestUpdate(); + } + + render(){ + return html` + + + + + ${(this._isLoading ? html` +

Loading...

+ ` : html` +
+ +
+ `)} +
+ `; + } +} diff --git a/src/paths/index/template.html b/src/paths/index/template.html new file mode 100644 index 0000000..bb579b2 --- /dev/null +++ b/src/paths/index/template.html @@ -0,0 +1,16 @@ + + + + + + Godot PRs by File + + + + + + + + + + \ No newline at end of file diff --git a/src/shared/components/PageContent.js b/src/shared/components/PageContent.js new file mode 100644 index 0000000..a41a63a --- /dev/null +++ b/src/shared/components/PageContent.js @@ -0,0 +1,28 @@ +import { LitElement, html, css, customElement } from 'lit-element'; + +@customElement('page-content') +export default class PageContent extends LitElement { + static get styles() { + return css` + /** Component styling **/ + :host { + display: block; + margin: 0 auto; + padding: 0 12px; + max-width: 1024px; + } + + @media only screen and (max-width: 900px) { + :host { + padding: 0; + } + } + `; + } + + render(){ + return html` + + `; + } +} \ No newline at end of file diff --git a/src/shared/partials/body_content.html b/src/shared/partials/body_content.html new file mode 100644 index 0000000..e69de29 diff --git a/src/shared/partials/head_content.html b/src/shared/partials/head_content.html new file mode 100644 index 0000000..5d3e248 --- /dev/null +++ b/src/shared/partials/head_content.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/shared/scripts/global.js b/src/shared/scripts/global.js new file mode 100644 index 0000000..9426f1b --- /dev/null +++ b/src/shared/scripts/global.js @@ -0,0 +1,124 @@ +const LOCAL_PREFERENCE_PREFIX = "_godot_prbf" +const LOCAL_PREFERENCE_DEFAULTS = { + +}; + +// API Interaction +const ReportsAPI = { + async get(path = '/') { + const res = await fetch(`${path}`); + if (res.status !== 200) { + return null; + } + + return await res.json(); + }, + + async getData() { + return await this.get("data.json"); + }, +}; + +// Content helpers +const ReportsFormatter = { + formatDate(dateString) { + const options = { + year: 'numeric', month: 'long', day: 'numeric', + }; + const dateFormatter = new Intl.DateTimeFormat('en-US', options); + + const date = new Date(dateString); + return dateFormatter.format(date); + }, + + formatTimestamp(timeString) { + const options = { + year: 'numeric', month: 'long', day: 'numeric', + hour: 'numeric', hour12: false, minute: 'numeric', + timeZone: 'UTC', timeZoneName: 'short', + }; + const dateFormatter = new Intl.DateTimeFormat('en-US', options); + + const date = new Date(timeString); + return dateFormatter.format(date); + }, + + formatTimespan(timeValue, timeUnit) { + const options = { + style: 'long', + }; + const timeFormatter = new Intl.RelativeTimeFormat('en-US', options); + + return timeFormatter.format(timeValue, timeUnit); + }, + + getDaysSince(dateString) { + const date = new Date(dateString); + const msBetween = (new Date()) - date; + const days = Math.floor(msBetween / (1000 * 60 * 60 * 24)); + + return days; + }, + + formatDays(days) { + return days + " " + (days !== 1 ? "days" : "day"); + }, +}; + +const ReportsUtils = { + createEvent(name, detail = {}) { + return new CustomEvent(name, { + detail: detail + }); + }, + + getHistoryHash() { + let rawHash = window.location.hash; + if (rawHash !== "") { + return rawHash.substr(1); + } + + return ""; + }, + + setHistoryHash(hash) { + const url = new URL(window.location); + url.hash = hash; + window.history.pushState({}, "", url); + }, + + getLocalPreferences() { + // Always fallback on defaults. + const localPreferences = { ...LOCAL_PREFERENCE_DEFAULTS }; + + for (let key in localPreferences) { + const storedValue = localStorage.getItem(`${LOCAL_PREFERENCE_PREFIX}_${key}`); + if (storedValue != null) { + localPreferences[key] = JSON.parse(storedValue); + } + } + + return localPreferences; + }, + + setLocalPreferences(currentPreferences) { + for (let key in currentPreferences) { + // Only store known properties. + if (key in LOCAL_PREFERENCE_DEFAULTS) { + localStorage.setItem(`${LOCAL_PREFERENCE_PREFIX}_${key}`, JSON.stringify(currentPreferences[key])); + } + } + }, + + resetLocalPreferences() { + this.setLocalPreferences(LOCAL_PREFERENCE_DEFAULTS); + }, +}; + +const ReportsSingleton = { + api: ReportsAPI, + format: ReportsFormatter, + util: ReportsUtils, +}; + +window.greports = ReportsSingleton; \ No newline at end of file diff --git a/src/shared/styles/global.css b/src/shared/styles/global.css new file mode 100644 index 0000000..217cc4d --- /dev/null +++ b/src/shared/styles/global.css @@ -0,0 +1,53 @@ +/** Colors and variables **/ +:root { + --g-background-color: #fcfcfa; + --g-background-extra-color: #98a5b8; + --g-font-color: #121314; + --g-font-size: 15px; + --g-font-weight: 400; + --g-line-height: 20px; + + --link-font-color: #1d6dff; + --link-font-color-hover: #1051c9; + --link-font-color-inactive: #35496f; + + --dimmed-font-color: #535c5f; + --light-font-color: #6b7893; +} + +@media (prefers-color-scheme: dark) { + :root { + --g-background-color: #0d1117; + --g-background-extra-color: #515c6c; + --g-font-color: rgba(228, 228, 232, 0.9); + + --link-font-color: #367df7; + --link-font-color-hover: #6391ec; + --link-font-color-inactive: #abbdcc; + + --dimmed-font-color: #929da0; + --light-font-color: #8491ab; + } +} + +/** General styling **/ +html {} + +body { + background: var(--g-background-color); + color: var(--g-font-color); + font-family: 'Roboto', sans-serif; + font-size: var(--g-font-size); + font-weight: var(--g-font-weight); + line-height: var(--g-line-height); + min-width: 380px; +} + +a { + color: var(--link-font-color); + font-weight: 700; + text-decoration: none; +} +a:hover { + color: var(--link-font-color-hover); +} diff --git a/src/shared/styles/normalize.css b/src/shared/styles/normalize.css new file mode 100644 index 0000000..b0c1902 --- /dev/null +++ b/src/shared/styles/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} \ No newline at end of file diff --git a/src/static/favicon.png b/src/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..ac69f11e014b8a64542ec956ea5f36e712bd26cc GIT binary patch literal 1929 zcmV;42X^?0P)a&hkDhV#IKmkkd* zcuODpiN9?SKdjUrzG%Y@SP;F3H%Y(N2;yq3Nea@M#7;abZO)s-HE4sjXa_EEa;}co z4C0rW;Pp&!4(k9tXN?-;y)Vjf{L}a0m*D$+dvJmqF*sL*#w5*ULfawMp$TGu0-jyY zI1mG|I9CHY2Z3_pz4Z6L>TCotpEfxj-emUYTnw1E+aXTgB)uaJ_THp~LnD~Cx!wpc znO7-F0Oq$Ko#F-Q9(#_1;TeBp&rM1=c%!t^_0|BvJPG1dF{3om^+tfV$g7k9tN~ii z)quTPwGrSQ_!~|w4D}BGi2T+ij%LEjRZ9S38O@{mu3f%k^aE#wg0X7&CJL}tQk84v^jWR?PUo1w&If*YmkhFZe~e3 z|Jxr@Fz?LyqL$z71JsfTS;5=rQZ`U zY*#4771WYFR5iBKg3~em*7-$eVyU{Ro$Mhtzm|qFEvhEqP2V8OC~qVHsI9G~n>TLK z`STYjB_)M`){btPb|kXTou7U*l3F{v2}ns!K@7w~Ok4bnvPOEtCrG^n>=UzX!NbDB z=*>6YqQC#`A2u`Sjbyc0F+v=R5=2^mB@u>ahkgl#b!4$vNUt|M6XUHn-=^@eZ~}n6 zVwQRd$f;~1prWFJUVrTk2ztTrNm+?#^uBPr99LqB2mmfe7uoe0yZ;dZ=gytC7Xz`G zhDrjmE1J|v0MD$e+d{zUQ>P)su8kctjsQmsRW6eFKj7J(585&?uCWf|oH=urfKH}~ zS8D`xbXf@4vuCeEVHl#o^7HX_Ir6HS2>|eH&u8U@cmlxXD9+x!`v_?7>{f3Iq?I-h z5Ey87C~V1+rDU;Mv0L;y0V8&Yk(F^QS-iwy98nYrNGYjTF9F_cuLrQu&6_*#1yLF= zE#>6oP-tc){Ck}MSa?eyM;na8X;RtX(69Yi(dr4)`qEaE`pins+l0!FH zIea#v04p~>4cT_4J86X71ZKFMD(4SQl--DdScoZ8XsMKdzSz6|A@q)apUw>`B)|;} zUy^V=#ni)*Mt4cTlp_%auBWJP=~z`z(?Y1l15?ZCQgji{0Nq=zC%4Q$71O|0=f=1F zg4|$!LOK$X zYh?$gHYvTr3cqs^1+rq#M-~u(HfReDaDfxth=Ev$31Fp{e*%_MzY$i*rsXgX<-*5YCa^+I)jLhwlz;ntC{*f&KTke#6zICg1xR z?MFj%Q9BS0Opmgw@rlygzQG-Gd{{7pGb#yf&=wrv0w=f;1F;Yjv1RyRGq_#?mnGiM z2%-mY6&Ux+ebFD_`U1=!HWDfaVCTzqhQM!feFu^t{*29~k^$8DBHsA$CtQC*rx(A@ zo20-2(D?_rk^b_231So{YfNIXD}L(u23VC}mb|&)#z(Qby25QTTGCdYf4qv$j5TwhbKM0w=f;W5E4_ z42u$P692@`7jL63XCr1B>(qRlIAM6k-|!ATUIz0Y(dK2(XAeB^z=MAS4*8~-AMHOz P00000NkvXXu0mjfyF8U* literal 0 HcmV?d00001 diff --git a/src/static/icons/file.svg b/src/static/icons/file.svg new file mode 100644 index 0000000..d3c01ca --- /dev/null +++ b/src/static/icons/file.svg @@ -0,0 +1 @@ + diff --git a/src/static/icons/folder.svg b/src/static/icons/folder.svg new file mode 100644 index 0000000..c2def25 --- /dev/null +++ b/src/static/icons/folder.svg @@ -0,0 +1 @@ +