├── .gitattribute ├── .gitignore ├── LICENSE ├── README.md ├── assets └── .gitkeep ├── index.html ├── jsconfig.json ├── noop ├── build.mjs ├── client.mjs ├── index.mjs ├── log.mjs ├── server.mjs ├── styles.mjs └── utils.mjs ├── package-lock.json ├── package.json └── src ├── _dev ├── client.css └── client.js ├── index.js ├── lib └── query.js └── style └── index.css /.gitattribute: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text eol=lf 3 | # Denote all files that are truly binary and should not be modified. 4 | *.png binary 5 | *.jpg binary 6 | *.gif binary 7 | *.ttf binary 8 | *.woff binary 9 | *.woff2 binary 10 | *.eot binary 11 | *.ico binary 12 | *.mp4 binary 13 | *.mp3 binary 14 | *.webm binary 15 | *.wav binary 16 | *.ogg binary 17 | *.ogv binary 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | static 3 | node_modules/* 4 | server/ 5 | npm-debug.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Florian Morel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | noop-starter 2 | === 3 | 4 | A barebones modern web project starter. 5 | 6 | ## Includes: 7 | - esbuild & postcss 8 | - polka & sirv for dev server 9 | - code banner with # & date of build 10 | - gzip output 11 | - (very) minimal code structure 12 | - opiniated normalize.css 13 | - colorful terminal logging 14 | - in-browser popup for code errors 15 | - css live reload 16 | 17 | ## You probably want something else 18 | ¯\\_(ツ)_/¯ 19 | 20 | 21 | ## License 22 | MIT. -------------------------------------------------------------------------------- /assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayamflow/noop-starter/49237dccfa9a2a40a2e8b11bcd16d05f1e5f5f97/assets/.gitkeep -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "components/*": ["./src/components/*"], 6 | "lib/*": ["./src/lib/*"] 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /noop/build.mjs: -------------------------------------------------------------------------------- 1 | import * as utils from './utils.mjs' 2 | import * as client from './client.mjs' 3 | import { serve } from './server.mjs' 4 | import esbuild from 'esbuild' 5 | import kleur from 'kleur' 6 | import { log } from './log.mjs' 7 | import { styles } from './styles.mjs' 8 | 9 | export function build(options = {}) { 10 | utils.setup('production') 11 | 12 | return esbuild.build(getParams(options)).then(async () => { 13 | await utils.gzip() 14 | utils.filesize() 15 | }) 16 | } 17 | 18 | export async function watch(options = {}) { 19 | const port = options.port 20 | const server = serve({ port }) 21 | const params = getParams(options) 22 | 23 | function rebuild() { 24 | return esbuild.build(params) 25 | .then(() => { 26 | client.hideError() 27 | }) 28 | .catch(error => { 29 | client.showError(error) 30 | }) 31 | } 32 | 33 | utils.setup('dev') 34 | client.init(server) 35 | await rebuild() 36 | 37 | const watcher = await utils.watch('./src') 38 | watcher.on('+', async event => { 39 | if (!event.path.match(/(.js)|(.css)/)) return 40 | 41 | log(`code change: ${event.path}`) 42 | 43 | await rebuild() 44 | if (event.path.includes('.css')) client.reloadStyles() 45 | }) 46 | 47 | process.on('SIGINT', function () { 48 | utils.stopWatch() 49 | console.log('\n') 50 | log(`Stopped server (${kleur.underline('Watching')}/${kleur.underline('Serving')}). Bye!`) 51 | console.log('\n') 52 | client.close() 53 | process.exit() 54 | }) 55 | } 56 | 57 | function getParams(options) { 58 | if (options.port) delete options.port 59 | let env = setEnvironment(options) 60 | let version = utils.getVersion() 61 | 62 | return Object.assign({ 63 | entryPoints: ['src/index.js'], 64 | bundle: true, 65 | loader: { 66 | '.svg': 'file', 67 | '.json': 'file', 68 | '.jpg': 'file', 69 | '.gif': 'file', 70 | '.png': 'file' 71 | }, 72 | outfile: 'static/bundle.js', 73 | banner: `// noop build ${version}`, 74 | plugins: [styles()] 75 | }, env, options) 76 | } 77 | 78 | function setEnvironment(options) { 79 | if (!options.env) options.env = 'production' 80 | let env = { 81 | define: { 82 | 'process.env.NODE_ENV': `\"${options.env}\"` 83 | } 84 | } 85 | 86 | delete options.env 87 | return env 88 | } -------------------------------------------------------------------------------- /noop/client.mjs: -------------------------------------------------------------------------------- 1 | var source 2 | export function init(server) { 3 | server.get('/_dev', (req, res) => { 4 | res.writeHead(200, { 5 | 'Content-Type': 'text/event-stream', 6 | 'Cache-Control': 'no-cache', 7 | 'Connection': 'keep-alive', 8 | }) 9 | res.write('\n\n') 10 | 11 | source = res 12 | }) 13 | } 14 | 15 | export function close() { 16 | // ¯\_(ツ)_/¯ 17 | } 18 | 19 | export function reloadStyles() { 20 | if (!source) return 21 | 22 | send({ 23 | type: 'style' 24 | }) 25 | } 26 | 27 | export function showError(error) { 28 | if (!source) return 29 | 30 | send({ 31 | type: 'error', 32 | error: { 33 | original: error, 34 | type: getType(error) 35 | } 36 | }) 37 | } 38 | 39 | export function hideError() { 40 | if (!source) return 41 | 42 | send({ 43 | type: 'hideerror' 44 | }) 45 | } 46 | 47 | function send(event) { 48 | if (!source) return 49 | 50 | source.write(`data: ${JSON.stringify(event)}\n\n`) 51 | } 52 | 53 | function getType(object) { 54 | return object.constructor.toString().split('(')[0].replace('function ', '') 55 | } -------------------------------------------------------------------------------- /noop/index.mjs: -------------------------------------------------------------------------------- 1 | import { build, watch } from './build.mjs' 2 | 3 | let cmd = process.argv.slice(2) 4 | 5 | if (cmd.indexOf('--watch') > -1) dev() 6 | else prod() 7 | 8 | function prod() { 9 | build({ 10 | env: 'production', 11 | minify: true 12 | }) 13 | } 14 | 15 | function dev() { 16 | watch({ 17 | port: 3000, 18 | env: 'dev', 19 | sourcemap: true, 20 | color: true, 21 | incremental: true 22 | }) 23 | } -------------------------------------------------------------------------------- /noop/log.mjs: -------------------------------------------------------------------------------- 1 | import kleur from 'kleur' 2 | 3 | export function log(content) { 4 | let prefix = kleur.bold().white('[noop]') 5 | console.log(`${prefix} ${content}`) 6 | } 7 | 8 | export function warning(content) { 9 | log(kleur.italic().yellow(content)) 10 | } 11 | 12 | export function error(content) { 13 | log(kleur.bold().red(content)) 14 | } -------------------------------------------------------------------------------- /noop/server.mjs: -------------------------------------------------------------------------------- 1 | import polka from 'polka' 2 | import sirv from 'sirv' 3 | import path from 'path' 4 | import { log } from './log.mjs' 5 | import kleur from 'kleur' 6 | const dir = path.join(path.resolve(), './static') 7 | 8 | export function serve({ port = 3000 }) { 9 | const params = { 10 | single: true, 11 | dev: true, 12 | ignores: '_dev' 13 | } 14 | 15 | return polka().use(sirv(dir, params)).listen(port, error => { 16 | if (error) throw error 17 | let localhost = `http://localhost:${port}` 18 | log(`${kleur.underline('Serving')} files on ${kleur.yellow(localhost)}`) 19 | }) 20 | } -------------------------------------------------------------------------------- /noop/styles.mjs: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss' 2 | import autoprefixer from 'autoprefixer' 3 | import normalize from 'postcss-normalize' 4 | import nested from 'postcss-nested' 5 | import fs from 'fs' 6 | 7 | export function styles() { 8 | return { 9 | name: 'esbuild-postcss', 10 | setup(build) { 11 | build.onLoad({ filter: /\.css/ }, async args => { 12 | let contents = await fs.promises.readFile(args.path, 'utf8') 13 | let result = await postcss([ 14 | autoprefixer, 15 | nested, 16 | normalize 17 | ]) 18 | .process(contents, { from: args.path }) 19 | contents = result.css 20 | 21 | return { 22 | contents, 23 | loader: 'css' 24 | } 25 | }) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /noop/utils.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import childProcess from 'child_process' 4 | import zlib from 'zlib' 5 | import CheapWatch from 'cheap-watch' 6 | import kleur from 'kleur' 7 | import { log, error, warning } from './log.mjs' 8 | 9 | const watchers = [] 10 | 11 | export function setup(env) { 12 | clean('static') 13 | copyAssets('static', {watch: env == 'dev'}) 14 | templateHtml('static') 15 | } 16 | 17 | function getHour(date) { 18 | return ` 19 | ${date.getHours()} 20 | : 21 | ${date.getMinutes().toString().padStart(2, 0)} 22 | : 23 | ${date.getSeconds().toString().padStart(2, 0)} 24 | `.replace(/\n/g, '').replace(/\ /g, '') 25 | } 26 | 27 | /* 28 | getVersion 29 | Generates a unique version string based on 30 | time and git commits number 31 | */ 32 | export function getVersion() { 33 | let version = 0 34 | try { 35 | version = childProcess.execSync('git rev-list HEAD --count').toString() 36 | } catch(e) { 37 | error('You need at least 1 commit to build'); 38 | } 39 | let date = new Date(Date.now()) 40 | 41 | let day = ` 42 | ${date.getDate().toString().padStart(2, 0)} 43 | / 44 | ${date.getMonth() + 1} 45 | / 46 | ${date.getFullYear()} 47 | `.replace(/\n/g, '').replace(/\ /g, '') 48 | 49 | let time = getHour(date) 50 | 51 | return `${version}. (${day} ${time})`.replace(/\n/g, '') 52 | } 53 | 54 | /* 55 | clean 56 | completely empties a folder, recursively 57 | - path {string} the folder path to clean 58 | */ 59 | export function clean(path) { 60 | fs.rmSync(path, { recursive: true, force: true }) 61 | childProcess.execSync(`mkdir ${path}`).toString() 62 | } 63 | 64 | /* 65 | copyAssets 66 | copy the source assets to the output fodler 67 | - dest {string} the output fodler 68 | */ 69 | export async function copyAssets(dest, options = {}) { 70 | const src = './assets' 71 | childProcess.execSync(`cp -rf ${src} ${dest}/assets`).toString() 72 | 73 | if (options.watch) { 74 | const watcher = new CheapWatch({ 75 | dir: path.resolve(src), 76 | filter: ({path}) => { 77 | return path != 'assets' && !path.match(/(^|[\/\\])\../) 78 | } 79 | }) 80 | watchers.push(watcher) 81 | await watcher.init() 82 | log(`${kleur.underline('Watching')} ${kleur.yellow(src)} folder`) 83 | 84 | watcher.on('+', event => { 85 | let time = getHour(new Date(Date.now())) 86 | log(`${event.isNew ? 'add' : 'update'} ${kleur.yellow(path.join(src, event.path))} ${kleur.blue('(' + time + ')')}`) 87 | childProcess.execSync(`cp -rf ${path.join(src, event.path)} ${dest}/assets`).toString() 88 | }) 89 | watcher.on('-', event => { 90 | let time = getHour(new Date(Date.now())) 91 | log(`remove ${kleur.yellow(path.join(src, event.path))} ${kleur.blue('(' + time + ')')}`) 92 | childProcess.execSync(`rm -rf ${path.join(dest, src, event.path)}`).toString() 93 | }) 94 | } 95 | } 96 | 97 | export async function watch(folder) { 98 | const watcher = new CheapWatch({ dir: path.resolve(folder) }) 99 | await watcher.init() 100 | watchers.push(watcher) 101 | return watcher 102 | } 103 | 104 | export function stopWatch() { 105 | watchers.forEach(watcher => watcher.close()) 106 | } 107 | 108 | /* 109 | copyHtml 110 | copy the index.html template 111 | - dest {string} the output fodler 112 | */ 113 | export function templateHtml(dest) { 114 | let file = fs.readFileSync('index.html', 'utf8') 115 | file = file.replace('{{script}}', `./bundle.js`) 116 | file = file.replace('{{styles}}', `./bundle.css`) 117 | fs.writeFileSync(`${dest}/index.html`, file) 118 | } 119 | 120 | export function gzip() { 121 | const path = './static/bundle.js' 122 | const file = fs.createReadStream(path) 123 | const stream = fs.createWriteStream(path.replace('.js', '.js.gz')) 124 | const zip = zlib.createGzip() 125 | return new Promise((resolve, reject) => { 126 | file.pipe(zip).pipe(stream).on('finish', (err) => { 127 | if (err) return reject(err) 128 | else resolve() 129 | }) 130 | }) 131 | } 132 | 133 | export async function filesize() { 134 | const dir = './static' 135 | let sizes = {} 136 | let files = await fs.promises.readdir(dir) 137 | await Promise.all(files.map(async file => { 138 | let stats = await fs.promises.stat(path.join(dir, file)) 139 | let i = Math.floor(Math.log(stats.size) / Math.log(1024)) 140 | let suffix = ['B', 'KB', 'MB', 'GB', 'TB'][i] 141 | let size = (stats.size / Math.pow(1024, i)).toString() 142 | let index = size.indexOf('.') 143 | if (index > -1) { 144 | sizes[file] = `${size.slice(0, index + 3)}${suffix}` 145 | } else { 146 | sizes[file] = `${size}${suffix}` 147 | } 148 | })) 149 | 150 | for (let size in sizes) { 151 | warning(`${size}: ${sizes[size]}`) 152 | } 153 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "noop-starter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "autoprefixer": "^10.0.4", 12 | "cheap-watch": "^1.0.3", 13 | "esbuild": "^0.8.18", 14 | "kleur": "^4.1.3", 15 | "polka": "^0.5.2", 16 | "postcss": "^8.1.14", 17 | "postcss-nested": "^5.0.2", 18 | "postcss-normalize": "^9.0.0", 19 | "sirv": "^1.0.10" 20 | } 21 | }, 22 | "node_modules/@arr/every": { 23 | "version": "1.0.1", 24 | "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.1.tgz", 25 | "integrity": "sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==", 26 | "dev": true, 27 | "engines": { 28 | "node": ">=4" 29 | } 30 | }, 31 | "node_modules/@csstools/normalize.css": { 32 | "version": "11.0.1", 33 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-11.0.1.tgz", 34 | "integrity": "sha512-kUlWZHQkll+lOlYdj7dg8XwziO9WALkfG2dAXLITZMB8gO99CXQBH5W/HleXC3YwWFWXVxe1UClMk/2qsB9oAw==", 35 | "dev": true 36 | }, 37 | "node_modules/@polka/url": { 38 | "version": "0.5.0", 39 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", 40 | "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", 41 | "dev": true 42 | }, 43 | "node_modules/ansi-styles": { 44 | "version": "3.2.1", 45 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 46 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 47 | "dev": true, 48 | "dependencies": { 49 | "color-convert": "^1.9.0" 50 | }, 51 | "engines": { 52 | "node": ">=4" 53 | } 54 | }, 55 | "node_modules/autoprefixer": { 56 | "version": "10.0.4", 57 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.0.4.tgz", 58 | "integrity": "sha512-hmjYejN/WTyPP9cdNmiwtwqM8/ACVJPD5ExtwoOceQohNbgnFNiwpL2+U4bXS8aXozBL00WvH6WhqbuHf0Fgfg==", 59 | "dev": true, 60 | "dependencies": { 61 | "browserslist": "^4.14.7", 62 | "caniuse-lite": "^1.0.30001161", 63 | "colorette": "^1.2.1", 64 | "normalize-range": "^0.1.2", 65 | "num2fraction": "^1.2.2", 66 | "postcss-value-parser": "^4.1.0" 67 | }, 68 | "bin": { 69 | "autoprefixer": "bin/autoprefixer" 70 | }, 71 | "engines": { 72 | "node": "^10 || ^12 || >=14" 73 | } 74 | }, 75 | "node_modules/browserslist": { 76 | "version": "4.15.0", 77 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", 78 | "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", 79 | "dev": true, 80 | "dependencies": { 81 | "caniuse-lite": "^1.0.30001164", 82 | "colorette": "^1.2.1", 83 | "electron-to-chromium": "^1.3.612", 84 | "escalade": "^3.1.1", 85 | "node-releases": "^1.1.67" 86 | }, 87 | "bin": { 88 | "browserslist": "cli.js" 89 | }, 90 | "engines": { 91 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 92 | } 93 | }, 94 | "node_modules/caniuse-lite": { 95 | "version": "1.0.30001165", 96 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", 97 | "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", 98 | "dev": true 99 | }, 100 | "node_modules/chalk": { 101 | "version": "2.4.2", 102 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 103 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 104 | "dev": true, 105 | "dependencies": { 106 | "ansi-styles": "^3.2.1", 107 | "escape-string-regexp": "^1.0.5", 108 | "supports-color": "^5.3.0" 109 | }, 110 | "engines": { 111 | "node": ">=4" 112 | } 113 | }, 114 | "node_modules/chalk/node_modules/supports-color": { 115 | "version": "5.5.0", 116 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 117 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 118 | "dev": true, 119 | "dependencies": { 120 | "has-flag": "^3.0.0" 121 | }, 122 | "engines": { 123 | "node": ">=4" 124 | } 125 | }, 126 | "node_modules/cheap-watch": { 127 | "version": "1.0.3", 128 | "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz", 129 | "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==", 130 | "dev": true, 131 | "engines": { 132 | "node": ">=8" 133 | } 134 | }, 135 | "node_modules/color-convert": { 136 | "version": "1.9.3", 137 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 138 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 139 | "dev": true, 140 | "dependencies": { 141 | "color-name": "1.1.3" 142 | } 143 | }, 144 | "node_modules/color-name": { 145 | "version": "1.1.3", 146 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 147 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 148 | "dev": true 149 | }, 150 | "node_modules/colorette": { 151 | "version": "1.2.1", 152 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", 153 | "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", 154 | "dev": true 155 | }, 156 | "node_modules/cssesc": { 157 | "version": "3.0.0", 158 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 159 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 160 | "dev": true, 161 | "bin": { 162 | "cssesc": "bin/cssesc" 163 | }, 164 | "engines": { 165 | "node": ">=4" 166 | } 167 | }, 168 | "node_modules/electron-to-chromium": { 169 | "version": "1.3.616", 170 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.616.tgz", 171 | "integrity": "sha512-CI8L38UN2BEnqXw3/oRIQTmde0LiSeqWSRlPA42ZTYgJQ8fYenzAM2Z3ni+jtILTcrs5aiXZCGJ96Pm+3/yGyQ==", 172 | "dev": true 173 | }, 174 | "node_modules/esbuild": { 175 | "version": "0.8.18", 176 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.18.tgz", 177 | "integrity": "sha512-EPbCSr7Ems1loeoy1faUGnJOwTwSrGcoYVQ4f4T4JMKjxJxrWAg+zqXkZK7GvqxfvokPp3HV6Raqn6rqAuW7+Q==", 178 | "dev": true, 179 | "bin": { 180 | "esbuild": "bin/esbuild" 181 | } 182 | }, 183 | "node_modules/escalade": { 184 | "version": "3.1.1", 185 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 186 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 187 | "dev": true, 188 | "engines": { 189 | "node": ">=6" 190 | } 191 | }, 192 | "node_modules/escape-string-regexp": { 193 | "version": "1.0.5", 194 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 195 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 196 | "dev": true, 197 | "engines": { 198 | "node": ">=0.8.0" 199 | } 200 | }, 201 | "node_modules/has-flag": { 202 | "version": "3.0.0", 203 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 204 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 205 | "dev": true, 206 | "engines": { 207 | "node": ">=4" 208 | } 209 | }, 210 | "node_modules/indexes-of": { 211 | "version": "1.0.1", 212 | "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", 213 | "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", 214 | "dev": true 215 | }, 216 | "node_modules/kleur": { 217 | "version": "4.1.3", 218 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.3.tgz", 219 | "integrity": "sha512-H1tr8QP2PxFTNwAFM74Mui2b6ovcY9FoxJefgrwxY+OCJcq01k5nvhf4M/KnizzrJvLRap5STUy7dgDV35iUBw==", 220 | "dev": true, 221 | "engines": { 222 | "node": ">=6" 223 | } 224 | }, 225 | "node_modules/matchit": { 226 | "version": "1.1.0", 227 | "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.1.0.tgz", 228 | "integrity": "sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==", 229 | "dev": true, 230 | "dependencies": { 231 | "@arr/every": "^1.0.0" 232 | }, 233 | "engines": { 234 | "node": ">=6" 235 | } 236 | }, 237 | "node_modules/mime": { 238 | "version": "2.4.7", 239 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", 240 | "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", 241 | "dev": true, 242 | "bin": { 243 | "mime": "cli.js" 244 | }, 245 | "engines": { 246 | "node": ">=4.0.0" 247 | } 248 | }, 249 | "node_modules/nanoid": { 250 | "version": "3.1.20", 251 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", 252 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", 253 | "dev": true, 254 | "bin": { 255 | "nanoid": "bin/nanoid.cjs" 256 | }, 257 | "engines": { 258 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 259 | } 260 | }, 261 | "node_modules/node-releases": { 262 | "version": "1.1.67", 263 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", 264 | "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", 265 | "dev": true 266 | }, 267 | "node_modules/normalize-range": { 268 | "version": "0.1.2", 269 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 270 | "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", 271 | "dev": true, 272 | "engines": { 273 | "node": ">=0.10.0" 274 | } 275 | }, 276 | "node_modules/num2fraction": { 277 | "version": "1.2.2", 278 | "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", 279 | "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", 280 | "dev": true 281 | }, 282 | "node_modules/polka": { 283 | "version": "0.5.2", 284 | "resolved": "https://registry.npmjs.org/polka/-/polka-0.5.2.tgz", 285 | "integrity": "sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==", 286 | "dev": true, 287 | "dependencies": { 288 | "@polka/url": "^0.5.0", 289 | "trouter": "^2.0.1" 290 | } 291 | }, 292 | "node_modules/postcss": { 293 | "version": "8.1.14", 294 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.14.tgz", 295 | "integrity": "sha512-KatkyVPBKfENS+c3dpXJoDXnDD5UZs5exAnDksLqaRJPKwYphEPZt4N0m0i049v2/BtWVQibAhxW4ilXXcolpA==", 296 | "dev": true, 297 | "dependencies": { 298 | "colorette": "^1.2.1", 299 | "nanoid": "^3.1.20", 300 | "source-map": "^0.6.1" 301 | }, 302 | "engines": { 303 | "node": "^10 || ^12 || >=14" 304 | } 305 | }, 306 | "node_modules/postcss-browser-comments": { 307 | "version": "3.0.0", 308 | "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", 309 | "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", 310 | "dev": true, 311 | "dependencies": { 312 | "postcss": "^7" 313 | }, 314 | "engines": { 315 | "node": ">=8.0.0" 316 | } 317 | }, 318 | "node_modules/postcss-browser-comments/node_modules/postcss": { 319 | "version": "7.0.35", 320 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", 321 | "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", 322 | "dev": true, 323 | "dependencies": { 324 | "chalk": "^2.4.2", 325 | "source-map": "^0.6.1", 326 | "supports-color": "^6.1.0" 327 | }, 328 | "engines": { 329 | "node": ">=6.0.0" 330 | } 331 | }, 332 | "node_modules/postcss-nested": { 333 | "version": "5.0.2", 334 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.2.tgz", 335 | "integrity": "sha512-ddUGffzocy13jyKzl172kd7IY3puSbGLYDh2UgryYoQWHcpHkOecR6lDnIMgnu+TRVOPajIBqIX9si8a2oRfNg==", 336 | "dev": true, 337 | "dependencies": { 338 | "postcss-selector-parser": "^6.0.4" 339 | }, 340 | "engines": { 341 | "node": ">=10.0" 342 | } 343 | }, 344 | "node_modules/postcss-normalize": { 345 | "version": "9.0.0", 346 | "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-9.0.0.tgz", 347 | "integrity": "sha512-//kq5O1xkygzN1iCioFIBtzyVTgB6ce9+Hu0mNHuUhPn+FnnFSPybe5kBemnUPPqd7QrHc+kdX6GVECUWdU2uQ==", 348 | "dev": true, 349 | "dependencies": { 350 | "@csstools/normalize.css": "*", 351 | "postcss": "^7.0.27", 352 | "postcss-browser-comments": "^3.0.0", 353 | "sanitize.css": "*" 354 | }, 355 | "engines": { 356 | "node": ">=10.0.0" 357 | } 358 | }, 359 | "node_modules/postcss-normalize/node_modules/postcss": { 360 | "version": "7.0.35", 361 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", 362 | "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", 363 | "dev": true, 364 | "dependencies": { 365 | "chalk": "^2.4.2", 366 | "source-map": "^0.6.1", 367 | "supports-color": "^6.1.0" 368 | }, 369 | "engines": { 370 | "node": ">=6.0.0" 371 | } 372 | }, 373 | "node_modules/postcss-selector-parser": { 374 | "version": "6.0.4", 375 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", 376 | "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", 377 | "dev": true, 378 | "dependencies": { 379 | "cssesc": "^3.0.0", 380 | "indexes-of": "^1.0.1", 381 | "uniq": "^1.0.1", 382 | "util-deprecate": "^1.0.2" 383 | }, 384 | "engines": { 385 | "node": ">=4" 386 | } 387 | }, 388 | "node_modules/postcss-value-parser": { 389 | "version": "4.1.0", 390 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", 391 | "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", 392 | "dev": true 393 | }, 394 | "node_modules/sanitize.css": { 395 | "version": "12.0.1", 396 | "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-12.0.1.tgz", 397 | "integrity": "sha512-QbusSBnWHaRBZeTxsJyknwI0q+q6m1NtLBmB76JfW/rdVN7Ws6Zz70w65+430/ouVcdNVT3qwrDgrM6PaYyRtw==", 398 | "dev": true 399 | }, 400 | "node_modules/sirv": { 401 | "version": "1.0.10", 402 | "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.10.tgz", 403 | "integrity": "sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg==", 404 | "dev": true, 405 | "dependencies": { 406 | "@polka/url": "^1.0.0-next.9", 407 | "mime": "^2.3.1", 408 | "totalist": "^1.0.0" 409 | }, 410 | "engines": { 411 | "node": ">= 10" 412 | } 413 | }, 414 | "node_modules/sirv/node_modules/@polka/url": { 415 | "version": "1.0.0-next.11", 416 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz", 417 | "integrity": "sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==", 418 | "dev": true 419 | }, 420 | "node_modules/source-map": { 421 | "version": "0.6.1", 422 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 423 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 424 | "dev": true, 425 | "engines": { 426 | "node": ">=0.10.0" 427 | } 428 | }, 429 | "node_modules/supports-color": { 430 | "version": "6.1.0", 431 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", 432 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", 433 | "dev": true, 434 | "dependencies": { 435 | "has-flag": "^3.0.0" 436 | }, 437 | "engines": { 438 | "node": ">=6" 439 | } 440 | }, 441 | "node_modules/totalist": { 442 | "version": "1.1.0", 443 | "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", 444 | "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", 445 | "dev": true, 446 | "engines": { 447 | "node": ">=6" 448 | } 449 | }, 450 | "node_modules/trouter": { 451 | "version": "2.0.1", 452 | "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", 453 | "integrity": "sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==", 454 | "dev": true, 455 | "dependencies": { 456 | "matchit": "^1.0.0" 457 | }, 458 | "engines": { 459 | "node": ">=6" 460 | } 461 | }, 462 | "node_modules/uniq": { 463 | "version": "1.0.1", 464 | "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 465 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", 466 | "dev": true 467 | }, 468 | "node_modules/util-deprecate": { 469 | "version": "1.0.2", 470 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 471 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 472 | "dev": true 473 | } 474 | }, 475 | "dependencies": { 476 | "@arr/every": { 477 | "version": "1.0.1", 478 | "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.1.tgz", 479 | "integrity": "sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==", 480 | "dev": true 481 | }, 482 | "@csstools/normalize.css": { 483 | "version": "11.0.1", 484 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-11.0.1.tgz", 485 | "integrity": "sha512-kUlWZHQkll+lOlYdj7dg8XwziO9WALkfG2dAXLITZMB8gO99CXQBH5W/HleXC3YwWFWXVxe1UClMk/2qsB9oAw==", 486 | "dev": true 487 | }, 488 | "@polka/url": { 489 | "version": "0.5.0", 490 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", 491 | "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", 492 | "dev": true 493 | }, 494 | "ansi-styles": { 495 | "version": "3.2.1", 496 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 497 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 498 | "dev": true, 499 | "requires": { 500 | "color-convert": "^1.9.0" 501 | } 502 | }, 503 | "autoprefixer": { 504 | "version": "10.0.4", 505 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.0.4.tgz", 506 | "integrity": "sha512-hmjYejN/WTyPP9cdNmiwtwqM8/ACVJPD5ExtwoOceQohNbgnFNiwpL2+U4bXS8aXozBL00WvH6WhqbuHf0Fgfg==", 507 | "dev": true, 508 | "requires": { 509 | "browserslist": "^4.14.7", 510 | "caniuse-lite": "^1.0.30001161", 511 | "colorette": "^1.2.1", 512 | "normalize-range": "^0.1.2", 513 | "num2fraction": "^1.2.2", 514 | "postcss-value-parser": "^4.1.0" 515 | } 516 | }, 517 | "browserslist": { 518 | "version": "4.15.0", 519 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", 520 | "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", 521 | "dev": true, 522 | "requires": { 523 | "caniuse-lite": "^1.0.30001164", 524 | "colorette": "^1.2.1", 525 | "electron-to-chromium": "^1.3.612", 526 | "escalade": "^3.1.1", 527 | "node-releases": "^1.1.67" 528 | } 529 | }, 530 | "caniuse-lite": { 531 | "version": "1.0.30001165", 532 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", 533 | "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", 534 | "dev": true 535 | }, 536 | "chalk": { 537 | "version": "2.4.2", 538 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 539 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 540 | "dev": true, 541 | "requires": { 542 | "ansi-styles": "^3.2.1", 543 | "escape-string-regexp": "^1.0.5", 544 | "supports-color": "^5.3.0" 545 | }, 546 | "dependencies": { 547 | "supports-color": { 548 | "version": "5.5.0", 549 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 550 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 551 | "dev": true, 552 | "requires": { 553 | "has-flag": "^3.0.0" 554 | } 555 | } 556 | } 557 | }, 558 | "cheap-watch": { 559 | "version": "1.0.3", 560 | "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz", 561 | "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==", 562 | "dev": true 563 | }, 564 | "color-convert": { 565 | "version": "1.9.3", 566 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 567 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 568 | "dev": true, 569 | "requires": { 570 | "color-name": "1.1.3" 571 | } 572 | }, 573 | "color-name": { 574 | "version": "1.1.3", 575 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 576 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 577 | "dev": true 578 | }, 579 | "colorette": { 580 | "version": "1.2.1", 581 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", 582 | "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", 583 | "dev": true 584 | }, 585 | "cssesc": { 586 | "version": "3.0.0", 587 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 588 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 589 | "dev": true 590 | }, 591 | "electron-to-chromium": { 592 | "version": "1.3.616", 593 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.616.tgz", 594 | "integrity": "sha512-CI8L38UN2BEnqXw3/oRIQTmde0LiSeqWSRlPA42ZTYgJQ8fYenzAM2Z3ni+jtILTcrs5aiXZCGJ96Pm+3/yGyQ==", 595 | "dev": true 596 | }, 597 | "esbuild": { 598 | "version": "0.8.18", 599 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.18.tgz", 600 | "integrity": "sha512-EPbCSr7Ems1loeoy1faUGnJOwTwSrGcoYVQ4f4T4JMKjxJxrWAg+zqXkZK7GvqxfvokPp3HV6Raqn6rqAuW7+Q==", 601 | "dev": true 602 | }, 603 | "escalade": { 604 | "version": "3.1.1", 605 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 606 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 607 | "dev": true 608 | }, 609 | "escape-string-regexp": { 610 | "version": "1.0.5", 611 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 612 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 613 | "dev": true 614 | }, 615 | "has-flag": { 616 | "version": "3.0.0", 617 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 618 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 619 | "dev": true 620 | }, 621 | "indexes-of": { 622 | "version": "1.0.1", 623 | "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", 624 | "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", 625 | "dev": true 626 | }, 627 | "kleur": { 628 | "version": "4.1.3", 629 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.3.tgz", 630 | "integrity": "sha512-H1tr8QP2PxFTNwAFM74Mui2b6ovcY9FoxJefgrwxY+OCJcq01k5nvhf4M/KnizzrJvLRap5STUy7dgDV35iUBw==", 631 | "dev": true 632 | }, 633 | "matchit": { 634 | "version": "1.1.0", 635 | "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.1.0.tgz", 636 | "integrity": "sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==", 637 | "dev": true, 638 | "requires": { 639 | "@arr/every": "^1.0.0" 640 | } 641 | }, 642 | "mime": { 643 | "version": "2.4.7", 644 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", 645 | "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", 646 | "dev": true 647 | }, 648 | "nanoid": { 649 | "version": "3.1.20", 650 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", 651 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", 652 | "dev": true 653 | }, 654 | "node-releases": { 655 | "version": "1.1.67", 656 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", 657 | "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", 658 | "dev": true 659 | }, 660 | "normalize-range": { 661 | "version": "0.1.2", 662 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 663 | "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", 664 | "dev": true 665 | }, 666 | "num2fraction": { 667 | "version": "1.2.2", 668 | "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", 669 | "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", 670 | "dev": true 671 | }, 672 | "polka": { 673 | "version": "0.5.2", 674 | "resolved": "https://registry.npmjs.org/polka/-/polka-0.5.2.tgz", 675 | "integrity": "sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==", 676 | "dev": true, 677 | "requires": { 678 | "@polka/url": "^0.5.0", 679 | "trouter": "^2.0.1" 680 | } 681 | }, 682 | "postcss": { 683 | "version": "8.1.14", 684 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.14.tgz", 685 | "integrity": "sha512-KatkyVPBKfENS+c3dpXJoDXnDD5UZs5exAnDksLqaRJPKwYphEPZt4N0m0i049v2/BtWVQibAhxW4ilXXcolpA==", 686 | "dev": true, 687 | "requires": { 688 | "colorette": "^1.2.1", 689 | "nanoid": "^3.1.20", 690 | "source-map": "^0.6.1" 691 | } 692 | }, 693 | "postcss-browser-comments": { 694 | "version": "3.0.0", 695 | "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", 696 | "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", 697 | "dev": true, 698 | "requires": { 699 | "postcss": "^7" 700 | }, 701 | "dependencies": { 702 | "postcss": { 703 | "version": "7.0.35", 704 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", 705 | "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", 706 | "dev": true, 707 | "requires": { 708 | "chalk": "^2.4.2", 709 | "source-map": "^0.6.1", 710 | "supports-color": "^6.1.0" 711 | } 712 | } 713 | } 714 | }, 715 | "postcss-nested": { 716 | "version": "5.0.2", 717 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.2.tgz", 718 | "integrity": "sha512-ddUGffzocy13jyKzl172kd7IY3puSbGLYDh2UgryYoQWHcpHkOecR6lDnIMgnu+TRVOPajIBqIX9si8a2oRfNg==", 719 | "dev": true, 720 | "requires": { 721 | "postcss-selector-parser": "^6.0.4" 722 | } 723 | }, 724 | "postcss-normalize": { 725 | "version": "9.0.0", 726 | "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-9.0.0.tgz", 727 | "integrity": "sha512-//kq5O1xkygzN1iCioFIBtzyVTgB6ce9+Hu0mNHuUhPn+FnnFSPybe5kBemnUPPqd7QrHc+kdX6GVECUWdU2uQ==", 728 | "dev": true, 729 | "requires": { 730 | "@csstools/normalize.css": "*", 731 | "postcss": "^7.0.27", 732 | "postcss-browser-comments": "^3.0.0", 733 | "sanitize.css": "*" 734 | }, 735 | "dependencies": { 736 | "postcss": { 737 | "version": "7.0.35", 738 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", 739 | "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", 740 | "dev": true, 741 | "requires": { 742 | "chalk": "^2.4.2", 743 | "source-map": "^0.6.1", 744 | "supports-color": "^6.1.0" 745 | } 746 | } 747 | } 748 | }, 749 | "postcss-selector-parser": { 750 | "version": "6.0.4", 751 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", 752 | "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", 753 | "dev": true, 754 | "requires": { 755 | "cssesc": "^3.0.0", 756 | "indexes-of": "^1.0.1", 757 | "uniq": "^1.0.1", 758 | "util-deprecate": "^1.0.2" 759 | } 760 | }, 761 | "postcss-value-parser": { 762 | "version": "4.1.0", 763 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", 764 | "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", 765 | "dev": true 766 | }, 767 | "sanitize.css": { 768 | "version": "12.0.1", 769 | "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-12.0.1.tgz", 770 | "integrity": "sha512-QbusSBnWHaRBZeTxsJyknwI0q+q6m1NtLBmB76JfW/rdVN7Ws6Zz70w65+430/ouVcdNVT3qwrDgrM6PaYyRtw==", 771 | "dev": true 772 | }, 773 | "sirv": { 774 | "version": "1.0.10", 775 | "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.10.tgz", 776 | "integrity": "sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg==", 777 | "dev": true, 778 | "requires": { 779 | "@polka/url": "^1.0.0-next.9", 780 | "mime": "^2.3.1", 781 | "totalist": "^1.0.0" 782 | }, 783 | "dependencies": { 784 | "@polka/url": { 785 | "version": "1.0.0-next.11", 786 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz", 787 | "integrity": "sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==", 788 | "dev": true 789 | } 790 | } 791 | }, 792 | "source-map": { 793 | "version": "0.6.1", 794 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 795 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 796 | "dev": true 797 | }, 798 | "supports-color": { 799 | "version": "6.1.0", 800 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", 801 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", 802 | "dev": true, 803 | "requires": { 804 | "has-flag": "^3.0.0" 805 | } 806 | }, 807 | "totalist": { 808 | "version": "1.1.0", 809 | "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", 810 | "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", 811 | "dev": true 812 | }, 813 | "trouter": { 814 | "version": "2.0.1", 815 | "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", 816 | "integrity": "sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==", 817 | "dev": true, 818 | "requires": { 819 | "matchit": "^1.0.0" 820 | } 821 | }, 822 | "uniq": { 823 | "version": "1.0.1", 824 | "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 825 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", 826 | "dev": true 827 | }, 828 | "util-deprecate": { 829 | "version": "1.0.2", 830 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 831 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 832 | "dev": true 833 | } 834 | } 835 | } 836 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "noop-starter", 3 | "version": "1.0.0", 4 | "description": "A barebones modern web project starter", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm run dev", 8 | "dev": "node noop/index.mjs --watch", 9 | "build": "node noop/index.mjs" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/ayamflow/noop-starter.git" 14 | }, 15 | "keywords": [ 16 | "boilerplate", 17 | "starter", 18 | "web", 19 | "js" 20 | ], 21 | "author": "Florian Morel", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/ayamflow/noop-starter/issues" 25 | }, 26 | "homepage": "https://github.com/ayamflow/noop-starter#readme", 27 | "devDependencies": { 28 | "autoprefixer": "^10.0.4", 29 | "cheap-watch": "^1.0.3", 30 | "esbuild": "^0.8.18", 31 | "kleur": "^4.1.3", 32 | "polka": "^0.5.2", 33 | "postcss": "^8.1.14", 34 | "postcss-nested": "^5.0.2", 35 | "postcss-normalize": "^9.0.0", 36 | "sirv": "^1.0.10" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/_dev/client.css: -------------------------------------------------------------------------------- 1 | .dev-error { 2 | width: 100%; 3 | height: 100%; 4 | background-color: rgba(0, 0, 0, 0.3); 5 | z-index: 9999; 6 | 7 | position: absolute; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | } 12 | 13 | .wrapper { 14 | padding: 20px; 15 | border-radius: 3px; 16 | background-color: white; 17 | font-family: Arial, Helvetica, sans-serif; 18 | min-width: 600px; 19 | max-width: calc(100% - 80px); 20 | } 21 | 22 | .type { 23 | color: red; 24 | font-weight: bold; 25 | } 26 | 27 | .file { 28 | font-style: italic; 29 | opacity: 0.6; 30 | } 31 | 32 | .text { 33 | 34 | } 35 | 36 | .code { 37 | margin: 12px; 38 | padding: 20px; 39 | border-radius: 3px; 40 | background-color: #ddd; 41 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 42 | 43 | span { 44 | opacity: 0.6; 45 | } 46 | 47 | em { 48 | font-style: normal; 49 | position: relative; 50 | 51 | &::after { 52 | position: absolute; 53 | display: block; 54 | left: -3px; 55 | right: 0; 56 | margin: 0 auto; 57 | text-align: center; 58 | content: '^'; 59 | font-style: normal; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/_dev/client.js: -------------------------------------------------------------------------------- 1 | import './client.css' 2 | 3 | var source = new EventSource('/_dev') 4 | var errorEl = null 5 | 6 | source.onmessage = function(event) { 7 | let data = JSON.parse(event.data) 8 | // console.log(`[noop] Received server event "${data.type}"`) 9 | 10 | switch(data.type) { 11 | case 'style': return reloadStyles() 12 | case 'error': return showError(data.error) 13 | case 'hideerror': return hideError() 14 | } 15 | } 16 | 17 | function reloadStyles() { 18 | let stylesheet = document.querySelector('link[rel=stylesheet]') 19 | let href = stylesheet.getAttribute('href') 20 | href = href.split('?')[0] 21 | href += `?time=${Date.now()}` 22 | stylesheet.setAttribute('href', href) 23 | // console.log(`[noop] bundle.css reloaded! ${new Date(Date.now())}`) 24 | } 25 | 26 | function showError(error) { 27 | let data = error.original.errors[0] 28 | hideError() 29 | initError(data, error.type) 30 | } 31 | 32 | function hideError() { 33 | if (errorEl && errorEl.parentNode) errorEl.parentNode.removeChild(errorEl) 34 | } 35 | 36 | function initError(error, type) { 37 | let loc = error.location 38 | 39 | // Add ^ underneath error character to mimic terminal output 40 | let line = `${loc.lineText.slice(0, loc.column)}` 41 | line += `${loc.lineText.slice(loc.column, loc.column + loc.length)}` 42 | line += `${loc.lineText.slice(loc.column + loc.length)}` 43 | 44 | let el = document.createElement('div') 45 | el.classList.add('dev-error') 46 | el.innerHTML = ` 47 |
48 |
${type}
49 |
> ${loc.file} line ${loc.line}, column ${loc.column}
50 |

51 |
${error.text}
52 |
53 | ...

54 | ${loc.line}|    ${line} 55 |

... 56 |
57 |
58 | ` 59 | document.body.appendChild(el) 60 | errorEl = document.querySelector('.dev-error') 61 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Live-reload + error popup 2 | if (process.env.NODE_ENV == 'dev') { 3 | import('./_dev/client') 4 | } 5 | import './style/index.css' 6 | 7 | console.log('it starts here!') -------------------------------------------------------------------------------- /src/lib/query.js: -------------------------------------------------------------------------------- 1 | var params = {} 2 | 3 | export function query(value) { 4 | return params[value] 5 | } 6 | 7 | function parse(value) { 8 | // TODO: call on history/page change? 9 | let urlParams = new URLSearchParams(value || location.search) 10 | params = Object.fromEntries(urlParams.entries()) 11 | Object.keys(params).forEach(key => { 12 | let val = params[key] 13 | if (val == '') params[key] = true 14 | }) 15 | } 16 | 17 | parse() -------------------------------------------------------------------------------- /src/style/index.css: -------------------------------------------------------------------------------- 1 | @import "normalize.css/opinionated.css"; 2 | 3 | html, body { 4 | font-weight: 400; 5 | font-size: 16px; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } --------------------------------------------------------------------------------