# 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 ```text js-maker/ main.rkt public macro module info.rkt package metadata and package test entry scrbl/ js-maker.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 ``` ## Added language support This package includes conservative support for: - `(with-handlers ([exn? handler]) body ...)`, translated to JavaScript `try`/`catch`. Only generic `exn?` 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`, and `js-date->datetime`. Import prefixes are deliberately not hardcoded; the compiler matches on the local identifier name after any `prefix:` part. ## Run tests From the directory above `jsmaker`: ```bash 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 use an explicit `non-failing-javascript-stub`. The stub prints notes to stdout, does not execute the generated JavaScript, and succeeds unless `JSMAKER_REQUIRE_ENGINE` or `JSMAKER_REQUIRE_NODE` is set. This avoids package server failures caused solely by a missing JavaScript runtime. Useful environment variables: ```bash 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: ```text Work on a Racket module/package in a versioned build directory. Important: - Always create a new subdirectory for the deliverable, for example /mnt/data/-build-NNN/. - Do not work directly in /mnt/data with loose files that have the same names; avoid version confusion by copying and patching everything inside that build directory. - Keep the package structure stable: - main.rkt for the public module; - testing/ for test infrastructure and regression tests; - demo/ for demonstration files; - info.rkt for package metadata and test entry points. - Adjust require paths to that structure before testing. - Test with Racket itself, for example: /tmp/racket/bin/raco make /main.rkt /testing/.rkt /tmp/racket/bin/racket /testing/.rkt - If JavaScript execution is needed, use a separate executor/test-framework module. Tests must not fail solely because node/deno/bun/qjs is missing; in that case they should use an explicit non-failing JavaScript stub that reports this to stdout, unless a REQUIRE environment variable is set. - Do not use shell-based internet access for dependencies. If packages are needed, fetch them through the rktsndbx bootstrap/package-index flow. - Deliver a zip file containing exactly the tested build directory. - Briefly report which commands were run, what the test results were, and which zip contains the tested result. ``` ## 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 fixes top-level `js` statement-context handling in general: `js` now emits program/statement text and does not invent an implicit top-level `return`. This keeps snippets valid when they are passed directly to WebView `runJavaScript`/`evaluateJavaScript`, where a top-level JavaScript `return` is a syntax error. Use `js/expression` when a generated JavaScript value is needed. The build 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.