Skip to content
Snippets Groups Projects
Verified Commit 5e905a86 authored by Jonas Zohren's avatar Jonas Zohren :speech_balloon:
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
node_modules
dist
.env.dev
.debug_data
scratchpad
test_outputdir
test_transcripts
\ No newline at end of file
LICENSE 0 → 100644
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
\ No newline at end of file
# FSInfo Sitting Minutes Postprocessor
> Bunch of markdown files to HTML page
## Usage
`node out.js <input_directory> <output_directory>`
## Dev Setup
Install dependencies:
1. Install NodeJS >= 18
2. `corepack enable` (Enables the `pnpm` shipped with NodeJS)
3. `pnpm install --ignore-scripts`
4. `pnpm build`
out.js 0 → 100644
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "fsinfo-protokoll-postprocessor-2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "vitest",
"format": "prettier --write --ignore-path=.gitignore ./src",
"format:check": "prettier --check --ignore-path=.gitignore ./src",
"build": "esbuild --bundle --platform=node --target=node18 ./src/index.ts --outfile=out.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/markdown-it": "^12.2.3",
"@types/node": "^20.2.5",
"commander": "^10.0.1",
"esbuild": "^0.17.19",
"prettier": "^2.8.8",
"vitest": "^0.31.4"
},
"engines": {
"node": ">=18"
},
"dependencies": {
"gray-matter": "^4.0.3",
"markdown-it": "^13.0.1",
"markdown-it-abbr": "^1.0.4",
"markdown-it-anchor": "^8.6.7",
"markdown-it-attrs": "^4.1.6",
"markdown-it-bracketed-spans": "^1.0.1",
"markdown-it-container": "^3.0.0",
"markdown-it-deflist": "^2.1.0",
"markdown-it-emoji": "^2.0.2",
"markdown-it-footnote": "^3.0.3",
"markdown-it-ins": "^3.0.1",
"markdown-it-mark": "^3.0.1",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0"
}
}
lockfileVersion: '6.0'
dependencies:
gray-matter:
specifier: ^4.0.3
version: 4.0.3
markdown-it:
specifier: ^13.0.1
version: 13.0.1
markdown-it-abbr:
specifier: ^1.0.4
version: 1.0.4
markdown-it-anchor:
specifier: ^8.6.7
version: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@13.0.1)
markdown-it-attrs:
specifier: ^4.1.6
version: 4.1.6(markdown-it@13.0.1)
markdown-it-bracketed-spans:
specifier: ^1.0.1
version: 1.0.1
markdown-it-container:
specifier: ^3.0.0
version: 3.0.0
markdown-it-deflist:
specifier: ^2.1.0
version: 2.1.0
markdown-it-emoji:
specifier: ^2.0.2
version: 2.0.2
markdown-it-footnote:
specifier: ^3.0.3
version: 3.0.3
markdown-it-ins:
specifier: ^3.0.1
version: 3.0.1
markdown-it-mark:
specifier: ^3.0.1
version: 3.0.1
markdown-it-sub:
specifier: ^1.0.0
version: 1.0.0
markdown-it-sup:
specifier: ^1.0.0
version: 1.0.0
devDependencies:
'@types/markdown-it':
specifier: ^12.2.3
version: 12.2.3
'@types/node':
specifier: ^20.2.5
version: 20.2.5
commander:
specifier: ^10.0.1
version: 10.0.1
esbuild:
specifier: ^0.17.19
version: 0.17.19
prettier:
specifier: ^2.8.8
version: 2.8.8
vitest:
specifier: ^0.31.4
version: 0.31.4
packages:
/@esbuild/android-arm64@0.17.19:
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.17.19:
resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.17.19:
resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.17.19:
resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.17.19:
resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.17.19:
resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.17.19:
resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.17.19:
resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.17.19:
resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.17.19:
resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.17.19:
resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.17.19:
resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.17.19:
resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.17.19:
resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.17.19:
resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.17.19:
resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.17.19:
resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.17.19:
resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.17.19:
resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.17.19:
resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.17.19:
resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.17.19:
resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
/@types/chai-subset@1.3.3:
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
dependencies:
'@types/chai': 4.3.5
dev: true
/@types/chai@4.3.5:
resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==}
dev: true
/@types/linkify-it@3.0.2:
resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==}
/@types/markdown-it@12.2.3:
resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==}
dependencies:
'@types/linkify-it': 3.0.2
'@types/mdurl': 1.0.2
/@types/mdurl@1.0.2:
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
/@types/node@20.2.5:
resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
dev: true
/@vitest/expect@0.31.4:
resolution: {integrity: sha512-tibyx8o7GUyGHZGyPgzwiaPaLDQ9MMuCOrc03BYT0nryUuhLbL7NV2r/q98iv5STlwMgaKuFJkgBW/8iPKwlSg==}
dependencies:
'@vitest/spy': 0.31.4
'@vitest/utils': 0.31.4
chai: 4.3.7
dev: true
/@vitest/runner@0.31.4:
resolution: {integrity: sha512-Wgm6UER+gwq6zkyrm5/wbpXGF+g+UBB78asJlFkIOwyse0pz8lZoiC6SW5i4gPnls/zUcPLWS7Zog0LVepXnpg==}
dependencies:
'@vitest/utils': 0.31.4
concordance: 5.0.4
p-limit: 4.0.0
pathe: 1.1.1
dev: true
/@vitest/snapshot@0.31.4:
resolution: {integrity: sha512-LemvNumL3NdWSmfVAMpXILGyaXPkZbG5tyl6+RQSdcHnTj6hvA49UAI8jzez9oQyE/FWLKRSNqTGzsHuk89LRA==}
dependencies:
magic-string: 0.30.0
pathe: 1.1.1
pretty-format: 27.5.1
dev: true
/@vitest/spy@0.31.4:
resolution: {integrity: sha512-3ei5ZH1s3aqbEyftPAzSuunGICRuhE+IXOmpURFdkm5ybUADk+viyQfejNk6q8M5QGX8/EVKw+QWMEP3DTJDag==}
dependencies:
tinyspy: 2.1.1
dev: true
/@vitest/utils@0.31.4:
resolution: {integrity: sha512-DobZbHacWznoGUfYU8XDPY78UubJxXfMNY1+SUdOp1NsI34eopSA6aZMeaGu10waSOeYwE8lxrd/pLfT0RMxjQ==}
dependencies:
concordance: 5.0.4
loupe: 2.3.6
pretty-format: 27.5.1
dev: true
/acorn-walk@8.2.0:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'}
dev: true
/acorn@8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
/ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
dev: true
/argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
dependencies:
sprintf-js: 1.0.3
dev: false
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: false
/assertion-error@1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: true
/blueimp-md5@2.19.0:
resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==}
dev: true
/cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
dev: true
/chai@4.3.7:
resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==}
engines: {node: '>=4'}
dependencies:
assertion-error: 1.1.0
check-error: 1.0.2
deep-eql: 4.1.3
get-func-name: 2.0.0
loupe: 2.3.6
pathval: 1.1.1
type-detect: 4.0.8
dev: true
/check-error@1.0.2:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: true
/commander@10.0.1:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
dev: true
/concordance@5.0.4:
resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==}
engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'}
dependencies:
date-time: 3.1.0
esutils: 2.0.3
fast-diff: 1.3.0
js-string-escape: 1.0.1
lodash: 4.17.21
md5-hex: 3.0.1
semver: 7.5.1
well-known-symbols: 2.0.0
dev: true
/date-time@3.1.0:
resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
engines: {node: '>=6'}
dependencies:
time-zone: 1.0.0
dev: true
/debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
/deep-eql@4.1.3:
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
engines: {node: '>=6'}
dependencies:
type-detect: 4.0.8
dev: true
/entities@3.0.1:
resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==}
engines: {node: '>=0.12'}
dev: false
/esbuild@0.17.19:
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.17.19
'@esbuild/android-arm64': 0.17.19
'@esbuild/android-x64': 0.17.19
'@esbuild/darwin-arm64': 0.17.19
'@esbuild/darwin-x64': 0.17.19
'@esbuild/freebsd-arm64': 0.17.19
'@esbuild/freebsd-x64': 0.17.19
'@esbuild/linux-arm': 0.17.19
'@esbuild/linux-arm64': 0.17.19
'@esbuild/linux-ia32': 0.17.19
'@esbuild/linux-loong64': 0.17.19
'@esbuild/linux-mips64el': 0.17.19
'@esbuild/linux-ppc64': 0.17.19
'@esbuild/linux-riscv64': 0.17.19
'@esbuild/linux-s390x': 0.17.19
'@esbuild/linux-x64': 0.17.19
'@esbuild/netbsd-x64': 0.17.19
'@esbuild/openbsd-x64': 0.17.19
'@esbuild/sunos-x64': 0.17.19
'@esbuild/win32-arm64': 0.17.19
'@esbuild/win32-ia32': 0.17.19
'@esbuild/win32-x64': 0.17.19
dev: true
/esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
dev: false
/esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
dev: true
/extend-shallow@2.0.1:
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
engines: {node: '>=0.10.0'}
dependencies:
is-extendable: 0.1.1
dev: false
/fast-diff@1.3.0:
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
dev: true
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/get-func-name@2.0.0:
resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
dev: true
/gray-matter@4.0.3:
resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
engines: {node: '>=6.0'}
dependencies:
js-yaml: 3.14.1
kind-of: 6.0.3
section-matter: 1.0.0
strip-bom-string: 1.0.0
dev: false
/is-extendable@0.1.1:
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
engines: {node: '>=0.10.0'}
dev: false
/js-string-escape@1.0.1:
resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==}
engines: {node: '>= 0.8'}
dev: true
/js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
dependencies:
argparse: 1.0.10
esprima: 4.0.1
dev: false
/jsonc-parser@3.2.0:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
/kind-of@6.0.3:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
dev: false
/linkify-it@4.0.1:
resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==}
dependencies:
uc.micro: 1.0.6
dev: false
/local-pkg@0.4.3:
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
engines: {node: '>=14'}
dev: true
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true
/loupe@2.3.6:
resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==}
dependencies:
get-func-name: 2.0.0
dev: true
/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
dev: true
/magic-string@0.30.0:
resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/markdown-it-abbr@1.0.4:
resolution: {integrity: sha512-ZeA4Z4SaBbYysZap5iZcxKmlPL6bYA8grqhzJIHB1ikn7njnzaP8uwbtuXc4YXD5LicI4/2Xmc0VwmSiFV04gg==}
dev: false
/markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@13.0.1):
resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==}
peerDependencies:
'@types/markdown-it': '*'
markdown-it: '*'
dependencies:
'@types/markdown-it': 12.2.3
markdown-it: 13.0.1
dev: false
/markdown-it-attrs@4.1.6(markdown-it@13.0.1):
resolution: {integrity: sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==}
engines: {node: '>=6'}
peerDependencies:
markdown-it: '>= 9.0.0'
dependencies:
markdown-it: 13.0.1
dev: false
/markdown-it-bracketed-spans@1.0.1:
resolution: {integrity: sha512-ffDHx7bdOEecvo/EPpsatFLNnLZBRdj+ZF/v6gGJh0ixU0oVWxUUeJn5daVnT0sn0MU73G9oPDTqBX82z99fnw==}
dev: false
/markdown-it-container@3.0.0:
resolution: {integrity: sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==}
dev: false
/markdown-it-deflist@2.1.0:
resolution: {integrity: sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg==}
dev: false
/markdown-it-emoji@2.0.2:
resolution: {integrity: sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==}
dev: false
/markdown-it-footnote@3.0.3:
resolution: {integrity: sha512-YZMSuCGVZAjzKMn+xqIco9d1cLGxbELHZ9do/TSYVzraooV8ypsppKNmUJ0fVH5ljkCInQAtFpm8Rb3eXSrt5w==}
dev: false
/markdown-it-ins@3.0.1:
resolution: {integrity: sha512-32SSfZqSzqyAmmQ4SHvhxbFqSzPDqsZgMHDwxqPzp+v+t8RsmqsBZRG+RfRQskJko9PfKC2/oxyOs4Yg/CfiRw==}
dev: false
/markdown-it-mark@3.0.1:
resolution: {integrity: sha512-HyxjAu6BRsdt6Xcv6TKVQnkz/E70TdGXEFHRYBGLncRE9lBFwDNLVtFojKxjJWgJ+5XxUwLaHXy+2sGBbDn+4A==}
dev: false
/markdown-it-sub@1.0.0:
resolution: {integrity: sha512-z2Rm/LzEE1wzwTSDrI+FlPEveAAbgdAdPhdWarq/ZGJrGW/uCQbKAnhoCsE4hAbc3SEym26+W2z/VQB0cQiA9Q==}
dev: false
/markdown-it-sup@1.0.0:
resolution: {integrity: sha512-E32m0nV9iyhRR7CrhnzL5msqic7rL1juWre6TQNxsnApg7Uf+F97JOKxUijg5YwXz86lZ0mqfOnutoryyNdntQ==}
dev: false
/markdown-it@13.0.1:
resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==}
hasBin: true
dependencies:
argparse: 2.0.1
entities: 3.0.1
linkify-it: 4.0.1
mdurl: 1.0.1
uc.micro: 1.0.6
dev: false
/md5-hex@3.0.1:
resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==}
engines: {node: '>=8'}
dependencies:
blueimp-md5: 2.19.0
dev: true
/mdurl@1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
dev: false
/mlly@1.3.0:
resolution: {integrity: sha512-HT5mcgIQKkOrZecOjOX3DJorTikWXwsBfpcr/MGBkhfWcjiqvnaL/9ppxvIUXfjT6xt4DVIAsN9fMUz1ev4bIw==}
dependencies:
acorn: 8.8.2
pathe: 1.1.1
pkg-types: 1.0.3
ufo: 1.1.2
dev: true
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/nanoid@3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/p-limit@4.0.0:
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
yocto-queue: 1.0.0
dev: true
/pathe@1.1.1:
resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
dev: true
/pathval@1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
/pkg-types@1.0.3:
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
dependencies:
jsonc-parser: 3.2.0
mlly: 1.3.0
pathe: 1.1.1
dev: true
/postcss@8.4.24:
resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.6
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dependencies:
ansi-regex: 5.0.1
ansi-styles: 5.2.0
react-is: 17.0.2
dev: true
/react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: true
/rollup@3.23.0:
resolution: {integrity: sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/section-matter@1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
dependencies:
extend-shallow: 2.0.1
kind-of: 6.0.3
dev: false
/semver@7.5.1:
resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: true
/siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
dev: true
/source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: true
/sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
dev: false
/stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
/std-env@3.3.3:
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
dev: true
/strip-bom-string@1.0.0:
resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
engines: {node: '>=0.10.0'}
dev: false
/strip-literal@1.0.1:
resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==}
dependencies:
acorn: 8.8.2
dev: true
/time-zone@1.0.0:
resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==}
engines: {node: '>=4'}
dev: true
/tinybench@2.5.0:
resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==}
dev: true
/tinypool@0.5.0:
resolution: {integrity: sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==}
engines: {node: '>=14.0.0'}
dev: true
/tinyspy@2.1.1:
resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==}
engines: {node: '>=14.0.0'}
dev: true
/type-detect@4.0.8:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
dev: true
/uc.micro@1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
dev: false
/ufo@1.1.2:
resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==}
dev: true
/vite-node@0.31.4(@types/node@20.2.5):
resolution: {integrity: sha512-uzL377GjJtTbuc5KQxVbDu2xfU/x0wVjUtXQR2ihS21q/NK6ROr4oG0rsSkBBddZUVCwzfx22in76/0ZZHXgkQ==}
engines: {node: '>=v14.18.0'}
hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.4
mlly: 1.3.0
pathe: 1.1.1
picocolors: 1.0.0
vite: 4.3.9(@types/node@20.2.5)
transitivePeerDependencies:
- '@types/node'
- less
- sass
- stylus
- sugarss
- supports-color
- terser
dev: true
/vite@4.3.9(@types/node@20.2.5):
resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 20.2.5
esbuild: 0.17.19
postcss: 8.4.24
rollup: 3.23.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vitest@0.31.4:
resolution: {integrity: sha512-GoV0VQPmWrUFOZSg3RpQAPN+LPmHg2/gxlMNJlyxJihkz6qReHDV6b0pPDcqFLNEPya4tWJ1pgwUNP9MLmUfvQ==}
engines: {node: '>=v14.18.0'}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@vitest/browser': '*'
'@vitest/ui': '*'
happy-dom: '*'
jsdom: '*'
playwright: '*'
safaridriver: '*'
webdriverio: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@vitest/browser':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
playwright:
optional: true
safaridriver:
optional: true
webdriverio:
optional: true
dependencies:
'@types/chai': 4.3.5
'@types/chai-subset': 1.3.3
'@types/node': 20.2.5
'@vitest/expect': 0.31.4
'@vitest/runner': 0.31.4
'@vitest/snapshot': 0.31.4
'@vitest/spy': 0.31.4
'@vitest/utils': 0.31.4
acorn: 8.8.2
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.7
concordance: 5.0.4
debug: 4.3.4
local-pkg: 0.4.3
magic-string: 0.30.0
pathe: 1.1.1
picocolors: 1.0.0
std-env: 3.3.3
strip-literal: 1.0.1
tinybench: 2.5.0
tinypool: 0.5.0
vite: 4.3.9(@types/node@20.2.5)
vite-node: 0.31.4(@types/node@20.2.5)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
- sass
- stylus
- sugarss
- supports-color
- terser
dev: true
/well-known-symbols@2.0.0:
resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==}
engines: {node: '>=6'}
dev: true
/why-is-node-running@2.2.2:
resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
engines: {node: '>=8'}
hasBin: true
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
dev: true
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yocto-queue@1.0.0:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
dev: true
Source diff could not be displayed: it is too large. Options to address this: view the blob.
import { Command } from "commander";
async function main() {
const program = new Command();
program
.name("fsinfo-transcript-postprocessor")
.description("CLI to post-process markdown transcripts of FSR meetings")
.version("unknown")
.argument(
"<inputDir>",
"path to directory containing the input markdown files"
)
.argument("<outputDir>", "path to output the rendered html to")
.option("-A, --output-ast", "Also save the AST as a file", false)
.action(run);
await program.parseAsync(process.argv);
}
main();
interface Options {
outputAst: boolean;
}
async function run(inputDir: string, outputDir: string, options: Options) {
// (1) Get a list of files to read
const files = await getListOfFiles(inputDir);
// (2) Parse all of them, report errors
if (files.length > 1000) {
throw "Too many files to be read, something is off: " + files.length;
}
console.info("⚙️ Parsing", files.length, "markdown files...");
const parseResults = await Promise.all(
files.map((file) => parseFileWithoutCrashing(file))
);
const errorResults = parseResults.filter((r) => !r.ok && r.data == undefined);
if (errorResults.length > 0) {
console.error(errorResults.length, "transcripts had errors. Aborting.");
process.exit(1);
}
// (3) Generate transcript pages
console.info("✏️ Writing individual HTML files...");
await generateAllTranscriptPages(
outputDir,
parseResults.map((res) => res.data),
options
);
// (4) Generate index page
console.info("✏️ Writing index file...");
await generateIndexPage(
outputDir,
parseResults.map((res) => res.data),
options
);
// (5) Generate resolution list
console.info("✏️ Writing resolutions list file...");
await generateResolutionsPage(
outputDir,
parseResults.map((res) => res.data),
options
);
await fs.writeFile(outputDir + '/bootstrap.min.css', bootstrap, 'utf8');
// (6) Generate JSON of all data
console.info("✅ All done!");
}
import fs from "node:fs/promises";
import { parse } from "./lib/parsing";
import { generateIndexHtml, generateResolutionsHtml, renderTranscriptPageHtml } from "./lib/rendering";
import { bootstrap } from "./css/bootstrap.min.css";
async function getListOfFiles(rootPath: string | undefined): Promise<string[]> {
if (typeof rootPath !== "string") {
throw new TypeError("Missing argument <filesDirectory>");
}
return (await fs.readdir(rootPath))
.filter((path) => path.endsWith(".md"))
.map((path) => rootPath + "/" + path)
.reverse();
}
async function parseFileWithoutCrashing(
path: string
): Promise<{ ok: boolean; data: any; error: any }> {
try {
const fileContent = await fs.readFile(path, "utf-8");
const data = parse(fileContent);
return { ok: true, data: data, error: undefined };
} catch (error) {
console.log("Error for file", path, error);
return { ok: false, data: undefined, error: error };
}
}
async function generateAllTranscriptPages(
outputDir: string,
transcripts: { meta: any; html: string; ast: any }[],
options: Options
) {
// make sure the dir exists and is empty
await fs.mkdir(outputDir, { recursive: true });
await fs.rm(outputDir, {
force: true,
recursive: true,
});
await fs.mkdir(outputDir, { recursive: true });
await Promise.all(
transcripts.map((transcript) =>
generateTranscriptPage(transcript, outputDir, options)
)
);
}
async function generateTranscriptPage(
transcript,
outputDir: string,
options: Options
) {
const { meta, html, ast } = transcript;
const transcriptDir = `fsr-sitzung-${meta.number}-${meta.date}`;
const makeDirPromise = fs.mkdir(outputDir + "/" + transcriptDir, {
recursive: true,
});
const pageHtml = renderTranscriptPageHtml(html, meta);
const htmlFilePath = outputDir + "/" + transcriptDir + "/index.html";
await makeDirPromise;
await fs.writeFile(htmlFilePath, pageHtml, "utf-8");
if (options.outputAst) {
const astFilePath = outputDir + "/" + transcriptDir + "/ast.json";
await fs.writeFile(astFilePath, JSON.stringify(ast, null, 2), "utf-8");
}
}
async function generateIndexPage(outputDir: string, parseResults: { meta: any; html: string; ast: any }[], options: Options) {
const html = generateIndexHtml(parseResults);
const htmlFilePath = outputDir + "/index.html";
await fs.writeFile(htmlFilePath, html, 'utf8');
}
async function generateResolutionsPage(outputDir: string, parseResults: { meta: any; html: string; ast: any }[], options: Options) {
const html = generateResolutionsHtml(parseResults);
const htmlFilePath = outputDir + "/resolutions.html";
await fs.writeFile(htmlFilePath, html, 'utf8');
}
export function getMetadataProblems(metaData: Record<string, any>) {
const warnings: string[] = [];
const errors: string[] = [];
// Check spec_version
if (typeof metaData["spec_version"] === "undefined") {
errors.push(
"Metadata is missing attribute 'spec_version'. Suggested fix: Insert 'spec_version: \"2\"' into the metadata-block "
);
} else if ((""+metaData["spec_version"]) !== "2") {
errors.push(
"Metadata attribute 'spec_version' should always be 2, because the current CI can't handle other version. Suggested fix: Replace 'spec_version: " +
metaData["spec_version"] +
"' with 'spec_version: \"2\"'."
);
}
// Check lang
if (typeof metaData["lang"] !== "string") {
warnings.push(
"Metadata is missing attribute 'lang'. This influences some metadata and might end up in resulting documents. Suggested fix: Insert 'lang: \"de\"' into the metadata-block."
);
}
// Check title
if (
typeof metaData["title"] !== "string" ||
metaData["title"].trim().length === 0
) {
errors.push(
"Metadata is missing attribute 'title'. Used by website-generator to display a propper title. Suggested fix: Insert a valid title into the metadata-block."
);
}
// Check date
if (
metaData["date"] === undefined ||
(typeof metaData["date"] === "string" &&
metaData["date"].trim().length === 0)
) {
errors.push(
"Metadata is missing attribute 'date'. You absolutely need a valid date at which the transcript was initiall written. Suggested fix: Insert a valid date into the metadata-block in the format of 'date: \"YYYY-MM-DD\"', e.g.'date: \"2015-02-28\"'"
);
} else if (
typeof Date.parse(metaData["date"]) !== "number" ||
isNaN(Date.parse(metaData["date"]))
) {
errors.push(
"Metadata attribute 'date' ('" +
metaData["date"] +
"')is not a valid date. You absolutely need a valid date at which the transcript was initiall written. Suggested fix: Replace 'date: \"" +
metaData["date"] +
"\"' with a valid entry in the format of 'date: \"YYYY-MM-DD\"', e.g.'date: \"2015-02-28\"'"
);
}
// Check number
if (typeof metaData["number"] === "undefined") {
errors.push(
"Metadata is missing attribute 'number'. Fachschaft Informatik TU Dortmunds requires a valid transcript number. Suggested fix: Insert a number into the metadata-block in the format of 'number: \"N\"', e.g.'number: \"404\"'"
);
} else if (
typeof parseInt(metaData["number"], 10) !== "number" ||
isNaN(parseInt(metaData["number"], 10)) ||
(""+metaData["number"]).search(/[^\d]/g) !== -1
) {
errors.push(
"Metadata attribute 'number' must be a valid transcript number. Suggested fix: Replace 'number: \"" +
metaData["number"] +
"\"' with a valid number-entry in the format of 'number: \"N\"', e.g.'number: \"404\"'"
);
}
// Check start
if (typeof metaData["start"] !== "string") {
errors.push(
"Metadata is missing attribute 'start'. Suggested fix: Insert a valid start time into the metadata-block in the format of 'start: \"hh:mm\"', e.g.'start: \"22:15\"'"
);
} else if (!isValidGermanTimeStamp(metaData["start"])) {
errors.push(
"Metadata attribute 'start' must be a valid start time. Suggested fix: Replace 'start: \"" +
metaData["start"] +
"\"' with a valid start-entry in the format of 'start: \"hh:mm\"', e.g.'start: \"22:15\"'"
);
}
// Check end
if (typeof metaData["end"] !== "string") {
errors.push(
"Metadata is missing attribute 'end'. Suggested fix: Insert a valid end time into the metadata-block in the format of 'end: \"hh:mm\"', e.g.'end: \"22:45\"'"
);
} else if (!isValidGermanTimeStamp(metaData["end"])) {
errors.push(
"Metadata attribute 'end' must be a valid end time. Suggested fix: Replace 'end: \"" +
metaData["end"] +
"\"' with a valid end-entry in the format of 'end: \"hh:mm\"', e.g.'end: \"22:45\"'"
);
}
// Check that end is after start
if (
isValidGermanTimeStamp(metaData["start"]) &&
isValidGermanTimeStamp(metaData["end"]) &&
!isSecondTimeGreaterThanFirst(metaData["start"], metaData["end"])
) {
errors.push(
"Metadata attribute 'end' ('" +
metaData["end"] +
"') must be a later time than 'start' ('" +
metaData["start"] +
"')."
);
}
// Check author
if (typeof metaData["author"] !== "string") {
errors.push(
"Metadata is missing attribute 'author'. Suggested fix: Insert a valid author into the metadata-block in the format of 'author: \"Your name\"'"
);
}
// Check haed
if (typeof metaData["head"] !== "string") {
errors.push(
"Metadata is missing attribute 'head'. Suggested fix: Insert a valid head name (or -) into the metadata-block in the format of 'head: \"Name of chairman\"'"
);
}
// check present
if (metaData["present"] === undefined) {
errors.push(
"Metadata is missing attribute 'present'. Suggested fix: Insert a list of people who were present into the metadata-block in the format of 'present: [\"Name of person1\", ...]'"
);
} else if (!Array.isArray(metaData["present"])) {
errors.push(
"Metadata attribute 'present' should be an Array of names, even if only one person is present. Suggested fix: Make sure that 'present' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
} else if (!metaData["present"].every((entry) => typeof entry === "string")) {
errors.push(
"Metadata attribute 'present' should be an Array of names (which are strings), even if only one person is present. Suggested fix: Make sure that 'present' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
}
// check absent
if (metaData["absent"] === undefined) {
errors.push(
"Metadata is missing attribute 'absent'. Suggested fix: Insert a list of people who were absent into the metadata-block in the format of 'absent: [\"Name of person1\", ...]'"
);
} else if (!Array.isArray(metaData["absent"])) {
errors.push(
"Metadata attribute 'absent' should be an Array of names, even if only one person is absent. Suggested fix: Make sure that 'absent' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
} else if (!metaData["absent"].every((entry) => typeof entry === "string")) {
errors.push(
"Metadata attribute 'absent' should be an Array of names (which are strings), even if only one person is absent. Suggested fix: Make sure that 'absent' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
}
// check guests
if (metaData["guests"] === undefined) {
errors.push(
"Metadata is missing attribute 'guests'. Suggested fix: Insert a list of people who were guests into the metadata-block in the format of 'guests: [\"Name of person1\", ...]'"
);
} else if (!Array.isArray(metaData["guests"])) {
errors.push(
"Metadata attribute 'guests' should be an Array of names, even if only one person is attending as a guest. Suggested fix: Make sure that 'guests' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
} else if (!metaData["guests"].every((entry) => typeof entry === "string")) {
errors.push(
"Metadata attribute 'guests' should be an Array of names (which are strings), even if only one person is guests. Suggested fix: Make sure that 'guests' is a yaml-Array of names (See: https://www.w3schools.io/file/yaml-arrays/)"
);
}
return {
warnings,
errors,
};
}
function isValidGermanTimeStamp(input: string) {
if (typeof input !== "string") return false;
if (input.search(/^(\d\d:\d\d)$/g) === -1) return false;
const hours = parseInt(input.split(":")[0], 10);
if (hours < 0 || hours > 23) return false;
const minutes = parseInt(input.split(":")[1], 10);
if (minutes < 0 || minutes > 59) return false;
return true;
}
function isSecondTimeGreaterThanFirst(firstTimeStr: string, secondTimeStr: string) {
const firstTime =
parseInt(firstTimeStr.split(":")[0], 10) * 100 +
parseInt(firstTimeStr.split(":")[1], 10);
const secondTime =
parseInt(secondTimeStr.split(":")[0], 10) * 100 +
parseInt(secondTimeStr.split(":")[1], 10);
return firstTime < secondTime;
}
\ No newline at end of file
import { expect, describe, it, test } from "vitest";
import { parse } from "./parsing";
import {writeFileSync} from 'fs';
const exampleMarkdown = `
---
start: "xx"
---
TEST_MARKDOWN_BEFORE
[
Der FSR beschließt, X
]{.resolution .fin money-granted="150,00" yes=6 no=0 abstention=1 result=Angenommen}
TEST_MARKDOWN_AFTER
---
end: "yy"
---
`.trim();
const fullExampleMarkdown = `
---
spec_version: 2
lang: de
title: FSR-Protokoll 647 (11.04.2023)
date: "2023-04-11"
number: 647
start: "16:18"
author: "Falk Rehse"
resolutions: ""
head: "Marcel Morczinek"
present:
- Caroline Livia Baumann
- Fabian Winter
- Falk Rehse
- Jasmin Hankel
- Lukas Kidin
- Marcel Morczinek
- Nikan Roosta Azad
absent:
- Denis Pekdemir
- Marlon Bagans
- Nis Weber
- Philipp Lehnert
- Viktoria Braune
guests:
- Dominik Riemer
---
<!-- *nicht* entfernen, wenn beschlussfähig -->
**Die Beschlussfähigkeit wird festgestellt.**
# Post {start="16:18"}
_Keine relevante Post._
# Mails {start="16:19"}
# Berichte {start="16:22"}
## Teams
- Vorstand
- *Bericht zu Kartenterminals in eigenem TOP*
- Studienberatung (Marlon Bagans)
- *nichts relevantes zu berichten*
- Öffentlichkeit (Falk Rehse)
- *nichts relevantes zu berichten*
- Kommunikation (Lukas Kidin)
- es soll eine AG geben, die sich um den Discord kümmert
- Events (Falk Rehse)
- eine erste FooBar hat stattgefunden, es waren viele Gäste da
- diese Woche findet die Karaoke statt
- nächste Woche dann die nächste FooBar
- Admins
- neue Monitore werden im Büro montiert
- Protokolle (Fabian Winter)
- *nichts relevantes zu berichten*
- FsRK (Marlon Bagans)
- die nächste ist am 19.04.
# Bücher fürs CZI für 120 € {start="16:28" origin="Jonas Zohren" issue="308" .fin}
**Vorstellung:**
Der FSR ist ja auch für kulturelle Belange zuständig. Es wird daher vorgeschlagen, ein paar nette Bücher zum Blättern für das CZI anzuschaffen. Z.B. Randall Munroe\'s What If\'s, Thing Explainer, Commitstrip, etc.
**Ziel/Gewünschte Entscheidung am Ende der Diskussion/des TOPs:**
120 € um mal 4 bis 5 Bücher anzuschaffen und die Reaktion zu evaluieren.
Jonas würde die kaufen, labeln und ins CZI stellen
**Beschlussvorlage:**
> Der FSR stellt 120 € für Bücher zur Verfügung
**Diskussion vor der Sitzung:**
- Matthias Schaffartzik:
Heyho,
Zuerst find ich die Idee toll, danke euch dafür :)
Als jemand der Bücher durchaus gerne auf deutsch statt auf englisch liest, fände ich es auch cool, wenn es dann auch Bücher auf deutsch zu lesen käme. Vielleicht wäre es für die Evaluation interessant, beides zu mischen und zu schauen, ob es sich lohnt, englische UND deutsche Bücher da zu haben?
- Jonas Zohren:
- Jonas Buchliste für den Bereich „Kultur"
- 7 € für What If 1
- 12 € für What If 2
- 18 € für Thing Explainer
- **Summe**: 37 € für alle als Softcover
\- Daher gerne lieber 60 € beschlossen, falls es irgendwie mal nicht als Softcover klappt
**Diskussion:**
**aufgrund geringer Anwesenheitsquote verschoben auf nächste Sitzung**
# Neuer Sitzungstermin {start="16:29" origin="Marcel Morczinek" issue="311"}
**Vorstellung:**
Es soll ein neuer Sitzungstermin bestimmt werden. Eventuelle neue Mitlgieder im FSR sollen berücksichtigt werden. Die folgende Umfrage wird dafür verwendet:
<https://bitpoll.mafiasi.de/poll/BU6vzNkD/>
**Ziel/Gewünschte Entscheidung am Ende der Diskussion/des TOPs:**
Ein neuer Sitzungstermin wird bestimmt ODER es wird ein Tag bestimmt, bis zu dem abgestimmt werden soll.
**Diskussion:**
**Meinungsbild:** Sind wir mit Sitzungstermin Freitags, 16 Uhr, ab dem 21. einverstanden?
- Zustimmung: 7
- Ablehnung: 0
- Enthaltung: 1
# Kartenlesegeräte {start="16:39" origin="Marcel Morczinek" issue="312" .fin}
**Vorstellung:**
Es sollen Kartenlesegeräte angeschafft werden, um bargeldlose Zahlungen in der Zukunft zu ermöglichen.
Folgendes Gerät soll angeschafft werden, welches vom AStA empfohlen wurde:
<https://www.sumup.com/de-de/solo-kartenlesegeraet/>
Ein Gerät soll für den FSR angeschafft werden, um in Zukunft Events bargeldlos zu machen.
Ein zweites Gerät soll für den Kiosk angeschafft werden. Aufgrund der Liquidität und der Zweckgebundenheit, soll das Gerät für den Kiosk von den Kioskfinanzen getragen werden.
Einzelkosten sind 80€. Bei zwei Geräten sind die Gesamtkosten 160€, welche von verschiedenen Töpfen getragen werden sollen.
**Ziel/Gewünschte Entscheidung am Ende der Diskussion/des TOPs:**
Werden die vorgeschlagenen Geräte angeschafft oder soll auf andere Modelle ausgewichen werden?
Welches Gerät will der Kiosk haben, welches Gerät will der FSR haben?
**Beschlussvorlage:**
Der FSR beschließt ein Kartenlesegerät im Wert von 80€ für den FSR, und ein Kartenlesegerät im Wert von 80€ für den Kiosk anzuschaffen.
**Diskussion vor der Sitzung:**
- Felix Strick:
Soweit ich das sehe kommen auf die 80€ noch Mehrwertsteuer.
Somit sind das also ca. 95€
- Hendrik Fuchs:
Immer mehr Geld beantragen als man tatsächlich braucht. Es kann jederzeit sein, dass man Versandkosten, Mehrwertsteuer oder ähnliches übersehen hat. Und einen Nachbeschluss über 4,95€ Versand braucht niemand.
Desweiteren: Ich sehe auf der Seite noch Transaktionsgebühren. Wer trägt diese und wie wird es abgerechnet? Das sollte vorher besprochen sein.
**Diskussion:**
- Lukas Kidin:
- es sollte auf Events auch die Möglichkeit geben, Bar zu bezahlen
- es sollte eine Evaluationsphase geben
- wie ist die Konnektivität / Stabilität
- wie ist die Geschwindigkeit der Transaktionen
- Jasmin Hankel:
- es gibt auch das SumUp [Kassensystem lite+Air](https://www.sumup.com/de-de/kassensystem-uebersicht/kassensystem-lite/), welches direkt eine Barkasse integriert
- Transaktionsgebühren:
- 0,9 % bei Debitkarten
- 1,9 % bei Kreditkarten
- keine monatlichen Kosten
- das Kassensystem würde auch die Arbeit der Helfenden vereinfachen
- Nikan Roosta Azad
- das Kassensystem würde es auch ermöglichen, hinterher eine Übersicht zu generieren, was verkauft wurde
- *wir beschließen heute nur das Solo für den Kiosk, und warten die Erfahrungen ab, bevor wir ein weiteres Gerät für die Fachschaft anschaffen*
[
Der FSR beschließt, dass der Kiosk für bis zu 150 € ein SumUp Solo anschaffen darf.
]{.resolution .fin money-granted="150,00" yes=6 no=0 abstention=1 result=Angenommen}
# Nicht-Öffentlicher Teil der Sitzung {start="17:17"}
# Discord-AG {start="17:17" origin="Lukas Kidin"}
- die Verwaltung des Discord soll in eine AG ausgelagert werden
<br />
- Lukas Kidin
- dadurch hätten wieder Privatpersonen die Verantwortung für den Discord
- was passiert, wenn alle aus der AG austreten?
- Caroline Livia Baumann
- es gibt die Möglichkeit, einen Funktionsaccount als Owner des Discord festzulegen
- Nikan
- mit einer AG wäre die Zuständigkeit klarer geregelt
- Jasmin Hankel
- die AG soll gegründet werden, damit es einen Discord gibt
- der FSR hat offenbar kein Interesse, sich um einen Discord zu kümmern
- Lukas Kidin
- dann sollte da auch nicht Logo / Name der Fachschaft drin stehen
- der Discord sollte also nicht der "offizielle" Discord der Fachschaft sein
- Jasmin Hankel
- ja, der Discord ist dann nur inoffiziell
- er ist trotzdem Teil der Fachschaft
- Caroline Livia Baumann
- eine Auslagerung in eine AG sollte Datenschutzprobleme umgehen
- Marlon Bagans
- wäre ein Infokanal für den FSR in diesem Discord *technisch* möglich?
- ja
- Lukas Kidin
- **Verfahrensvorschlag:** in der nächsten Sitzung stimmen wir darüber ab, ob es einen offiziellen Discord des FSR geben soll
# Sonstiges {start="17:31"}
- Jasmin Hankel: Discord-Server
- ein Discord-Server kann im Zweifel auch von Studis gegründet werden
- Erstis machen das sowieso
# ToDos {start="17:33"}
## Alt
- Jasmin Hankel: Zuständigkeit für den FS Discord klären (In Arbeit, [meta#154][m154])
- Marlon Bagans: AG für Kommunikation mit Studis (In Arbeit, [meta#153][m153])
- Lukas Kidin: Sommerfest organisieren (In Arbeit, [meta#152][m152])
- Fabian Winter: Klausurtagung organisieren (ausgesetzt, bis nach der nächsten FVV, [meta#143][m143])
- Philipp Lehnert: Software/Hardware für das Smart-Board organisieren (In Arbeit, [meta#139][m139])
- unbesetzt: Neuer Infoscreen (In Arbeit, [meta#61][m61])
- Marcel Morczinek: Arbeitet an der Satzungsänderung mit Caro
- Marlon Bagans, Fabian Winter und Lukas Kidin FVV vorbereiten
## Neu
# News {start="17:35"}
- Upgrade zum 49 € jeweils zum Monatsanfang für 12,39 € in der VRR-App verfügbar
---
end: "17:36"
---
<!-- Expanded Links: -->
[m154]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/154
[m153]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/153
[m152]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/152
[m144]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/144
[m143]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/143
[m139]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/139
[m61]: https://gitlab.fachschaften.org/tudo-fsinfo/fsr/meta/issues/61
[t5054]: https://zammad.oh14.de/#ticket/zoom/5054
[t5053]: https://zammad.oh14.de/#ticket/zoom/5053
`.trim();
describe('Parsing', () => {
it('parses', () => {
const {meta, html} = parse(fullExampleMarkdown);
writeFileSync('./scratchpad/out.html', html, 'utf-8');
})
})
\ No newline at end of file
import matter from "gray-matter"; // Front matter yaml metadata parsing
import md from "markdown-it"; // Markdown parsing
import bracketed_spans_plugin from "markdown-it-bracketed-spans"; //attributed spans (`[blah]{.fin yes=1)}`)
import attributes from "markdown-it-attrs";
import emoji_plugin from "markdown-it-emoji"; // `:smile:` -> `😊`
import footnote_plugin from "markdown-it-footnote"; // for `[^1]` and friends
import abbreviation from "markdown-it-abbr";
import definitionList from "markdown-it-deflist";
import inserted from "markdown-it-ins";
import marked from "markdown-it-mark";
import subscript from "markdown-it-sub";
import superscript from "markdown-it-sup";
import container from "markdown-it-container";
import Token from "markdown-it/lib/token";
import { getMetadataProblems } from "./metadataChecks";
import { renderResolutionToHtml } from "./rendering";
const markdownParser = md({
html: true,
xhtmlOut: true,
breaks: true,
linkify: true,
typographer: true,
})
.use(bracketed_spans_plugin)
.use(attributes)
.use(emoji_plugin)
.use(footnote_plugin)
.use(abbreviation)
.use(definitionList)
.use(inserted)
.use(marked)
.use(subscript)
.use(superscript)
.use(container); // missing: rendering alert divs via bootstrap
markdownParser.linkify.set({ fuzzyEmail: false });
/**
* Parse single transcript/meeting minutes from markdown
* @param markdown Raw markdown from the HedgeDoc pad used
*/
export function parse(markdown: string) {
let { data, content } = matter(markdown, { language: "yaml" });
// Evil hack:
// Pandoc (used previously) allowed multiple front matters and this was employed to mark the end time at the bottom of the document.
// gray-matter does not allow this, so we cut out everything leading up to `---\nend:`, so that the second meta data
// is now at the beginning of the string again.
const startOfEndBlock = content.indexOf("---\nend:");
if (startOfEndBlock > -1) {
const endData = matter(content.slice(startOfEndBlock), {
language: "yaml",
}).data;
data = { ...data, ...endData };
content = content.replace(/---\nend:.+?\n---/m, "");
}
// First off, check metadata:
const { warnings, errors } = getMetadataProblems(data);
if (errors.length > 0) {
throw errors.join("\n");
}
// From now on, assume metadata is valid
const markdownAstTokens = markdownParser.parse(content, {});
const nextResoNumbers = {
B: 1,
F: 1,
P: 1,
};
const resolutions: Resolution[] = [];
for (let tokenIdx = 0; tokenIdx < markdownAstTokens.length; tokenIdx++) {
const token = markdownAstTokens[tokenIdx];
// Handle resolutions:
if (token.type === "inline" && token.children?.[0]?.type === "span_open") {
const resolution = extractResolution(token);
if (resolution) {
resolution.number =
data.number +
"." +
nextResoNumbers[resolution.type] +
resolution.type;
nextResoNumbers[resolution.type] += 1;
resolution.date = data.date;
const resolutionToken = new Token("html_block", "div", 0);
resolutionToken.content = renderResolutionToHtml(resolution);
markdownAstTokens[tokenIdx] = resolutionToken;
resolutions.push(resolution);
}
}
// Handle headers
if (token.type === "heading_open") {
// TODO:
}
}
data.resolutions = resolutions;
const renderedHtml = markdownParser.renderer.render(
markdownAstTokens,
markdownParser.options,
{}
);
return {
meta: data,
html: renderedHtml,
ast: markdownAstTokens,
};
}
export interface Resolution {
date?: string;
number: string;
type: "B" | "F" | "P";
result: string;
text: string;
money_granted?: string;
votes: {
yes: number;
no: number;
abstention: number;
};
}
function extractResolution(token: Token): Resolution | null {
if (!Array.isArray(token?.children) || token.children.length < 1) {
throw TypeError(
"Resolution parsing requires an inline span with at least one child"
);
}
const spanAttributes = new Map(token.children[0].attrs);
if (!spanAttributes.get("class")?.split(" ")?.includes("resolution")) {
// no resolution class given -> bail gracefully, as this is simply no resultion
return null;
}
let type: Resolution["type"] = "B"; // regulärer Beschluss
if (spanAttributes.get("class")?.split(" ")?.includes("fin")) {
type = "F"; // Finanzbeschluss
} else if (spanAttributes.get("class")?.split(" ")?.includes("transcript")) {
type = "P"; // Protokollbeschluss
}
const rawYes = spanAttributes.get("yes")?.trim();
let yes = NaN;
if (!rawYes || typeof rawYes !== "string" || rawYes.length === 0) {
throw new TypeError(
"Beschluss muss ein `yes`-Attribut (Ja-Stimmen) enthalten"
);
}
yes = parseInt(rawYes, 10);
const rawNo = spanAttributes.get("no")?.trim();
let no = NaN;
if (!rawNo || typeof rawNo !== "string" || rawNo.length === 0) {
throw new TypeError(
"Beschluss muss ein `no`-Attribut (Nein-Stimmen) enthalten"
);
}
no = parseInt(rawNo, 10);
const rawAbstention = spanAttributes.get("abstention")?.trim();
let abstention = NaN;
if (
!rawAbstention ||
typeof rawAbstention !== "string" ||
rawAbstention.length === 0
) {
throw new TypeError(
"Beschluss muss ein `abstention`-Attribut (Enhaltungen) enthalten"
);
}
abstention = parseInt(rawAbstention, 10);
const rawResult = spanAttributes.get("result")?.trim();
let result = "";
if (!rawResult || typeof rawResult !== "string" || rawResult.length === 0) {
throw new TypeError(
"Beschluss muss ein `result`-Attribut (Ergebnis, 'Angenommen' oder 'Abgelehnt') enthalten"
);
}
result = rawResult;
const rawMoneyGranted = spanAttributes.get("money-granted")?.trim();
let moneyGranted: string | undefined = undefined;
if (
!rawMoneyGranted ||
typeof rawMoneyGranted !== "string" ||
rawMoneyGranted.length === 0
) {
// do not care, must not exist, but precondition for existing elements
} else if (rawMoneyGranted.split(",").length === 2) {
moneyGranted = rawMoneyGranted;
}
const resolutionText = token.children
.filter((token) => token.type === "text")
.map((token) => token.content)
.join(" ")
.trim();
return {
date: "",
number: "???",
type: type,
result: result,
text: resolutionText,
votes: {
yes: yes,
no: no,
abstention: abstention,
},
money_granted: moneyGranted,
};
}
import { Resolution } from "./parsing";
export function renderResolutionToHtml(resolution: any): string {
return `<div class="card mb-3 text-center" id="${resolution.number}">
<div class="card-header" >Beschluss <code>${
resolution.number
}</code></div>
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">${resolution.text}</p>
<div class="container container-sm">
<div class="row align-items-center">
<div class="col">
<span class="p-1 border-bottom border-success border-2">
Ja: ${resolution.votes.yes}
</span>
</div>
<div class="col">
<span class="p-1 border-bottom border-danger border-2">
Nein: ${resolution.votes.no}
</span>
</div>
<div class="col">
<span class="p-1 border-bottom border-info border-2">
Enthaltung: ${resolution.votes.abstention}
</span>
</div>
</div>
</div>
</div>
<div class="card-footer ${
resolution.result === "Angenommen" ? "bg-success" : "bg-danger"
} bg-opacity-25">
${resolution.result}
</div>
</div>`;
}
export function renderTranscriptPageHtml(
transcriptHtml: string,
meta: any
): string {
return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${meta.title}</title>
<link href="../bootstrap.min.css" rel="stylesheet">
<style>
div:target {
border-width: 0.4rem;
border-color: #f4c4aa;
scroll-margin-top: 4rem;
}
</style>
</head>
<body class="bg-body">
<div>
<header>
<nav class="navbar navbar-expand bg-body-tertiary">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="..">Protokolle</a>
</li>
<li class="nav-item">
<a class="nav-link" href="../resolutions.html">Beschlüsse</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
</li>
</ul>
</nav>
</header>
</div>
<div class="container mt-2 mb-2">
<div class="bg-body-tertiary">
<article class="p-4">
${transcriptHtml}
</article>
</div>
</div>
</body>
</html>`;
}
export function generateIndexHtml(
parseResults: { meta: any; html: string; ast: any }[]
): string {
return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FSR-Protokolle</title>
<link href="./bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="">
<header>
<nav class="navbar navbar-expand bg-body-tertiary">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Protokolle</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./resolutions.html">Beschlüsse</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
</li>
</ul>
</nav>
</header>
</div>
<div class="container mt-2 mb-2">
<ul>
${parseResults
.map(
(t) =>
`<li><a href="./fsr-sitzung-${t.meta.number}-${t.meta.date}">FSR-Sitzung ${t.meta.number} (${t.meta.date})</a></li>`
)
.join("\n")}
</ul>
</div>
</body>
</html>`;
}
export function generateResolutionsHtml(
transcripts: { meta: any; html: string; ast: any }[]
): string {
return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FSR-Protokolle</title>
<link href="./bootstrap.min.css" rel="stylesheet">
<style>
tr:target {
border-width: 0.4rem;
border-color: #f4c4aa;
scroll-margin-top: 4rem;
}
</style>
</head>
<body>
<div class="">
<header>
<nav class="navbar navbar-expand bg-body-tertiary">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="./index.html">Protokolle</a>
<i class="fa fa-link"></i> </li>
<li class="nav-item">
<a class="nav-link" href="#">Beschlüsse</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
</li>
</ul>
</nav>
</header>
</div>
<div class="container mt-2 mb-2">
<table class="table">
<thead>
<tr>
<th>
Datum
</th>
<th>
Nummer
</th>
<th>
Text
</th>
<th>
Ja/Nein/Enth.
</th>
<th>
Ergebnis
</th>
<th>
⚓️
</th>
</tr>
</thead>
<tbody>
${transcripts
.map((t) =>
t.meta.resolutions.map((r: Resolution) =>
generateResolutionRowHtml(r)
)
)
.flat()
.join("\n")}
</tbody>
</table>
</div>
</body>
</html>`;
}
function generateResolutionRowHtml(resolution: Resolution): string {
return `<tr id="${resolution.number}">
<td>
${resolution.date}
</td>
<td>
<a href="./fsr-sitzung-${resolution.number.split(".")[0]}-${
resolution.date
}/#${resolution.number}">
${resolution.number}
</a>
</td>
<td>
${resolution.text}
</td>
<td>
${resolution.votes.yes} / ${resolution.votes.no} / ${
resolution.votes.abstention
}
</td>
<td>
${resolution.result}
</td>
<td>
<a href="#${resolution.number}">
🔗
</a>
</td>
</tr>`;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment