├── .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 | }
--------------------------------------------------------------------------------