Skip to content

Commit 8632594

Browse files
committed
add svg and mathml template tags for supporting partial SVG and MathML templates within html templates
improve case sensitivity handling update contributing rules, add GitHub CI for testing pull requests
1 parent 06aa6cf commit 8632594

File tree

7 files changed

+775
-23
lines changed

7 files changed

+775
-23
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Follow instructions from the repo's top-level CONTRIBUTING.md.
1+
Always, always, read the repo's top-level CONTRIBUTING.md file before doing any
2+
work, and follow its instructions.

.github/workflows/test.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Test
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
test:
9+
runs-on: ${{ matrix.os }}
10+
11+
strategy:
12+
matrix:
13+
os: [ubuntu-latest, windows-latest, macos-latest]
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: '20'
23+
cache: 'npm'
24+
25+
- name: Install dependencies
26+
run: npm clean-install
27+
28+
- name: Run tests
29+
run: npm test

CONTRIBUTING.md

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,75 @@ Do not install any new dependencies.
1010
# Testing
1111

1212
Run `npm test` to test everything. This runs the build, checks code formatting,
13-
and finally runs unit tests.
13+
and finally runs unit tests. Never run any other test command than `npm test`.
1414

1515
When writing new tests, always follow the format in `html.test.js` as a
1616
reference, using `describe` and `it` functions to describe unit tests.
17+
18+
# Best Practices
19+
20+
- Don't constantly re-create RegExp objects, put them in a variable outside of
21+
functions so they are only created once (f.e. at the top level scope of the
22+
module).
23+
- Always aim for the simplest _readable, understandable_ solution.
24+
- If you're adding too much code, you might be solving the problem in a too complex way.
25+
- Put re-usable code in functions, always avoid duplication.
26+
- Don't use complex one-liners or clever bit fiddling unless it is absolutely
27+
necessary for something like solving a performance _problem_, prefer multiple
28+
simple readable lines.
29+
- Don't prematurely optimize, always prefer readable code first.
30+
- Document your code with JSDoc comments. All functions, methods, and classes
31+
should have JSDoc comments.
32+
- Avoid unnecessary braces. For example for conditional or loop blocks with a single statement, prefer:
33+
```js
34+
if (condition) doSomething()
35+
```
36+
instead of:
37+
```js
38+
if (condition) {
39+
doSomething()
40+
}
41+
```
42+
Similar with for loops, while loops, arrow functions.
43+
- Use new features such as optional chaining (`obj?.prop`), nullish coalescing
44+
(`value ?? defaultValue`), and logical assignment operators (`x ||= y`, `x &&= y`,
45+
`x ??= y`) when they make the code simpler and more readable.
46+
- Always prefer `const` for variables that don't change, and `let` only for
47+
variables that change. Never use `var` unless absolutely necessary for special
48+
hoisting reasons.
49+
- Always prefer for-of over items.forEach
50+
- Always prefer `element.remove()` instead of `element.parentNode.removeChild(element)`.
51+
- Always prefer `parentElement.append(childElement)` instead of
52+
`parentElement.appendChild(childElement)`.
53+
- Always prefer `parentElement.append(...childElements)` instead of
54+
`childElements.forEach(child => parentElement.appendChild(child))`.
55+
- Always prefer `parentElement.prepend(childElement)` instead of
56+
`parentElement.insertBefore(childElement, parentElement.firstChild)`.
57+
- Always prefer `element.replaceWith(newElement)` instead of
58+
`element.parentNode.replaceChild(newElement, element)`.
59+
60+
# AI only:
61+
62+
## Never do the following:
63+
64+
- Never create tests in files that you run with `node`. Our code is for
65+
browsers, so always run tests using `npm test` which ensures our tests run in a
66+
headless browser. The output is logged back to terminal.
67+
68+
## Responding to prompts
69+
70+
After every prompt, always provide at least three proposals for a solution, with
71+
pros and cons, and stop to allow the user to select the desired direction.
72+
73+
A conversation should be like this:
74+
75+
1. User: Let's do [thing to do].
76+
2. AI: Here are three ways we could do X:
77+
1. Do it this way because of A, B, C. Pros: ... Cons: ...
78+
2. Do it that way because of D, E, F. Pros: ... Cons: ...
79+
3. Do it another way because of G, H, I. Pros: ... Cons: ...
80+
3. User: Let's go with option 2.
81+
4. AI: Great! (AI goes and implements option 2)
82+
5. Repeat from step 1.
83+
84+
Basically, _always_ confirm with three proposals before implementing anything.

examples/svg-and-mathml.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>SVG Test</title>
5+
</head>
6+
<body>
7+
<script type="module" src="./svg-and-mathml.js"></script>
8+
</body>
9+
</html>

examples/svg-and-mathml.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {html, svg, mathml} from '../html.js'
2+
3+
const template = html /*html*/ `
4+
<div>
5+
<p>
6+
Showing SVG and MathML support. This example shows the <code>svg</code> and <code>mathml</code> template tags can
7+
be used to create SVG and MathML elements within HTML templates. Replacing the <code>svg</code> and
8+
<code>mathml</code> template tags with <code>html</code> will result in HTMLUnknownElement instances due to the
9+
partial templates being parsed as HTML instead of SVG or MathML.
10+
11+
<style>
12+
code {
13+
color: deeppink;
14+
font-weight: bold;
15+
}
16+
</style>
17+
</p>
18+
19+
<p>Regular HTML:</p>
20+
<div class="my-div" style="padding: 20px; border: 1px solid black;">This is a regular div</div>
21+
22+
<p>SVG Elements:</p>
23+
<svg width="200" height="200">
24+
${svg /*xml*/ `
25+
<circle cx="100" cy="100" r="50" fill="red" />
26+
<rect x="50" y="50" width="100" height="100" fill="blue" opacity="0.5" />
27+
`}
28+
</svg>
29+
30+
<p>MathML Elements:</p>
31+
<br />
32+
<math style="padding: 20px; border: 1px solid black;">
33+
${mathml /*xml*/ `
34+
<mfrac>
35+
<mi>x</mi>
36+
<mi>y</mi>
37+
</mfrac>
38+
`}
39+
</math>
40+
</div>
41+
`(Symbol())
42+
43+
document.body.append(...template)
44+
45+
// Let's inspect the elements
46+
const div = document.querySelector('.my-div')
47+
const svgEl = document.querySelector('svg')
48+
const circle = document.querySelector('circle')
49+
const rect = document.querySelector('rect')
50+
const mathEl = document.querySelector('math')
51+
const mfrac = document.querySelector('mfrac')
52+
53+
console.log('Regular div:', div, div?.constructor.name) // HTMLDivElement
54+
console.log('SVG element:', svgEl, svgEl?.constructor.name) // SVGSVGElement
55+
console.log('Circle element:', circle, circle?.constructor.name) // SVGCircleElement
56+
console.log('Rect element:', rect, rect?.constructor.name) // SVGRectElement
57+
console.log('MathML element:', mathEl, mathEl?.constructor.name) // MathMLElement
58+
console.log('mfrac element:', mfrac, mfrac?.constructor.name) // MathMLFractionElement
59+
60+
// Check if SVG elements are actually SVG elements
61+
console.log('div instanceof HTMLDivElement:', div instanceof HTMLDivElement) // true
62+
console.log('svg instanceof SVGSVGElement:', svgEl instanceof SVGSVGElement) // true
63+
console.log('circle instanceof SVGCircleElement:', circle instanceof SVGCircleElement) // true
64+
console.log('rect instanceof SVGRectElement:', rect instanceof SVGRectElement) // true
65+
console.log('math instanceof MathMLElement:', mathEl instanceof MathMLElement) // true
66+
console.log('mfrac instanceof MathMLFractionElement:', mfrac instanceof MathMLElement) // true
67+
68+
// Check if they're unknown elements
69+
console.log('circle instanceof HTMLUnknownElement:', circle instanceof HTMLUnknownElement) // false
70+
console.log('rect instanceof HTMLUnknownElement:', rect instanceof HTMLUnknownElement) // false
71+
console.log('mfrac instanceof HTMLUnknownElement:', mfrac instanceof HTMLUnknownElement) // false

0 commit comments

Comments
 (0)