|
|
|
@ -34,17 +34,10 @@ Source: https://www.programmfabrik.de/en/assignment-frontend-backend-developer-j |
|
|
|
|
## Features |
|
|
|
|
|
|
|
|
|
* Does not have any client-side dependencies, does not use any third-party libraries on the client. |
|
|
|
|
* Works in modern browsers (which includes at least 96% overall and at least 98% desktop userbase; |
|
|
|
|
includes Chrome since 2017, Firefox since 2018, Safari since 2017) |
|
|
|
|
as a single-page application, in offline mode. |
|
|
|
|
* Works in older browsers / browsers with JS disabled (which, together with the previous point, |
|
|
|
|
covers 99.9%+ overall userbase, including text-based browsers and presumably IE6 and probably IE5) |
|
|
|
|
as a plain old multi-page application (requiring internet connection). |
|
|
|
|
Basically it has almost perfect backwards compatibility |
|
|
|
|
(compatible with all browsers that fully support HTML4). |
|
|
|
|
* Doesn't use browser detection / sniffing, instead gracefully degrading depending on the feature set available. |
|
|
|
|
* Works in modern browsers as a single-page application, in offline mode. |
|
|
|
|
* Works in older browsers / browsers with JS disabled as a plain old multi-page application (requiring internet connection). |
|
|
|
|
* Hopefully more accessible than a typical single-page application. |
|
|
|
|
* Supports multiple games on the same page, playable at the same time, concurrently and independently. |
|
|
|
|
* Supports multiple games on the same page, playable at the same time. |
|
|
|
|
* Supports different game rules (the provided main page has two games with different rule sets). |
|
|
|
|
* Supports custom board size. |
|
|
|
|
* The computer opponent is unbeatable. |
|
|
|
@ -91,9 +84,6 @@ I wanted to minimise the size of the resulting frontend application, |
|
|
|
|
and the best way to reduce the number of dependencies on frontend is to not have any dependencies, |
|
|
|
|
to write vanilla JS. |
|
|
|
|
|
|
|
|
|
Also, I wanted to make this work everywhere (or almost everywhere), |
|
|
|
|
in the spirit of GOV.UK public services: https://shkspr.mobi/blog/2021/01/the-unreasonable-effectiveness-of-simple-html/ |
|
|
|
|
|
|
|
|
|
I've been hearing a lot about Web Components lately, |
|
|
|
|
so I decided to use this as an opportunity to learn more about them. |
|
|
|
|
But there are some major design choices to be made with Web Components too, |
|
|
|
@ -274,22 +264,28 @@ Listed in `package.json`, installed with `npm ci`, not vendored. |
|
|
|
|
|
|
|
|
|
#### Dev dependencies (build/compile-time) |
|
|
|
|
|
|
|
|
|
* `tap`, a test framework (https://node-tap.org/). |
|
|
|
|
* `typescript`, because it's so much easier to write even small projects in TS than in JS. |
|
|
|
|
* `@tsconfig/strictest`, to avoid having to enable all the strict TS compiler options manually. |
|
|
|
|
* `tap`, a test framework. |
|
|
|
|
* `typescript`, because it's so much easier to write even small projects in TS than in JS |
|
|
|
|
(fixed at 5.7-rc because |
|
|
|
|
(1) for code to run in browser as-is without a bundler, I'm using ES modules; |
|
|
|
|
(2) for ts-node (used by node-tap) to work properly, imports need to be with `.ts` extension; |
|
|
|
|
(3) for frontend to work, imports need to be with compiled `.js` extension, |
|
|
|
|
and rewriting import extensions was only implemented in TS 5.7: |
|
|
|
|
https://devblogs.microsoft.com/typescript/announcing-typescript-5-7-rc/#path-rewriting-for-relative-paths |
|
|
|
|
which is not yet stable). |
|
|
|
|
* `@tsconfig/strictest`, to avoid |
|
|
|
|
* `eslint`, for linting. |
|
|
|
|
* `prettier`, to ensure common code style. |
|
|
|
|
* `typescript-eslint`, for type-aware and type-checked linting. |
|
|
|
|
* `copyfiles`, to copy static assets to the `dist` directory so that they can be next to compiled JS for frontend. |
|
|
|
|
* `rimraf`, to cleanup `dist` directory before building. |
|
|
|
|
* `typed-query-selector`, because https://github.com/microsoft/TypeScript/issues/29037 is not addressed yet. |
|
|
|
|
|
|
|
|
|
#### Backend dependencies (runtime) |
|
|
|
|
|
|
|
|
|
* `express`, because I needed a minimal server framework to handle requests and serve static files. |
|
|
|
|
* `body-parser`, to handle POST requests in express. |
|
|
|
|
* `preact`, `preact-render-to-string`, to render intermediate JSX code to HTML |
|
|
|
|
(because I'm using JSX as a template language on backend) (https://preactjs.com/). |
|
|
|
|
(because I'm using JSX as a template language on backend). |
|
|
|
|
|
|
|
|
|
#### Frontend dependencies (runtime) |
|
|
|
|
|
|
|
|
@ -297,105 +293,21 @@ None. |
|
|
|
|
|
|
|
|
|
## Supported browser engines |
|
|
|
|
|
|
|
|
|
There are three main fully functional modes: |
|
|
|
|
|
|
|
|
|
1. Main simple offline-based JS-only mode, using modern web standards; |
|
|
|
|
2. More elaborate (almost) offline-based (almost) JS-only mode (game works offline, |
|
|
|
|
but demo counters on the top of the page (separate from the game) only work online |
|
|
|
|
because I don't have the motivation to redesign `ReactiveButton` and `ReactiveSpan`), |
|
|
|
|
using modern web standards _without_ custom built-in elements |
|
|
|
|
(which means old, but not too old Blink-based browsers like Chromium 54-66, |
|
|
|
|
and all modern WebKit-based browsers like Safari 10.1+); |
|
|
|
|
3. Online-based (plain old client-server) mode available in most browsers even with JS disabled. |
|
|
|
|
|
|
|
|
|
The main required features are: |
|
|
|
|
|
|
|
|
|
* ES modules: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type, |
|
|
|
|
without them no JS will load, and this demo will fall back to `mode 3` |
|
|
|
|
(can be worked around by using bundlers, but this will only affect Chromium-based browsers from 54 (2016) to 60 (2017), |
|
|
|
|
so it's not worth it to change the entire build setup just for better UX in these browsers). |
|
|
|
|
* Custom elements: https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define, |
|
|
|
|
without them this demo will fall back to `mode 3`. |
|
|
|
|
* Custom built-in elements: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is, |
|
|
|
|
without them this demo will fall back to `mode 2`. |
|
|
|
|
* Templates: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template, |
|
|
|
|
without them `mode 1` or `mode 2` will break, but they're in baseline HTML, |
|
|
|
|
so if they're not supported, neither custom elements nor ES modules are not supported either, |
|
|
|
|
and this demo will fall back to `mode 3` anyway. |
|
|
|
|
* `:scope` CSS selector, needed in `mode 2` only: |
|
|
|
|
https://developer.mozilla.org/en-US/docs/Web/CSS/:scope |
|
|
|
|
* `MutationObserver`, needed in `mode 2` only: |
|
|
|
|
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver |
|
|
|
|
* `:not` CSS selector for critical UI features, needed in all modes |
|
|
|
|
(hiding and showing different messages and buttons depending on the game state): |
|
|
|
|
https://developer.mozilla.org/en-US/docs/Web/CSS/:not. |
|
|
|
|
* `hidden` attribute or inline `style="display: none"` support in `mode 3` to not display templates. |
|
|
|
|
* Also CSS grid for nice presentation: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template |
|
|
|
|
(presentation is less nice on small screens, should be easy to make layout responsive, but I already spent too much time on this project). |
|
|
|
|
|
|
|
|
|
Therefore, for desktop browsers: |
|
|
|
|
|
|
|
|
|
* **Gecko** (Firefox): checked and works in v132, both with JS enabled (`mode 1`) and with JS disabled (`mode 3`). |
|
|
|
|
Should work in `mode 1` starting with Firefox 63 (2018); should fall back plain old client-server mode in all versions, |
|
|
|
|
although the design in Firefox 51 (2017) and older will probably not look very good due to the lack of the recent CSS features. |
|
|
|
|
* **Blink** (e.g. Chromium and Chromium-based browsers): superficially checked in Chromium 130, basic functionality seems to work in `mode 1`. |
|
|
|
|
Should work in `mode 1` starting with Chrome 67 (2018); should fall back to `mode 2` in Chrome plain old client-server mode in all versions; |
|
|
|
|
should fall back to `mode 3` with JS disabled, |
|
|
|
|
although the design in Chrome 56 (2017) and older will probably not look very good due to the lack of the recent CSS features. |
|
|
|
|
* **WebKit** (e.g. qutebrowser, Konqueror, Epiphany and notably Safari): superficialy checked in [BadWolf](https://hacktivis.me/projects/badwolf) 1.3 / webkit2gtk-4.1 2.46.3, |
|
|
|
|
basic functionality seems to work both with JS enabled (in `mode 2`) and with JS disabled (in `mode 3`). |
|
|
|
|
Should work with JS enabled in `mode 2` in all browsers supporting custom elements (e.g. in Safari 10.1 (2017) and newer); |
|
|
|
|
should fall back to `mode 3` in older browsers or with JS disabled, |
|
|
|
|
except that the design in Safari before 10.1 (2017) will not look very good (but will still be functional). |
|
|
|
|
* **Servo**: checked, does **not** work in nightly as of 2024-11-20 and as of 2024-11-25 (even progressive form doesn't work), |
|
|
|
|
and its developer tools host seemingly crashes on this page |
|
|
|
|
(at least on the borrowed Windows machine; Servo is not packaged for Alpine, and its regular Linux builds don't run on Alpine). |
|
|
|
|
I decided to _not_ disable dynamic features in Servo because: |
|
|
|
|
1. Servo is not production-ready, its users know that it is not suitable to be used as a daily driver; |
|
|
|
|
2. This is likely a bug in servo, it will be fixed in the future, so |
|
|
|
|
3. Gating offline JS mode for non-servo browsers only will mean that in the future, when servo is fixed, |
|
|
|
|
this demo will only work in client-server mode, just because servo once had a bug. Not ideal. |
|
|
|
|
(Also not even attempting to use dynamic features in servo will make it more difficult to debug the problem in it.) |
|
|
|
|
* **EdgeHTML** (classic MS Edge before they migrated to Chromium): not checked, should fall back to `mode 3` in all versions, |
|
|
|
|
but the page will not look very pretty in Edge before 16 (2017). |
|
|
|
|
* **Trident** (Internet Explorer): not checked, should fall back to `mode 3`, |
|
|
|
|
UI should be functional but not pretty. |
|
|
|
|
* **elinks**: checked on 0.18.0, fully functional (in `mode 3`): |
|
|
|
|
* elinks just like links did not not support disabled buttons in 0.17.1.1 or earlier, |
|
|
|
|
but this is fixed in https://github.com/rkd77/elinks/issues/341 (merged in 0.18.0). |
|
|
|
|
* elinks did not support `hidden` attribute and by default does not support `display: none` in 0.17.1.1 or earlier, |
|
|
|
|
so it will display the content of all (unused) templates. |
|
|
|
|
Support for `hidden` attribute is already implemented in https://github.com/rkd77/elinks/issues/341 (merged in 0.18.0), |
|
|
|
|
and will automatically work in 0.18.0; in older versions, the user should add |
|
|
|
|
`set document.css.ignore_display_none = 0` to their elinks config. |
|
|
|
|
* **links**: checked on 2.30, mostly functional (in `mode 3): |
|
|
|
|
* Links doesn't support disabled buttons |
|
|
|
|
so all board fields are always clickable (even if they're already filled), |
|
|
|
|
this does not cause any functional issues (clicking on the occupied field, |
|
|
|
|
or clicking on an empty field when the game is over, does not change anything), |
|
|
|
|
but still that's a poor UX. |
|
|
|
|
* **lynx**: checked on 2.9.2, somewhat functional in `mode 3` (mostly broken but can be used by those who wish): |
|
|
|
|
* lynx just like links and elinks does not support disabled buttons; |
|
|
|
|
* It does not support neither `hidden` attribute nor styles, so all (unused) templates are displayed; |
|
|
|
|
* It displays the `value` of submit buttons along with their text, which means that, for example, |
|
|
|
|
in every cell of the board along with its state (X/O/_) it also displays full serialized state of the next board. |
|
|
|
|
* **w3m**: checked on 0.5.3.20230718 (the newest version that my distro has to offer), |
|
|
|
|
broken due to https://git.sr.ht/~rkta/w3m/commit/5d9c728592de1c053e064e8db4452b0bface58e0, |
|
|
|
|
might work in newer versions (but newer versions are not pacaged in Alpine). |
|
|
|
|
|
|
|
|
|
So presumably, according to caniuse.com, |
|
|
|
|
|
|
|
|
|
* `mode 1` should work in 88% desktop browsers and 80% total browsers |
|
|
|
|
(with Safari being the notable exception contributing 9% on desktop and 18% overall); |
|
|
|
|
* Fallback to `mode 2` (which is just as functional but with a bit more convoluted JS code) |
|
|
|
|
should bring the total to at least 96% overall (98% on desktop; for some less popular mobile browsers support status is unknown); |
|
|
|
|
* The demo should at least fall back to functional `mode 3` (client-server) with the functional UI |
|
|
|
|
in all more or less mainstream browsers of the last 25 years (including presumably Internet Explorer 6 and NetFront 3, not tested), |
|
|
|
|
and in some non-mainstream ones, including text browsers such as links and elinks (checked) but notably not lynx; |
|
|
|
|
* All this is achieved almost without code duplication. |
|
|
|
|
* Gecko (Firefox): works in v132, both with JS enabled (offline mode) and with JS disabled (plain old client-server mode). |
|
|
|
|
* Blink (e.g. Chromium and Chromium-based browsers): not checked, should work in reasonably recent versions, |
|
|
|
|
both with JS enabled (offline mode) and with JS disabled (plain old client-server mode). |
|
|
|
|
* WebKit (e.g. qutebrowser and notably Safari): not checked; should **not** work in offline mode, |
|
|
|
|
because WebKit does not fully implement decade-old standard which Gecko and Blink supported since 2018: |
|
|
|
|
https://github.com/WebKit/standards-positions/issues/97. |
|
|
|
|
Everything should still work in online mode, falling back on client-server communication. |
|
|
|
|
* Servo: checked, does **not** work in nightly as of 2024-11-20, and I don't have an opportunity to figure out why |
|
|
|
|
(it's not packaged for Alpine (...yet), their regular Linux builds don't work on Alpine, |
|
|
|
|
so I had to check it on someone else's Windows machine). |
|
|
|
|
|
|
|
|
|
## Known issues and quirks |
|
|
|
|
|
|
|
|
|
* The game does not work offline fully on client (i.e. without falling back on client-server communication) |
|
|
|
|
in WebKit-based browsers (such as qutebrowser or Safari), see above. |
|
|
|
|
* Playing against the computer opponent is only supported for boards under 12 squares (i.e. 4x3 or 3x4 max). |
|
|
|
|
* The computer player moves might be counterintuitive at times, |
|
|
|
|
and it might be difficult for a human player to get to lose the game, |
|
|
|
@ -410,6 +322,10 @@ So presumably, according to caniuse.com, |
|
|
|
|
* Implement error handling and handling of incorrect / malformed game states (since they come from the client). |
|
|
|
|
* Improve UI for large boards (disable autoplayers when board is too large, |
|
|
|
|
hide "enable autoplayer" buttons, provide clear indication to the user instead). |
|
|
|
|
* Target older ES versions on the frontend, |
|
|
|
|
so that offline-only game can work in all browsers fully supporting Web Components |
|
|
|
|
(i.e. Blink-based and Firefox not older than 2018); |
|
|
|
|
right now ES2022 is targeted. |
|
|
|
|
* Figure out better API for game board, |
|
|
|
|
`board.get(row, column)` and `board.get(row, column)` seemed to be a good way to encapsulate readonly state, |
|
|
|
|
but they became very inconvenient as the codebase grew and became more complex, |
|
|
|
|