Skip to content
This repository was archived by the owner on Sep 18, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8fea8cd
Add bash script o extract examples from the docs.
Potherca Aug 23, 2024
48e4b58
Add examples extracted from the docs.
Potherca Aug 23, 2024
a509a2b
Add HTML page to display examples in.
Potherca Aug 23, 2024
ec803ff
Add styling for examples and their output.
Potherca Aug 23, 2024
8459d74
Add logic for loading examples and showing their output.
Potherca Aug 23, 2024
fa1e948
Add logic to load scripts in order.
Potherca Aug 23, 2024
cffc530
Code cleanup.
Potherca Aug 23, 2024
62e1cd0
Add more entries to the import map.
Potherca Aug 23, 2024
dfc21ff
Add logic to load MetroJS for examples that do not do so themselves.
Potherca Aug 23, 2024
6aa98de
Add logic to ignore Cloudflare injected scripts.
Potherca Aug 24, 2024
3871d02
Add logic to mark MetroJS script as loaded.
Potherca Aug 24, 2024
a2a29d3
Code cleanup.
Potherca Aug 24, 2024
99cd81d
Add prefetch meta for performance.
Potherca Aug 24, 2024
77f4586
Change title when an example is selected.
Potherca Aug 24, 2024
be56536
Add styling so the menu works on mobile.
Potherca Aug 24, 2024
de21b98
Fix bug caused by missing null check.
Potherca Aug 24, 2024
9ee1e63
Change styling for mobile menu
Potherca Aug 24, 2024
2099807
Add button for menu on mobile.
Potherca Aug 24, 2024
d2fd438
Add aria labels and logic to mobile menu button.
Potherca Aug 24, 2024
b6f75c1
Change CSS to be cleaner
Potherca Aug 24, 2024
6c25f62
Add support for dark theme.
Potherca Aug 24, 2024
1653c66
Add viewport meta for mobile friendliness.
Potherca Aug 24, 2024
9ee73a1
Change name logic to make cleaner name.
Potherca Aug 24, 2024
9a05fcd
Add logic to highlight selected menu item and parent.
Potherca Aug 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions bin/extract-examples.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/usr/bin/env bash

set -o errexit # Exit script when a command exits with non-zero status.
set -o errtrace # Exit on error inside any functions or sub-shells.
set -o nounset # Exit script on use of an undefined variable.
set -o pipefail # Return exit status of the last command in the pipe that exited with a non-zero exit code

# ==============================================================================
#/ Extract HTML and JS code examples from Markdown files
# ------------------------------------------------------------------------------
#/
#/ Usage: $0 [-h] <path>
#/
#/ Where:
#/ <path> The path to the Markdown files to extract examples from
#/
#/ Options:
#/ -h|--help Print this help dialogue and exit
#/
#/ This script will recursively extract all the code examples from the Markdown
#/ files found in the given path. The extracted examples will be saved in a
#/ directory called 'examples' in the same directory as the Markdown file(s).
#/
#/ The script will look for code blocks that start with "```javascript" or
#/ "```html". It will extract the content of the code block and save it in a
#/ file with the same name as the Markdown file but with a number at the end and
#/ a .js or .html extension (depending on the code block).
#/
#/ For example, if the Markdown file is called 'example.md', the extracted
#/ examples will be saved in 'examples/example.1.js', 'examples/example.2.js',
#/
#/ Usage example:
#/
#/ $0 ./docs

: "${OUTPUT_ERROR:=/dev/stderr}"
: "${OUTPUT_NORMAL:=/dev/stdout}"

: readonly "${COLOR_RED:=$(tput setaf 1)}"
: readonly "${COLOR_WHITE:=$(tput setaf 7)}"
: readonly "${RESET_TEXT:=$(tput sgr0)}" # turn off all attributes
: readonly "${TEXT_BOLD:=$(tput bold)}" # turn on bold (extra bright) mode
: readonly "${TEXT_DIM:=$(tput dim)}" # turn on half-bright mode
: readonly -i "${EXIT_NOT_ENOUGH_PARAMETERS:=65}"

error(){
if [ ${#} == 1 ];then
logMessage "ERROR " "${COLOR_RED}" "${@}" "Call with ${TEXT_BOLD}--help${RESET_TEXT}${TEXT_DIM} for more information." >&2
else
logMessage "ERROR " "${COLOR_RED}" "${@}" >&2
fi
}

logMessage(){
local sType="${1?Three parameters required: <type> <color> <message>}"
local sColor="${2?Three parameters required: <type> <color> <message>}"
local sMessage="${3?Three parameters required: <type> <color> <message>}"

message "${COLOR_WHITE}[${sColor}${sType}${COLOR_WHITE}]${RESET_TEXT}" "${sMessage}"

# Each additional parameter will be treated as extra information to display with the error
if [[ "$#" -gt 3 ]]; then
shift 3
for sMessage in "$@"; do
message " " "${TEXT_DIM}${sMessage}${RESET_TEXT}"
done
fi
}

message(){
local sPrefix="${1?Two parameters required: <message-prefix> <message>}"
local sMessage="${2?Two parameters required: <message-prefix> <message>}"

echo -e "${sPrefix} ${sMessage}"
}

statusMessage() {
message " -----> " "${@}"
}

topicMessage() {
message " =====> " "${@}"
}

usage() {
local sFile sScript sUsage

readonly sFile="${0}"

sScript="$(basename "${sFile}")"
sUsage="$(grep '^#/' < "${sFile}" | cut -c4-)"

readonly sScript sUsage

echo -e "${sUsage//\$0/${sScript}}\n" >> /dev/stdout
}

extract_examples() {
local -i iCounter
local sBaseName sDirname sFile sPath sExamplePath


if [[ ${#@} -lt 1 ]]; then
error "One parameter required: <path>"
exit "${EXIT_NOT_ENOUGH_PARAMETERS}"
fi


if [[ "$1" == '-h' || "$1" == '--help' ]];then
usage
return 0
fi

local sPath="${1?One parameter required: <path>}"

sPath="$(realpath "${sPath}")"
sExamplePath="${sPath}/examples"

topicMessage "Extracting examples from ${sPath} to ${sExamplePath}"

while IFS= read -r -d '' sFile; do
iCounter=0
sDirname="$(dirname "${sFile}")"
sDirname="${sDirname#"${sPath}"}"
sBaseName="$(basename "${sFile}" '.md')"

# shellcheck disable=SC2016
grep -ozP '```(javascript|html)(.|\n)+?```' < "${sFile}" | while IFS= read -r sLine; do
local sFilename sExtension

if grep -qP '```(javascript|html)' <<< "${sLine}"; then
iCounter=$((iCounter + 1))
statusMessage "Found example ${iCounter} in ${sDirname}/${sBaseName}.md"
sExtension="$(grep -oP '```(javascript|html)' <<< "${sLine}" | cut -c 4-)"
if [ "${sExtension}" == "javascript" ]; then
sExtension="js"
fi

sFilename="${sExamplePath}${sDirname}/${sBaseName}.${iCounter}.${sExtension}"

if [ ! -d "$(dirname "${sFilename}")" ]; then
mkdir -p "$(dirname "${sFilename}")"
fi

echo '' > "${sFilename}"
else
echo "${sLine}" >> "${sFilename}"
fi
done
done < <(find "${sPath}" -type f -name '*.md' -print0)
}

if [ "$0" != "${BASH_SOURCE[0]}" ]; then
export -f extract_examples
else
extract_examples "${@}"
fi
17 changes: 17 additions & 0 deletions docs/examples/asserts.1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

let error

if (error = assert.fails(url, {
searchParams: {
response_type: 'code',
client_id: 'mockClientId',
state: assert.optional(/.+/)
}
})) {
return metro.response({
url: req.url,
status: 400,
statusText: 'Bad Request',
body: '400 Bad Request'
})
}
6 changes: 6 additions & 0 deletions docs/examples/asserts.2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

assert.check(oauth2, {
client_id: /.+/,
authRedirectURL: /.+/,
scope: /.*/
})
2 changes: 2 additions & 0 deletions docs/examples/asserts.3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

metro.assert.enable()
10 changes: 10 additions & 0 deletions docs/examples/debugging.1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

metro.trace.add('debug', {
request: (req) => {
if (req.searchParams.has('foo')) {
debugger;

}

}
})
2 changes: 2 additions & 0 deletions docs/examples/debugging.2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

metro.trace.add('group', metro.trace.group())
180 changes: 180 additions & 0 deletions docs/examples/example-helper.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
title {
display: inline;
}

header, main {
padding-bottom: 0;
padding-top: 0;
}

main blockquote {
font-size: 1.35em;
margin-top: 0;
padding: 0;
}

main h2 {
width: 100%;
text-align: center;
}

main li {
display: block;
overflow: auto;
white-space: pre;
}

main pre code {
width: fit-content;
}

main script::after {
content: '</script>';
}

main script::before {
content: '<script>';
}

main script[src]::before {
content: '<script src="' attr(src) '">';
}

main script[type="module"]::before {
content: '<script type="module">';
}

main script {
display: block;
white-space: pre;
}

main script[src][data-js="loaded"] {
display: none;
}

output {
width: 100%;
}

output li {
border-top: 1px solid #ccc;
font-size: 0.8em;
line-height: 1;
margin: 0;
padding: 1em 0;
}

output ul {
margin: 0;
padding: 0;
}

.error {
border: 1px solid crimson;
background: #dc143c33;
padding: 1em;
border-radius: 0.35em;
}

.error::before {
color: crimson;
content: 'Error: ';
font-weight: bold;
}

nav a {
text-decoration: none;
}

nav a:hover {
text-decoration: underline;
text-decoration-color: var(--color-secondary);
}

nav li:has(ul a.selected) a[href="#"] {
text-decoration: underline;
}

nav .selected {
color: var(--color-secondary);
text-decoration: underline;
cursor: default;
}

/* Menu on mobile devices */
:root {
--menu-label:'Toggle Menu';
}
nav > button:empty,
nav > button[aria-controls] {
display: none;
}

@media (max-width: 768px) {
nav > ul {
background: var(--color-bg);
bottom: 0;
display: none;
height: fit-content;
left: 0;
margin: 0;
max-height: 100%;
overflow: auto;
position: fixed;
right: 0;
top: 0;
}

nav > button:empty,
nav > button[aria-controls] {
cursor: pointer;
display: inline-block;
}

nav > button:empty::before {
content: attr(aria-label, var(--menu-label));
}

nav > button:empty:focus,
nav > button[aria-controls]:focus {
opacity: 0;
}

nav:has(button:empty) > ul::before,
nav:has(button[aria-controls]) > ul::before {
/* FROM: a b, a strong, button, input[type="submit"] */
background-color: var(--color-link);
border: 2px solid var(--color-link);
color: var(--color-bg);

/* FROM: a b, a em, a i, a strong, button, input[type="submit"] */
border-radius: var(--border-radius);
display: block;
font-size: medium;
font-weight: bold;
line-height: var(--line-height);
padding: 1rem 2rem;

/* Element specific */
content: attr(aria-label, var(--menu-label));
cursor: pointer;
float: right;
margin: 0.75rem 1rem 0.5rem 0;
}

nav:focus-within > ul,
nav:has(button:empty:focus) > ul,
nav:has(button[aria-controls]:focus) > ul {
display: block;
}

nav ul li {
width: calc(100% - 1em);
}

nav ul li ul {
display: block;
position: static;
}
}
Loading