6.9 KiB
js-maker
A syntax-driven Racket-to-JavaScript macro module.
This js-maker module was started as part of racket-webview, and has since evolved under supervision of the author using ChatGPT as AI agent.
Layout
js-maker/
main.rkt public macro module
info.rkt package metadata and package test entry
private/
syntax-helpers.rkt compatibility/helper material
utils.rkt compatibility/helper material
scrbl/
jsmaker.scrbl Scribble documentation
testing/
jsmaker-executors.rkt JavaScript engine discovery/execution
jsmaker-test-framework.rkt JS regression framework
jsmaker-test-runner.rkt old-name compatibility wrapper
jsmaker-regression.rkt core expression tests
jsmaker-regexp-regression.rkt regexp tests
jsmaker-program-regression.rkt larger program tests
jsmaker-regressions.rkt aggregate test entry
demo/
show-jsmaker-output.rkt
show-optimized.rkt
Notes on private helpers
Static analysis of this package layout shows that main.rkt, the test
infrastructure, the regression tests, demos and Scribble documentation do not
require private/utils.rkt or private/syntax-helpers.rkt. Those files are
retained as compatibility material from the source project and are omitted from
compilation and the package test entry point in info.rkt.
The current public module has no dependency on Gregor or the old helper
modules. Gregor-style date/time forms are translated syntactically by
main.rkt into a small JavaScript-side representation.
Added language support
This package includes conservative support for:
(with-handlers ([exn? handler]) body ...), translated to JavaScripttry/catch. Only genericexn?predicates are accepted.- Gregor-style local names such as
date,time,moment,parse-date,parse-time,parse-moment,date->string,time->string,->year,->month,->day,->hours,->minutes,->seconds,->js-date, andjs-date->datetime. Import prefixes are deliberately not hardcoded; the compiler matches on the local identifier name after anyprefix:part.
Run tests
From the directory above jsmaker:
raco make jsmaker/main.rkt jsmaker/testing/jsmaker-regressions.rkt \
jsmaker/scrbl/jsmaker.scrbl
racket jsmaker/testing/jsmaker-regressions.rkt
raco test -p jsmaker
The test framework looks for JavaScript engines such as node, deno,
bun, qjs, d8, jsc, js. Chromium is only used when explicitly selected
or when JSMAKER_BROWSER_FALLBACK=1 is set.
When no JavaScript engine is available, the tests generate the JavaScript test
files and print warnings, but do not fail unless JSMAKER_REQUIRE_ENGINE or
JSMAKER_REQUIRE_NODE is set.
Useful environment variables:
JSMAKER_ENGINE=auto|node|deno|bun|qjs|d8|jsc|js|chromium
JSMAKER_ENGINE_PATH=/path/to/executable
JSMAKER_NODE=/path/to/node
JSMAKER_REQUIRE_ENGINE=1
JSMAKER_ENGINE_TIMEOUT_SECONDS=15
JSMAKER_BROWSER_FALLBACK=1
Suggested start prompt for future work
Use this prompt when asking ChatGPT to make a new Racket module or extend this one:
Werk aan een Racket-module/package in een versievaste buildmap.
Belangrijk:
- Maak altijd een nieuwe submap voor de oplevering, bijvoorbeeld
/mnt/data/<project>-build-NNN/<collection-name>.
- Werk niet direct in /mnt/data met losse bestanden met dezelfde naam;
voorkom versieverwarring door alles in die buildmap te kopiëren/patchen.
- Houd de package-structuur stabiel:
- main.rkt voor de publieke module;
- private/ voor helpers en utils;
- testing/ voor test-infrastructuur en regressietests;
- demo/ voor demonstratiebestanden;
- info.rkt voor package metadata en test entry points.
- Pas require-paden aan op die structuur voordat je test.
- Test met Racket zelf, bijvoorbeeld:
/tmp/racket/bin/raco make <collection>/main.rkt <collection>/testing/<tests>.rkt
/tmp/racket/bin/racket <collection>/testing/<tests>.rkt
- Als JavaScript nodig is, gebruik een aparte executor/test-framework module.
Tests mogen niet falen alleen omdat node/deno/bun/qjs ontbreekt; ze moeten
dan skippen met duidelijke warnings, tenzij een REQUIRE-envvar is gezet.
- Gebruik geen shell-internet voor dependencies. Als packages nodig zijn, haal
ze via de rktsndbx bootstrap/package-index flow op.
- Lever na afloop een zip van exact de geteste buildmap op.
- Rapporteer kort welke commando's zijn uitgevoerd, wat de testresultaten waren,
en welke zip het geteste resultaat bevat.
Latest tested fix
This build includes the with-handlers callee-position fix for inline lambda
handlers, including rest-argument handlers such as (lambda args ...). It also
adds a Racket-like division-by-zero runtime check for /, so the generic
exn? handler subset can catch (/ 10 0).
JavaScript use case demos
The package includes a larger set of JavaScript use case snippets in
demo/js-usecases.rkt. They are written in the Racket surface syntax accepted
by js and compiled to JavaScript by the macro. The generated JavaScript is
also written to demo/js-usecases.generated.js.
The corresponding regression tests live in testing/jsmaker-usecases.rkt and
are included by testing/jsmaker-regressions.rkt. The test framework now awaits
Promise-valued tests, so asynchronous examples such as the Fetch API can be
checked with Node as well.
Covered use cases include random numbers, Set, JavaScript falsey values,
currying, object destructuring, setInterval/clearInterval, object property
get/set/delete, string concatenation order, Object.freeze/Object.seal,
switch/case, classes with constructor defaults, sorting objects, array deletion
techniques, Bubble Sort, recursive Binary Search, Map counting, DOM HTML
access, anagram checks, pair-sum checks, and Fetch API result/error handling.
Use-case documentation
The file scrbl/usecases.scrbl documents the JavaScript use cases from
demo/js-usecases.rkt. Each use case is shown as Racket/js-maker source next
to representative generated JavaScript, followed by the behavior covered by the
regression test.
The use-case tests in testing/jsmaker-usecases.rkt intentionally use
js/expression for the test calls wherever possible. Raw JavaScript is kept
only for small test-harness preambles such as fake timers, fake DOM objects, and
fake fetch.
Hash regression tests
This build adds testing/jsmaker-hash-regression.rkt, covering common hash operations such as hash, make-hash, hash-ref, hash-set, hash-set!, hash-remove, hash-remove!, hash-update, hash-update!, hash-clear, hash-clear!, hash-copy, hash-keys, hash-values, hash->list, hash-map, and hash-for-each. The current JavaScript backend represents hashes as plain JavaScript objects, so this is a practical string/symbol-key subset rather than a full Racket hash-table implementation for arbitrary keys.