📺 finlizes routing and improves bs

This commit is contained in:
2026-05-22 15:56:44 +02:00
parent c3782509e8
commit 8bb12aaac1
25 changed files with 277 additions and 88 deletions
+10 -4
View File
@@ -4,9 +4,6 @@ This project uses [jaandrle/bs: The simplest possible build system using executa
## Available executables
If it makes sense, arguments are passed to internally used commands (e.g. `tsc`), e. g. `--help`.
### bs/lint
This lints the project using `tsc`.
### bs/test
This lints the project and runs tests using `wtr`.
@@ -19,8 +16,17 @@ This builds the project using `rollup`.
### bs/analyze
This analyze Custom Element manifest using the `@custom-elements-manifest/analyzer`.
### bs/dev/assets
Copies assets using `cp` into proper destination in `.tmp`.
### bs/dev/tsc
Builds typescript files using `tsc`.
### bs/dev/lint
Lints the project using `tsc`.
### bs/npm/lint
Linted projects npm dependencies.
Lints projects npm dependencies.
### bs/npm/update
Updates projects npm dependencies.
+7 -3
View File
@@ -5,8 +5,12 @@ set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3
exit 1;
}
# depends on
declare -r app='src/app-cfpodcasts.ts'
declare -r cem='node_modules/.bin/cem'
declare -r app=(
'src/**/c-*.ts'
'src/**/app-*/index.ts'
'src/index.ts'
)
declare -r cem='node_modules/.bin/custom-elements-manifest'
help(){
if ! isHelp "${@}"; then return 0; fi
@@ -17,7 +21,7 @@ help(){
}
main(){
help "${@}"
$cem analyze --litelement --globs "$app" "${@}"
$cem analyze --litelement --globs "${app[@]}" "${@}"
}
main "${@}"
+4 -2
View File
@@ -5,7 +5,8 @@ set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3
exit 1;
}
# depends on
declare -r lint='bs/lint'
declare -r tsc='bs/dev/tsc'
declare -r assets='bs/dev/assets'
declare -r rollup='node_modules/.bin/rollup'
declare -r analyze='bs/analyze'
declare -r config='rollup.config.js'
@@ -21,7 +22,8 @@ help(){
main(){
help "${@}"
$lint
$tsc
$assets
rm -rf "$dist"
$rollup -c $config "${@}"
$analyze --exclude "$dist"
Executable
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3Ml53kvc
. bs/.common || {
echo 'Please run this script from the project root directory' >&2;
exit 1;
}
# depends on
declare -r paths='bs/dev/paths.js'
declare -r assets="$($paths 'src')/**/assets"
declare -r target="$($paths 'tscoutput')"
help(){
if ! isHelp "${@}"; then return 0; fi
echoReadmeInfo
echo
exit 0
}
main(){
help "${@}"
shopt -s globstar nullglob
for dir in $assets; do
cp -r "$dir" "$target/$dir"
done
}
main "${@}"
+1 -5
View File
@@ -6,21 +6,17 @@ set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3
}
# depends on
declare -r tsc='node_modules/.bin/tsc'
declare -r tscAlias='node_modules/.bin/tsc-alias'
help(){
if ! isHelp "${@}"; then return 0; fi
echoReadmeInfo
echo
$tsc --help
$tscAlias --help
exit 0
}
main(){
help "${@}"
$tsc "${@}"
echo "$tscAlias ${@}"
$tscAlias "${@}"
$tsc --noEmit "${@}"
}
main "${@}"
+44
View File
@@ -0,0 +1,44 @@
#!/bin/env node
import tsconfig from "../../tsconfig.json" with { type: "json" };
export const paths = {
/** code source */
src: "src",
/** code processed by tsc */
tscoutput: tsconfig.compilerOptions.outDir,
/** code processed by tsc and rollup */
dist: "dist",
/** tempral results of tsc, tests, … */
tmp: ".tmp",
};
export default paths;
import { argv, exit } from "node:process";
import { fileURLToPath } from "node:url";
const [ _, script ] = argv;
if (script === fileURLToPath(import.meta.url))
main(argv.slice(2));
function main(args) {
if(args.includes("--help") || args.includes("-h")){
console.log(`
Usage: ${script} [options]
Options:
[type] … if omitted, echo all
`);
exit(0);
}
if(args.length === 0){
console.log(paths);
exit(0);
}
for(const arg of args){
const path = paths[arg];
if(!path){
console.error(`Unknown path: ${arg}`);
exit(1);
}
console.log(path);
}
}
Executable
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3Ml53kvc
. bs/.common || {
echo 'Please run this script from the project root directory' >&2;
exit 1;
}
# depends on
declare -r tsc='node_modules/.bin/tsc'
declare -r tscAlias='node_modules/.bin/tsc-alias'
help(){
if ! isHelp "${@}"; then return 0; fi
echoReadmeInfo
echo
$tsc --help
$tscAlias --help
exit 0
}
main(){
help "${@}"
$tsc "${@}"
$tscAlias "${@}"
}
main "${@}"
+6 -2
View File
@@ -6,7 +6,9 @@ set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3
}
# depends on
declare -r server='node_modules/.bin/web-dev-server'
declare -r lint='bs/lint'
declare -r lint='bs/dev/lint'
declare -r tsc='bs/dev/tsc'
declare -r assets='bs/dev/assets'
declare -r server_config='web-dev-server.config.js'
help(){
@@ -25,12 +27,14 @@ EOF
main(){
help "${@}"
$lint
local -r target="${1:-./src}" # ./src or ./dist
if [[ "$target" =~ 'dist' ]]; then
# console warns because of config file, use npx serve?
$server "${@}"
else
$lint --watch --preserveWatchOutput &
$assets
$tsc --watch --preserveWatchOutput &
$server "${@}" &
wait
fi
+4 -4
View File
@@ -5,8 +5,8 @@ set -eo pipefail # this can be harmful, see https://www.youtube.com/watch?v=4Jo3
exit 1;
}
# depends on
declare -r lint='bs/lint'
declare -r wtr='node_modules/.bin/wtr'
declare -r tsc='bs/dev/tsc'
declare -r wtr='node_modules/.bin/web-test-runner'
help(){
if ! isHelp "${@}"; then return 0; fi
@@ -20,12 +20,12 @@ EOF
main(){
help "$1"
if [[ "$1" != "--watch" ]]; then
$lint
$tsc
$wtr "$@" --coverage
exit
fi
$lint --watch --preserveWatchOutput &
$tsc --watch --preserveWatchOutput &
$wtr "${*}" &
wait
}
+2 -2
View File
@@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="Description" content="Put your description here.">
<link rel="icon" type="image/svg+xml" href="./assets/logo.svg">
<link rel="icon" type="image/svg+xml" href="./.tmp/tsc/src/assets/logo.svg">
<base href="/">
<style>
@@ -23,7 +23,7 @@
<body>
<app-cfpodcasts></app-cfpodcasts>
<script type="module" src="./out-tsc/src/index.js"></script>
<script type="module" src="./.tmp/tsc/src/index.js"></script>
</body>
</html>
+6 -4
View File
@@ -4,8 +4,10 @@ import { rollupPluginHTML as html } from "@web/rollup-plugin-html";
import { importMetaAssets } from "@web/rollup-plugin-import-meta-assets";
import esbuild from "rollup-plugin-esbuild";
import { generateSW } from "rollup-plugin-workbox";
import { paths } from "./bs/dev/paths.js";
import { join } from "node:path";
const pathDist = join.bind(null, paths.dist);
export default {
input: "index.html",
output: {
@@ -13,7 +15,7 @@ export default {
chunkFileNames: "[hash].js",
assetFileNames: "[hash][extname]",
format: "es",
dir: "dist",
dir: pathDist(),
},
preserveEntrySignatures: false,
@@ -22,7 +24,7 @@ export default {
html({
minify: true,
injectServiceWorker: true,
serviceWorkerPath: "dist/sw.js",
serviceWorkerPath: pathDist("sw.js"),
}),
/** Resolve bare module imports */
nodeResolve(),
@@ -59,9 +61,9 @@ export default {
globIgnores: ["polyfills/*.js", "nomodule-*.js"],
navigateFallback: "/index.html",
// where to output the generated sw
swDest: join("dist", "sw.js"),
swDest: pathDist("sw.js"),
// directory to match patterns against to be precached
globDirectory: join("dist"),
globDirectory: pathDist(),
// cache any html js and css by default
globPatterns: ["**/*.{html,js,css,webmanifest}"],
skipWaiting: true,
+2 -2
View File
@@ -1,10 +1,10 @@
import { route } from "@/core/route.js";
import { html } from "lit";
export const path = "/episodes" as const;
export const path = "/episodes/" as const;
export const routes = route(
{
path: "/episodes/:id",
path: "/episodes/:id/",
async enter() {
await import("./index.js");
return true;
+1 -1
View File
@@ -2,7 +2,7 @@ import { route } from "@/core/route.js";
import { routes as routeEpisode } from "./:id/routes.js";
import { html } from "lit";
export const path = "/episodes" as const;
export const path = "/episodes/" as const;
export const routes = route(
{
path,
+9
View File
@@ -0,0 +1,9 @@
import { css } from "lit";
export const styles = css`
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
`;
+22
View File
@@ -0,0 +1,22 @@
import { html } from "lit";
import { fixture, expect } from "@open-wc/testing";
import type { AppHome } from "./index.js";
import "./index.js";
describe("AppHome", () => {
let element: AppHome;
beforeEach(async () => {
element = await fixture(html`<app-home></app-home>`);
});
it("renders a h1", () => {
const h1 = element.shadowRoot!.querySelector('h1')!;
expect(h1).to.exist;
expect(h1.textContent).to.equal('My app');
});
it("passes the a11y audit", async () => {
await expect(element).shadowDom.to.be.accessible();
});
});
+15
View File
@@ -0,0 +1,15 @@
import { LitElement, html } from "lit";
import { customElement } from "lit/decorators.js";
import { styles } from "./index.css.js";
@customElement("app-home")
export class AppHome extends LitElement {
static override styles = styles;
override render() {
return html`
<h1>My app</h1>
<p>Hello world</p>
<a href="/episodes">Episode</a>
`;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { route } from "@/core/route.js";
import { html } from "lit";
export const path = "/" as const;
export const routes = route(
{
path,
async enter() {
await import("./index.js");
return true;
},
render(){
return html`<app-home></app-home>`;
},
},
);

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

+3 -1
View File
@@ -5,9 +5,11 @@ export function route(...routes: (PathRouteConfig|PathRouteConfig[])[]): PathRou
if (Array.isArray(route)) // already processed
return route;
const { path, ...rest }= route;
if (!path.endsWith("/"))
throw new Error("path must end with „/”!");
return [
{ path, ...rest },
{ path: path + "/", rest },
{ path: path.slice(0, -1), ...rest },
];
});
}
+27
View File
@@ -0,0 +1,27 @@
import { html } from "lit";
import { fixture, expect } from "@open-wc/testing";
import type { AppCfpodcasts } from "./index.js";
import "./index.js";
describe("App", () => {
let element: AppCfpodcasts;
let nav: HTMLElement;
beforeEach(async () => {
element = await fixture(html`<app-cfpodcasts></app-cfpodcasts>`);
nav = element.shadowRoot!.querySelector("nav")!;
});
it("renders <main>", () => {
const content = element.shadowRoot!.querySelector('main')!;
expect(content).to.exist;
});
it("has all pages", async () => {
const links = nav.querySelectorAll("a");
await expect(links.length).to.equal(2);
});
it("passes the a11y audit", async () => {
await expect(nav).shadowDom.to.be.accessible();
});
});
+4 -3
View File
@@ -3,16 +3,17 @@ import { customElement } from "lit/decorators.js";
import { styles } from "./index.css.js";
import { Router } from "@lit-labs/router";
import * as routeHome from "./app-home/routes.js";
import * as routeEpisodes from "./app-episodes/routes.js";
import "./components/c-sronly.js";
const logo = new URL("../../assets/logo.svg", import.meta.url);
const logo = new URL("./assets/logo.svg", import.meta.url);
@customElement("app-cfpodcasts")
export class AppCfpodcasts extends LitElement {
static override styles = styles;
private _routes = new Router(this, [
{ path: "/", render: () => html`Hello world` },
...routeHome.routes,
...routeEpisodes.routes,
]);
override render() {
@@ -24,7 +25,7 @@ export class AppCfpodcasts extends LitElement {
href="${this._routes.link("/")}"
title="Home"
>
<img src=${logo} />
<img alt="" src=${logo} />
<c-sronly>Home</c-sronly>
</a>
<a
-22
View File
@@ -1,22 +0,0 @@
import { html } from 'lit';
import { fixture, expect } from '@open-wc/testing';
import type { AppCfpodcasts } from '../src/index.js';
import '../src/index.js';
describe('AppCfpodcasts', () => {
let element: AppCfpodcasts;
beforeEach(async () => {
element = await fixture(html`<app-cfpodcasts></app-cfpodcasts>`);
});
it('renders a h1', () => {
const h1 = element.shadowRoot!.querySelector('h1')!;
expect(h1).to.exist;
expect(h1.textContent).to.equal('My app');
});
it('passes the a11y audit', async () => {
await expect(element).shadowDom.to.be.accessible();
});
});
+1 -1
View File
@@ -26,7 +26,7 @@
"noErrorTruncation": false,
"outDir": "out-tsc",
"outDir": ".tmp/tsc",
"sourceMap": true,
"inlineSources": true,
"rootDir": "./",
+7 -3
View File
@@ -3,14 +3,18 @@
/** Use Hot Module replacement by adding --watch to the start command */
//const hmr = process.argv.includes("--watch");
const [ mode ] = process.argv.slice(2);
import { paths } from "./bs/dev/paths.js";
import { join } from "node:path";
const pathDist = join.bind(null, paths.dist);
const target = mode !== "dist" // OR "src"
? {
rootDir: ".",
appIndex: "./index.html",
appIndex: "index.html",
}
: {
rootDir: "./dist",
appIndex: "./dist/index.html",
rootDir: pathDist(),
appIndex: pathDist("index.html"),
};
export default /** @type {import("@web/dev-server").DevServerConfig} */ ({
+35 -29
View File
@@ -1,41 +1,47 @@
// import { playwrightLauncher } from '@web/test-runner-playwright';
const filteredLogs = ['Running in dev mode', 'Lit is in dev mode'];
const filteredLogs = ["Running in dev mode", "Lit is in dev mode"];
import { paths } from "./bs/dev/paths.js";
import { join } from "node:path";
export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
/** Test files to run */
files: 'out-tsc/test/**/*.test.js',
/** Test files to run */
files: join(paths.tscoutput, "src/**/*.test.js"),
/** Resolve bare module imports */
nodeResolve: {
exportConditions: ['browser', 'development'],
},
/** Resolve bare module imports */
nodeResolve: {
exportConditions: ["browser", "development"],
},
/** Filter out lit dev mode logs */
filterBrowserLogs(log) {
for (const arg of log.args) {
if (typeof arg === 'string' && filteredLogs.some(l => arg.includes(l))) {
return false;
}
}
return true;
},
/** Filter out lit dev mode logs */
filterBrowserLogs(log) {
for (const arg of log.args) {
if (typeof arg === "string" && filteredLogs.some(l => arg.includes(l))) {
return false;
}
}
return true;
},
/** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
// esbuildTarget: 'auto',
coverageConfig: {
reportDir: join(paths.tmp, "coverage"),
},
/** Amount of browsers to run concurrently */
// concurrentBrowsers: 2,
/** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
// esbuildTarget: 'auto',
/** Amount of test files per browser to test concurrently */
// concurrency: 1,
/** Amount of browsers to run concurrently */
// concurrentBrowsers: 2,
/** Browsers to run tests on */
// browsers: [
// playwrightLauncher({ product: 'chromium' }),
// playwrightLauncher({ product: 'firefox' }),
// playwrightLauncher({ product: 'webkit' }),
// ],
/** Amount of test files per browser to test concurrently */
// concurrency: 1,
// See documentation for all available options
/** Browsers to run tests on */
// browsers: [
// playwrightLauncher({ product: 'chromium' }),
// playwrightLauncher({ product: 'firefox' }),
// playwrightLauncher({ product: 'webkit' }),
// ],
// See documentation for all available options
});